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} summaryFooterShow (true|false) generate tfoot for summary, default false
9161 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162 * @cfg {Boolean} rowSelection (true|false) default false
9163 * @cfg {Boolean} cellSelection (true|false) default false
9164 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9166 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9167 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9168 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9172 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9175 * Create a new Table
9176 * @param {Object} config The config object
9179 Roo.bootstrap.Table = function(config)
9181 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9184 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9189 this.view = this; // compat with grid.
9191 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9193 this.sm.grid = this;
9194 this.selModel = Roo.factory(this.sm, Roo.grid);
9195 this.sm = this.selModel;
9196 this.sm.xmodule = this.xmodule || false;
9199 if (this.cm && typeof(this.cm.config) == 'undefined') {
9200 this.colModel = new Roo.grid.ColumnModel(this.cm);
9201 this.cm = this.colModel;
9202 this.cm.xmodule = this.xmodule || false;
9205 this.store= Roo.factory(this.store, Roo.data);
9206 this.ds = this.store;
9207 this.ds.xmodule = this.xmodule || false;
9210 if (this.footer && this.store) {
9211 this.footer.dataSource = this.ds;
9212 this.footer = Roo.factory(this.footer);
9219 * Fires when a cell is clicked
9220 * @param {Roo.bootstrap.Table} this
9221 * @param {Roo.Element} el
9222 * @param {Number} rowIndex
9223 * @param {Number} columnIndex
9224 * @param {Roo.EventObject} e
9228 * @event celldblclick
9229 * Fires when a cell is double clicked
9230 * @param {Roo.bootstrap.Table} this
9231 * @param {Roo.Element} el
9232 * @param {Number} rowIndex
9233 * @param {Number} columnIndex
9234 * @param {Roo.EventObject} e
9236 "celldblclick" : true,
9239 * Fires when a row is clicked
9240 * @param {Roo.bootstrap.Table} this
9241 * @param {Roo.Element} el
9242 * @param {Number} rowIndex
9243 * @param {Roo.EventObject} e
9247 * @event rowdblclick
9248 * Fires when a row is double clicked
9249 * @param {Roo.bootstrap.Table} this
9250 * @param {Roo.Element} el
9251 * @param {Number} rowIndex
9252 * @param {Roo.EventObject} e
9254 "rowdblclick" : true,
9257 * Fires when a mouseover occur
9258 * @param {Roo.bootstrap.Table} this
9259 * @param {Roo.Element} el
9260 * @param {Number} rowIndex
9261 * @param {Number} columnIndex
9262 * @param {Roo.EventObject} e
9267 * Fires when a mouseout occur
9268 * @param {Roo.bootstrap.Table} this
9269 * @param {Roo.Element} el
9270 * @param {Number} rowIndex
9271 * @param {Number} columnIndex
9272 * @param {Roo.EventObject} e
9277 * Fires when a row is rendered, so you can change add a style to it.
9278 * @param {Roo.bootstrap.Table} this
9279 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9283 * @event rowsrendered
9284 * Fires when all the rows have been rendered
9285 * @param {Roo.bootstrap.Table} this
9287 'rowsrendered' : true,
9289 * @event contextmenu
9290 * The raw contextmenu event for the entire grid.
9291 * @param {Roo.EventObject} e
9293 "contextmenu" : true,
9295 * @event rowcontextmenu
9296 * Fires when a row is right clicked
9297 * @param {Roo.bootstrap.Table} this
9298 * @param {Number} rowIndex
9299 * @param {Roo.EventObject} e
9301 "rowcontextmenu" : true,
9303 * @event cellcontextmenu
9304 * Fires when a cell is right clicked
9305 * @param {Roo.bootstrap.Table} this
9306 * @param {Number} rowIndex
9307 * @param {Number} cellIndex
9308 * @param {Roo.EventObject} e
9310 "cellcontextmenu" : true,
9312 * @event headercontextmenu
9313 * Fires when a header is right clicked
9314 * @param {Roo.bootstrap.Table} this
9315 * @param {Number} columnIndex
9316 * @param {Roo.EventObject} e
9318 "headercontextmenu" : true,
9321 * The raw mousedown event for the entire grid.
9322 * @param {Roo.EventObject} e
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9345 summaryFooterShow : false,
9347 enableColumnResize: true,
9348 disableAutoSize: false,
9350 rowSelection : false,
9351 cellSelection : false,
9354 minColumnWidth : 50,
9356 // Roo.Element - the tbody
9357 bodyEl: false, // <tbody> Roo.Element - thead element
9358 headEl: false, // <thead> Roo.Element - thead element
9359 resizeProxy : false, // proxy element for dragging?
9363 container: false, // used by gridpanel...
9369 auto_hide_footer : false,
9371 view: false, // actually points to this..
9373 getAutoCreate : function()
9375 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9382 // this get's auto added by panel.Grid
9383 if (this.scrollBody) {
9384 cfg.cls += ' table-body-fixed';
9387 cfg.cls += ' table-striped';
9391 cfg.cls += ' table-hover';
9393 if (this.bordered) {
9394 cfg.cls += ' table-bordered';
9396 if (this.condensed) {
9397 cfg.cls += ' table-condensed';
9400 if (this.responsive) {
9401 cfg.cls += ' table-responsive';
9405 cfg.cls+= ' ' +this.cls;
9411 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9414 if(this.store || this.cm){
9415 if(this.headerShow){
9416 cfg.cn.push(this.renderHeader());
9419 cfg.cn.push(this.renderBody());
9421 if(this.footerShow){
9422 cfg.cn.push(this.renderFooter());
9425 // where does this come from?
9426 //cfg.cls+= ' TableGrid';
9429 return { cn : [ cfg ] };
9432 initEvents : function()
9434 if(!this.store || !this.cm){
9437 if (this.selModel) {
9438 this.selModel.initEvents();
9442 //Roo.log('initEvents with ds!!!!');
9444 this.bodyEl = this.el.select('tbody', true).first();
9445 this.headEl = this.el.select('thead', true).first();
9446 this.mainFoot = this.el.select('tfoot', true).first();
9451 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9452 e.on('click', this.sort, this);
9456 // why is this done????? = it breaks dialogs??
9457 //this.parent().el.setStyle('position', 'relative');
9461 this.footer.parentId = this.id;
9462 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9465 this.el.select('tfoot tr td').first().addClass('hide');
9470 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9473 this.store.on('load', this.onLoad, this);
9474 this.store.on('beforeload', this.onBeforeLoad, this);
9475 this.store.on('update', this.onUpdate, this);
9476 this.store.on('add', this.onAdd, this);
9477 this.store.on("clear", this.clear, this);
9479 this.el.on("contextmenu", this.onContextMenu, this);
9482 this.cm.on("headerchange", this.onHeaderChange, this);
9483 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9485 //?? does bodyEl get replaced on render?
9486 this.bodyEl.on("click", this.onClick, this);
9487 this.bodyEl.on("dblclick", this.onDblClick, this);
9488 this.bodyEl.on('scroll', this.onBodyScroll, this);
9490 // guessing mainbody will work - this relays usually caught by selmodel at present.
9491 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9494 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9497 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9498 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9503 // Compatibility with grid - we implement all the view features at present.
9504 getView : function()
9509 initCSS : function()
9511 if(this.disableAutoSize) {
9515 var cm = this.cm, styles = [];
9516 this.CSS.removeStyleSheet(this.id + '-cssrules');
9517 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9518 // we can honour xs/sm/md/xl as widths...
9519 // we first have to decide what widht we are currently at...
9520 var sz = Roo.getGridSize();
9524 var cols = []; // visable cols.
9526 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9527 var w = cm.getColumnWidth(i, false);
9529 cols.push( { rel : false, abs : 0 });
9533 cols.push( { rel : false, abs : w });
9535 last = i; // not really..
9538 var w = cm.getColumnWidth(i, sz);
9543 cols.push( { rel : w, abs : false });
9546 var avail = this.bodyEl.dom.clientWidth - total_abs;
9548 var unitWidth = Math.floor(avail / total);
9549 var rem = avail - (unitWidth * total);
9551 var hidden, width, pos = 0 , splithide , left;
9552 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9554 hidden = 'display:none;';
9556 width = 'width:0px;';
9558 if(!cm.isHidden(i)){
9562 // we can honour xs/sm/md/xl ?
9563 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9565 hidden = 'display:none;';
9567 // width should return a small number...
9569 w+=rem; // add the remaining with..
9572 left = "left:" + (pos -4) + "px;";
9573 width = "width:" + w+ "px;";
9576 if (this.responsive) {
9579 hidden = cm.isHidden(i) ? 'display:none;' : '';
9580 splithide = 'display: none;';
9583 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9586 splithide = 'display:none;';
9589 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9590 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9591 // this is the popover version..
9592 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9597 //Roo.log(styles.join(''));
9598 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9604 onContextMenu : function(e, t)
9606 this.processEvent("contextmenu", e);
9609 processEvent : function(name, e)
9611 if (name != 'touchstart' ) {
9612 this.fireEvent(name, e);
9615 var t = e.getTarget();
9617 var cell = Roo.get(t);
9623 if(cell.findParent('tfoot', false, true)){
9627 if(cell.findParent('thead', false, true)){
9629 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9630 cell = Roo.get(t).findParent('th', false, true);
9632 Roo.log("failed to find th in thead?");
9633 Roo.log(e.getTarget());
9638 var cellIndex = cell.dom.cellIndex;
9640 var ename = name == 'touchstart' ? 'click' : name;
9641 this.fireEvent("header" + ename, this, cellIndex, e);
9646 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9647 cell = Roo.get(t).findParent('td', false, true);
9649 Roo.log("failed to find th in tbody?");
9650 Roo.log(e.getTarget());
9655 var row = cell.findParent('tr', false, true);
9656 var cellIndex = cell.dom.cellIndex;
9657 var rowIndex = row.dom.rowIndex - 1;
9661 this.fireEvent("row" + name, this, rowIndex, e);
9665 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9671 onMouseover : function(e, el)
9673 var cell = Roo.get(el);
9679 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9680 cell = cell.findParent('td', false, true);
9683 var row = cell.findParent('tr', false, true);
9684 var cellIndex = cell.dom.cellIndex;
9685 var rowIndex = row.dom.rowIndex - 1; // start from 0
9687 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9691 onMouseout : function(e, el)
9693 var cell = Roo.get(el);
9699 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9700 cell = cell.findParent('td', false, true);
9703 var row = cell.findParent('tr', false, true);
9704 var cellIndex = cell.dom.cellIndex;
9705 var rowIndex = row.dom.rowIndex - 1; // start from 0
9707 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9711 onClick : function(e, el)
9713 var cell = Roo.get(el);
9715 if(!cell || (!this.cellSelection && !this.rowSelection)){
9719 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9720 cell = cell.findParent('td', false, true);
9723 if(!cell || typeof(cell) == 'undefined'){
9727 var row = cell.findParent('tr', false, true);
9729 if(!row || typeof(row) == 'undefined'){
9733 var cellIndex = cell.dom.cellIndex;
9734 var rowIndex = this.getRowIndex(row);
9736 // why??? - should these not be based on SelectionModel?
9737 //if(this.cellSelection){
9738 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9741 //if(this.rowSelection){
9742 this.fireEvent('rowclick', this, row, rowIndex, e);
9747 onDblClick : function(e,el)
9749 var cell = Roo.get(el);
9751 if(!cell || (!this.cellSelection && !this.rowSelection)){
9755 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9756 cell = cell.findParent('td', false, true);
9759 if(!cell || typeof(cell) == 'undefined'){
9763 var row = cell.findParent('tr', false, true);
9765 if(!row || typeof(row) == 'undefined'){
9769 var cellIndex = cell.dom.cellIndex;
9770 var rowIndex = this.getRowIndex(row);
9772 if(this.cellSelection){
9773 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9776 if(this.rowSelection){
9777 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9780 findRowIndex : function(el)
9782 var cell = Roo.get(el);
9786 var row = cell.findParent('tr', false, true);
9788 if(!row || typeof(row) == 'undefined'){
9791 return this.getRowIndex(row);
9793 sort : function(e,el)
9795 var col = Roo.get(el);
9797 if(!col.hasClass('sortable')){
9801 var sort = col.attr('sort');
9804 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9808 this.store.sortInfo = {field : sort, direction : dir};
9811 Roo.log("calling footer first");
9812 this.footer.onClick('first');
9815 this.store.load({ params : { start : 0 } });
9819 renderHeader : function()
9827 this.totalWidth = 0;
9829 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9831 var config = cm.config[i];
9835 cls : 'x-hcol-' + i,
9838 html: cm.getColumnHeader(i)
9841 var tooltip = cm.getColumnTooltip(i);
9843 c.tooltip = tooltip;
9849 if(typeof(config.sortable) != 'undefined' && config.sortable){
9850 c.cls += ' sortable';
9851 c.html = '<i class="fa"></i>' + c.html;
9854 // could use BS4 hidden-..-down
9856 if(typeof(config.lgHeader) != 'undefined'){
9857 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9860 if(typeof(config.mdHeader) != 'undefined'){
9861 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9864 if(typeof(config.smHeader) != 'undefined'){
9865 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9868 if(typeof(config.xsHeader) != 'undefined'){
9869 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9876 if(typeof(config.tooltip) != 'undefined'){
9877 c.tooltip = config.tooltip;
9880 if(typeof(config.colspan) != 'undefined'){
9881 c.colspan = config.colspan;
9884 // hidden is handled by CSS now
9886 if(typeof(config.dataIndex) != 'undefined'){
9887 c.sort = config.dataIndex;
9892 if(typeof(config.align) != 'undefined' && config.align.length){
9893 c.style += ' text-align:' + config.align + ';';
9896 /* width is done in CSS
9897 *if(typeof(config.width) != 'undefined'){
9898 c.style += ' width:' + config.width + 'px;';
9899 this.totalWidth += config.width;
9901 this.totalWidth += 100; // assume minimum of 100 per column?
9905 if(typeof(config.cls) != 'undefined'){
9906 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9908 // this is the bit that doesnt reall work at all...
9910 if (this.responsive) {
9913 ['xs','sm','md','lg'].map(function(size){
9915 if(typeof(config[size]) == 'undefined'){
9919 if (!config[size]) { // 0 = hidden
9920 // BS 4 '0' is treated as hide that column and below.
9921 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9925 c.cls += ' col-' + size + '-' + config[size] + (
9926 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9934 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9945 renderBody : function()
9955 colspan : this.cm.getColumnCount()
9965 renderFooter : function()
9975 colspan : this.cm.getColumnCount()
9985 renderSummaryFooter : function()
9992 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9996 cls : 'x-fcol-' + i,
10009 onLoad : function()
10011 // Roo.log('ds onload');
10016 var ds = this.store;
10018 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10019 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10020 if (_this.store.sortInfo) {
10022 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10023 e.select('i', true).addClass(['fa-arrow-up']);
10026 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10027 e.select('i', true).addClass(['fa-arrow-down']);
10032 var tbody = this.bodyEl;
10034 if(ds.getCount() > 0){
10035 ds.data.each(function(d,rowIndex){
10036 var row = this.renderRow(cm, ds, rowIndex);
10038 tbody.createChild(row);
10042 if(row.cellObjects.length){
10043 Roo.each(row.cellObjects, function(r){
10044 _this.renderCellObject(r);
10049 } else if (this.empty_results.length) {
10050 this.el.mask(this.empty_results, 'no-spinner');
10053 var tfoot = this.el.select('tfoot', true).first();
10055 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10057 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10059 var total = this.ds.getTotalCount();
10061 if(this.footer.pageSize < total){
10062 this.mainFoot.show();
10066 if(!this.footerShow && this.summaryFooterShow) {
10068 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10070 var value = cm.config[i].summaryFooter;
10072 Roo.log('value [' + i + '] : ' + value);
10076 Roo.each(this.el.select('tbody td', true).elements, function(e){
10077 e.on('mouseover', _this.onMouseover, _this);
10080 Roo.each(this.el.select('tbody td', true).elements, function(e){
10081 e.on('mouseout', _this.onMouseout, _this);
10083 this.fireEvent('rowsrendered', this);
10087 this.initCSS(); /// resize cols
10093 onUpdate : function(ds,record)
10095 this.refreshRow(record);
10099 onRemove : function(ds, record, index, isUpdate){
10100 if(isUpdate !== true){
10101 this.fireEvent("beforerowremoved", this, index, record);
10103 var bt = this.bodyEl.dom;
10105 var rows = this.el.select('tbody > tr', true).elements;
10107 if(typeof(rows[index]) != 'undefined'){
10108 bt.removeChild(rows[index].dom);
10111 // if(bt.rows[index]){
10112 // bt.removeChild(bt.rows[index]);
10115 if(isUpdate !== true){
10116 //this.stripeRows(index);
10117 //this.syncRowHeights(index, index);
10119 this.fireEvent("rowremoved", this, index, record);
10123 onAdd : function(ds, records, rowIndex)
10125 //Roo.log('on Add called');
10126 // - note this does not handle multiple adding very well..
10127 var bt = this.bodyEl.dom;
10128 for (var i =0 ; i < records.length;i++) {
10129 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10130 //Roo.log(records[i]);
10131 //Roo.log(this.store.getAt(rowIndex+i));
10132 this.insertRow(this.store, rowIndex + i, false);
10139 refreshRow : function(record){
10140 var ds = this.store, index;
10141 if(typeof record == 'number'){
10143 record = ds.getAt(index);
10145 index = ds.indexOf(record);
10147 return; // should not happen - but seems to
10150 this.insertRow(ds, index, true);
10152 this.onRemove(ds, record, index+1, true);
10154 //this.syncRowHeights(index, index);
10156 this.fireEvent("rowupdated", this, index, record);
10158 // private - called by RowSelection
10159 onRowSelect : function(rowIndex){
10160 var row = this.getRowDom(rowIndex);
10161 row.addClass(['bg-info','info']);
10163 // private - called by RowSelection
10164 onRowDeselect : function(rowIndex)
10166 if (rowIndex < 0) {
10169 var row = this.getRowDom(rowIndex);
10170 row.removeClass(['bg-info','info']);
10173 * Focuses the specified row.
10174 * @param {Number} row The row index
10176 focusRow : function(row)
10178 //Roo.log('GridView.focusRow');
10179 var x = this.bodyEl.dom.scrollLeft;
10180 this.focusCell(row, 0, false);
10181 this.bodyEl.dom.scrollLeft = x;
10185 * Focuses the specified cell.
10186 * @param {Number} row The row index
10187 * @param {Number} col The column index
10188 * @param {Boolean} hscroll false to disable horizontal scrolling
10190 focusCell : function(row, col, hscroll)
10192 //Roo.log('GridView.focusCell');
10193 var el = this.ensureVisible(row, col, hscroll);
10194 // not sure what focusEL achives = it's a <a> pos relative
10195 //this.focusEl.alignTo(el, "tl-tl");
10197 // this.focusEl.focus();
10199 // this.focusEl.focus.defer(1, this.focusEl);
10204 * Scrolls the specified cell into view
10205 * @param {Number} row The row index
10206 * @param {Number} col The column index
10207 * @param {Boolean} hscroll false to disable horizontal scrolling
10209 ensureVisible : function(row, col, hscroll)
10211 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10212 //return null; //disable for testing.
10213 if(typeof row != "number"){
10214 row = row.rowIndex;
10216 if(row < 0 && row >= this.ds.getCount()){
10219 col = (col !== undefined ? col : 0);
10221 while(cm.isHidden(col)){
10225 var el = this.getCellDom(row, col);
10229 var c = this.bodyEl.dom;
10231 var ctop = parseInt(el.offsetTop, 10);
10232 var cleft = parseInt(el.offsetLeft, 10);
10233 var cbot = ctop + el.offsetHeight;
10234 var cright = cleft + el.offsetWidth;
10236 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10237 var ch = 0; //?? header is not withing the area?
10238 var stop = parseInt(c.scrollTop, 10);
10239 var sleft = parseInt(c.scrollLeft, 10);
10240 var sbot = stop + ch;
10241 var sright = sleft + c.clientWidth;
10243 Roo.log('GridView.ensureVisible:' +
10245 ' c.clientHeight:' + c.clientHeight +
10246 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10254 c.scrollTop = ctop;
10255 //Roo.log("set scrolltop to ctop DISABLE?");
10256 }else if(cbot > sbot){
10257 //Roo.log("set scrolltop to cbot-ch");
10258 c.scrollTop = cbot-ch;
10261 if(hscroll !== false){
10263 c.scrollLeft = cleft;
10264 }else if(cright > sright){
10265 c.scrollLeft = cright-c.clientWidth;
10273 insertRow : function(dm, rowIndex, isUpdate){
10276 this.fireEvent("beforerowsinserted", this, rowIndex);
10278 //var s = this.getScrollState();
10279 var row = this.renderRow(this.cm, this.store, rowIndex);
10280 // insert before rowIndex..
10281 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10285 if(row.cellObjects.length){
10286 Roo.each(row.cellObjects, function(r){
10287 _this.renderCellObject(r);
10292 this.fireEvent("rowsinserted", this, rowIndex);
10293 //this.syncRowHeights(firstRow, lastRow);
10294 //this.stripeRows(firstRow);
10301 getRowDom : function(rowIndex)
10303 var rows = this.el.select('tbody > tr', true).elements;
10305 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10308 getCellDom : function(rowIndex, colIndex)
10310 var row = this.getRowDom(rowIndex);
10311 if (row === false) {
10314 var cols = row.select('td', true).elements;
10315 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10319 // returns the object tree for a tr..
10322 renderRow : function(cm, ds, rowIndex)
10324 var d = ds.getAt(rowIndex);
10328 cls : 'x-row-' + rowIndex,
10332 var cellObjects = [];
10334 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10335 var config = cm.config[i];
10337 var renderer = cm.getRenderer(i);
10341 if(typeof(renderer) !== 'undefined'){
10342 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10344 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10345 // and are rendered into the cells after the row is rendered - using the id for the element.
10347 if(typeof(value) === 'object'){
10357 rowIndex : rowIndex,
10362 this.fireEvent('rowclass', this, rowcfg);
10366 // this might end up displaying HTML?
10367 // this is too messy... - better to only do it on columsn you know are going to be too long
10368 //tooltip : (typeof(value) === 'object') ? '' : value,
10369 cls : rowcfg.rowClass + ' x-col-' + i,
10371 html: (typeof(value) === 'object') ? '' : value
10378 if(typeof(config.colspan) != 'undefined'){
10379 td.colspan = config.colspan;
10384 if(typeof(config.align) != 'undefined' && config.align.length){
10385 td.style += ' text-align:' + config.align + ';';
10387 if(typeof(config.valign) != 'undefined' && config.valign.length){
10388 td.style += ' vertical-align:' + config.valign + ';';
10391 if(typeof(config.width) != 'undefined'){
10392 td.style += ' width:' + config.width + 'px;';
10396 if(typeof(config.cursor) != 'undefined'){
10397 td.style += ' cursor:' + config.cursor + ';';
10400 if(typeof(config.cls) != 'undefined'){
10401 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10403 if (this.responsive) {
10404 ['xs','sm','md','lg'].map(function(size){
10406 if(typeof(config[size]) == 'undefined'){
10412 if (!config[size]) { // 0 = hidden
10413 // BS 4 '0' is treated as hide that column and below.
10414 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10418 td.cls += ' col-' + size + '-' + config[size] + (
10419 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10429 row.cellObjects = cellObjects;
10437 onBeforeLoad : function()
10439 this.el.unmask(); // if needed.
10446 this.el.select('tbody', true).first().dom.innerHTML = '';
10449 * Show or hide a row.
10450 * @param {Number} rowIndex to show or hide
10451 * @param {Boolean} state hide
10453 setRowVisibility : function(rowIndex, state)
10455 var bt = this.bodyEl.dom;
10457 var rows = this.el.select('tbody > tr', true).elements;
10459 if(typeof(rows[rowIndex]) == 'undefined'){
10462 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10467 getSelectionModel : function(){
10468 if(!this.selModel){
10469 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10471 return this.selModel;
10474 * Render the Roo.bootstrap object from renderder
10476 renderCellObject : function(r)
10480 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10482 var t = r.cfg.render(r.container);
10485 Roo.each(r.cfg.cn, function(c){
10487 container: t.getChildContainer(),
10490 _this.renderCellObject(child);
10495 * get the Row Index from a dom element.
10496 * @param {Roo.Element} row The row to look for
10497 * @returns {Number} the row
10499 getRowIndex : function(row)
10503 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10514 * get the header TH element for columnIndex
10515 * @param {Number} columnIndex
10516 * @returns {Roo.Element}
10518 getHeaderIndex: function(colIndex)
10520 var cols = this.headEl.select('th', true).elements;
10521 return cols[colIndex];
10524 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10525 * @param {domElement} cell to look for
10526 * @returns {Number} the column
10528 getCellIndex : function(cell)
10530 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10532 return parseInt(id[1], 10);
10537 * Returns the grid's underlying element = used by panel.Grid
10538 * @return {Element} The element
10540 getGridEl : function(){
10544 * Forces a resize - used by panel.Grid
10545 * @return {Element} The element
10547 autoSize : function()
10549 if(this.disableAutoSize) {
10552 //var ctr = Roo.get(this.container.dom.parentElement);
10553 var ctr = Roo.get(this.el.dom);
10555 var thd = this.getGridEl().select('thead',true).first();
10556 var tbd = this.getGridEl().select('tbody', true).first();
10557 var tfd = this.getGridEl().select('tfoot', true).first();
10559 var cw = ctr.getWidth();
10560 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10564 tbd.setWidth(ctr.getWidth());
10565 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10566 // this needs fixing for various usage - currently only hydra job advers I think..
10568 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10570 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10573 cw = Math.max(cw, this.totalWidth);
10574 this.getGridEl().select('tbody tr',true).setWidth(cw);
10577 // resize 'expandable coloumn?
10579 return; // we doe not have a view in this design..
10582 onBodyScroll: function()
10584 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10586 this.headEl.setStyle({
10587 'position' : 'relative',
10588 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10594 var scrollHeight = this.bodyEl.dom.scrollHeight;
10596 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10598 var height = this.bodyEl.getHeight();
10600 if(scrollHeight - height == scrollTop) {
10602 var total = this.ds.getTotalCount();
10604 if(this.footer.cursor + this.footer.pageSize < total){
10606 this.footer.ds.load({
10608 start : this.footer.cursor + this.footer.pageSize,
10609 limit : this.footer.pageSize
10618 onColumnSplitterMoved : function(i, diff)
10620 this.userResized = true;
10622 var cm = this.colModel;
10624 var w = this.getHeaderIndex(i).getWidth() + diff;
10627 cm.setColumnWidth(i, w, true);
10629 //var cid = cm.getColumnId(i); << not used in this version?
10630 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10632 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10633 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10634 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10636 //this.updateSplitters();
10637 //this.layout(); << ??
10638 this.fireEvent("columnresize", i, w);
10640 onHeaderChange : function()
10642 var header = this.renderHeader();
10643 var table = this.el.select('table', true).first();
10645 this.headEl.remove();
10646 this.headEl = table.createChild(header, this.bodyEl, false);
10648 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10649 e.on('click', this.sort, this);
10652 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10653 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10658 onHiddenChange : function(colModel, colIndex, hidden)
10661 this.cm.setHidden()
10662 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10663 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10665 this.CSS.updateRule(thSelector, "display", "");
10666 this.CSS.updateRule(tdSelector, "display", "");
10669 this.CSS.updateRule(thSelector, "display", "none");
10670 this.CSS.updateRule(tdSelector, "display", "none");
10673 // onload calls initCSS()
10674 this.onHeaderChange();
10678 setColumnWidth: function(col_index, width)
10680 // width = "md-2 xs-2..."
10681 if(!this.colModel.config[col_index]) {
10685 var w = width.split(" ");
10687 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10689 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10692 for(var j = 0; j < w.length; j++) {
10698 var size_cls = w[j].split("-");
10700 if(!Number.isInteger(size_cls[1] * 1)) {
10704 if(!this.colModel.config[col_index][size_cls[0]]) {
10708 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10712 h_row[0].classList.replace(
10713 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10714 "col-"+size_cls[0]+"-"+size_cls[1]
10717 for(var i = 0; i < rows.length; i++) {
10719 var size_cls = w[j].split("-");
10721 if(!Number.isInteger(size_cls[1] * 1)) {
10725 if(!this.colModel.config[col_index][size_cls[0]]) {
10729 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10733 rows[i].classList.replace(
10734 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10735 "col-"+size_cls[0]+"-"+size_cls[1]
10739 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10744 // currently only used to find the split on drag..
10745 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10750 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10751 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10760 * @class Roo.bootstrap.TableCell
10761 * @extends Roo.bootstrap.Component
10762 * @children Roo.bootstrap.Component
10763 * @parent Roo.bootstrap.TableRow
10764 * Bootstrap TableCell class
10766 * @cfg {String} html cell contain text
10767 * @cfg {String} cls cell class
10768 * @cfg {String} tag cell tag (td|th) default td
10769 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10770 * @cfg {String} align Aligns the content in a cell
10771 * @cfg {String} axis Categorizes cells
10772 * @cfg {String} bgcolor Specifies the background color of a cell
10773 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10774 * @cfg {Number} colspan Specifies the number of columns a cell should span
10775 * @cfg {String} headers Specifies one or more header cells a cell is related to
10776 * @cfg {Number} height Sets the height of a cell
10777 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10778 * @cfg {Number} rowspan Sets the number of rows a cell should span
10779 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10780 * @cfg {String} valign Vertical aligns the content in a cell
10781 * @cfg {Number} width Specifies the width of a cell
10784 * Create a new TableCell
10785 * @param {Object} config The config object
10788 Roo.bootstrap.TableCell = function(config){
10789 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10792 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10812 getAutoCreate : function(){
10813 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10820 cfg.tag = this.tag;
10833 cfg.align=this.align
10838 if (this.bgcolor) {
10839 cfg.bgcolor=this.bgcolor
10841 if (this.charoff) {
10842 cfg.charoff=this.charoff
10844 if (this.colspan) {
10845 cfg.colspan=this.colspan
10847 if (this.headers) {
10848 cfg.headers=this.headers
10851 cfg.height=this.height
10854 cfg.nowrap=this.nowrap
10856 if (this.rowspan) {
10857 cfg.rowspan=this.rowspan
10860 cfg.scope=this.scope
10863 cfg.valign=this.valign
10866 cfg.width=this.width
10885 * @class Roo.bootstrap.TableRow
10886 * @extends Roo.bootstrap.Component
10887 * @children Roo.bootstrap.TableCell
10888 * @parent Roo.bootstrap.TableBody
10889 * Bootstrap TableRow class
10890 * @cfg {String} cls row class
10891 * @cfg {String} align Aligns the content in a table row
10892 * @cfg {String} bgcolor Specifies a background color for a table row
10893 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10894 * @cfg {String} valign Vertical aligns the content in a table row
10897 * Create a new TableRow
10898 * @param {Object} config The config object
10901 Roo.bootstrap.TableRow = function(config){
10902 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10905 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10913 getAutoCreate : function(){
10914 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10921 cfg.cls = this.cls;
10924 cfg.align = this.align;
10927 cfg.bgcolor = this.bgcolor;
10930 cfg.charoff = this.charoff;
10933 cfg.valign = this.valign;
10951 * @class Roo.bootstrap.TableBody
10952 * @extends Roo.bootstrap.Component
10953 * @children Roo.bootstrap.TableRow
10954 * @parent Roo.bootstrap.Table
10955 * Bootstrap TableBody class
10956 * @cfg {String} cls element class
10957 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10958 * @cfg {String} align Aligns the content inside the element
10959 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10960 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10963 * Create a new TableBody
10964 * @param {Object} config The config object
10967 Roo.bootstrap.TableBody = function(config){
10968 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10971 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10979 getAutoCreate : function(){
10980 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10990 cfg.tag = this.tag;
10994 cfg.align = this.align;
10997 cfg.charoff = this.charoff;
11000 cfg.valign = this.valign;
11007 // initEvents : function()
11010 // if(!this.store){
11014 // this.store = Roo.factory(this.store, Roo.data);
11015 // this.store.on('load', this.onLoad, this);
11017 // this.store.load();
11021 // onLoad: function ()
11023 // this.fireEvent('load', this);
11033 * Ext JS Library 1.1.1
11034 * Copyright(c) 2006-2007, Ext JS, LLC.
11036 * Originally Released Under LGPL - original licence link has changed is not relivant.
11039 * <script type="text/javascript">
11042 // as we use this in bootstrap.
11043 Roo.namespace('Roo.form');
11045 * @class Roo.form.Action
11046 * Internal Class used to handle form actions
11048 * @param {Roo.form.BasicForm} el The form element or its id
11049 * @param {Object} config Configuration options
11054 // define the action interface
11055 Roo.form.Action = function(form, options){
11057 this.options = options || {};
11060 * Client Validation Failed
11063 Roo.form.Action.CLIENT_INVALID = 'client';
11065 * Server Validation Failed
11068 Roo.form.Action.SERVER_INVALID = 'server';
11070 * Connect to Server Failed
11073 Roo.form.Action.CONNECT_FAILURE = 'connect';
11075 * Reading Data from Server Failed
11078 Roo.form.Action.LOAD_FAILURE = 'load';
11080 Roo.form.Action.prototype = {
11082 failureType : undefined,
11083 response : undefined,
11084 result : undefined,
11086 // interface method
11087 run : function(options){
11091 // interface method
11092 success : function(response){
11096 // interface method
11097 handleResponse : function(response){
11101 // default connection failure
11102 failure : function(response){
11104 this.response = response;
11105 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11106 this.form.afterAction(this, false);
11109 processResponse : function(response){
11110 this.response = response;
11111 if(!response.responseText){
11114 this.result = this.handleResponse(response);
11115 return this.result;
11118 // utility functions used internally
11119 getUrl : function(appendParams){
11120 var url = this.options.url || this.form.url || this.form.el.dom.action;
11122 var p = this.getParams();
11124 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11130 getMethod : function(){
11131 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11134 getParams : function(){
11135 var bp = this.form.baseParams;
11136 var p = this.options.params;
11138 if(typeof p == "object"){
11139 p = Roo.urlEncode(Roo.applyIf(p, bp));
11140 }else if(typeof p == 'string' && bp){
11141 p += '&' + Roo.urlEncode(bp);
11144 p = Roo.urlEncode(bp);
11149 createCallback : function(){
11151 success: this.success,
11152 failure: this.failure,
11154 timeout: (this.form.timeout*1000),
11155 upload: this.form.fileUpload ? this.success : undefined
11160 Roo.form.Action.Submit = function(form, options){
11161 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11164 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11167 haveProgress : false,
11168 uploadComplete : false,
11170 // uploadProgress indicator.
11171 uploadProgress : function()
11173 if (!this.form.progressUrl) {
11177 if (!this.haveProgress) {
11178 Roo.MessageBox.progress("Uploading", "Uploading");
11180 if (this.uploadComplete) {
11181 Roo.MessageBox.hide();
11185 this.haveProgress = true;
11187 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11189 var c = new Roo.data.Connection();
11191 url : this.form.progressUrl,
11196 success : function(req){
11197 //console.log(data);
11201 rdata = Roo.decode(req.responseText)
11203 Roo.log("Invalid data from server..");
11207 if (!rdata || !rdata.success) {
11209 Roo.MessageBox.alert(Roo.encode(rdata));
11212 var data = rdata.data;
11214 if (this.uploadComplete) {
11215 Roo.MessageBox.hide();
11220 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11221 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11224 this.uploadProgress.defer(2000,this);
11227 failure: function(data) {
11228 Roo.log('progress url failed ');
11239 // run get Values on the form, so it syncs any secondary forms.
11240 this.form.getValues();
11242 var o = this.options;
11243 var method = this.getMethod();
11244 var isPost = method == 'POST';
11245 if(o.clientValidation === false || this.form.isValid()){
11247 if (this.form.progressUrl) {
11248 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11249 (new Date() * 1) + '' + Math.random());
11254 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11255 form:this.form.el.dom,
11256 url:this.getUrl(!isPost),
11258 params:isPost ? this.getParams() : null,
11259 isUpload: this.form.fileUpload,
11260 formData : this.form.formData
11263 this.uploadProgress();
11265 }else if (o.clientValidation !== false){ // client validation failed
11266 this.failureType = Roo.form.Action.CLIENT_INVALID;
11267 this.form.afterAction(this, false);
11271 success : function(response)
11273 this.uploadComplete= true;
11274 if (this.haveProgress) {
11275 Roo.MessageBox.hide();
11279 var result = this.processResponse(response);
11280 if(result === true || result.success){
11281 this.form.afterAction(this, true);
11285 this.form.markInvalid(result.errors);
11286 this.failureType = Roo.form.Action.SERVER_INVALID;
11288 this.form.afterAction(this, false);
11290 failure : function(response)
11292 this.uploadComplete= true;
11293 if (this.haveProgress) {
11294 Roo.MessageBox.hide();
11297 this.response = response;
11298 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11299 this.form.afterAction(this, false);
11302 handleResponse : function(response){
11303 if(this.form.errorReader){
11304 var rs = this.form.errorReader.read(response);
11307 for(var i = 0, len = rs.records.length; i < len; i++) {
11308 var r = rs.records[i];
11309 errors[i] = r.data;
11312 if(errors.length < 1){
11316 success : rs.success,
11322 ret = Roo.decode(response.responseText);
11326 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11336 Roo.form.Action.Load = function(form, options){
11337 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11338 this.reader = this.form.reader;
11341 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11346 Roo.Ajax.request(Roo.apply(
11347 this.createCallback(), {
11348 method:this.getMethod(),
11349 url:this.getUrl(false),
11350 params:this.getParams()
11354 success : function(response){
11356 var result = this.processResponse(response);
11357 if(result === true || !result.success || !result.data){
11358 this.failureType = Roo.form.Action.LOAD_FAILURE;
11359 this.form.afterAction(this, false);
11362 this.form.clearInvalid();
11363 this.form.setValues(result.data);
11364 this.form.afterAction(this, true);
11367 handleResponse : function(response){
11368 if(this.form.reader){
11369 var rs = this.form.reader.read(response);
11370 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11372 success : rs.success,
11376 return Roo.decode(response.responseText);
11380 Roo.form.Action.ACTION_TYPES = {
11381 'load' : Roo.form.Action.Load,
11382 'submit' : Roo.form.Action.Submit
11391 * @class Roo.bootstrap.form.Form
11392 * @extends Roo.bootstrap.Component
11393 * @children Roo.bootstrap.Component
11394 * Bootstrap Form class
11395 * @cfg {String} method GET | POST (default POST)
11396 * @cfg {String} labelAlign top | left (default top)
11397 * @cfg {String} align left | right - for navbars
11398 * @cfg {Boolean} loadMask load mask when submit (default true)
11402 * Create a new Form
11403 * @param {Object} config The config object
11407 Roo.bootstrap.form.Form = function(config){
11409 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11411 Roo.bootstrap.form.Form.popover.apply();
11415 * @event clientvalidation
11416 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11417 * @param {Form} this
11418 * @param {Boolean} valid true if the form has passed client-side validation
11420 clientvalidation: true,
11422 * @event beforeaction
11423 * Fires before any action is performed. Return false to cancel the action.
11424 * @param {Form} this
11425 * @param {Action} action The action to be performed
11427 beforeaction: true,
11429 * @event actionfailed
11430 * Fires when an action fails.
11431 * @param {Form} this
11432 * @param {Action} action The action that failed
11434 actionfailed : true,
11436 * @event actioncomplete
11437 * Fires when an action is completed.
11438 * @param {Form} this
11439 * @param {Action} action The action that completed
11441 actioncomplete : true
11445 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11448 * @cfg {String} method
11449 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11453 * @cfg {String} url
11454 * The URL to use for form actions if one isn't supplied in the action options.
11457 * @cfg {Boolean} fileUpload
11458 * Set to true if this form is a file upload.
11462 * @cfg {Object} baseParams
11463 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11467 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11471 * @cfg {Sting} align (left|right) for navbar forms
11476 activeAction : null,
11479 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11480 * element by passing it or its id or mask the form itself by passing in true.
11483 waitMsgTarget : false,
11488 * @cfg {Boolean} errorMask (true|false) default false
11493 * @cfg {Number} maskOffset Default 100
11498 * @cfg {Boolean} maskBody
11502 getAutoCreate : function(){
11506 method : this.method || 'POST',
11507 id : this.id || Roo.id(),
11510 if (this.parent().xtype.match(/^Nav/)) {
11511 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11515 if (this.labelAlign == 'left' ) {
11516 cfg.cls += ' form-horizontal';
11522 initEvents : function()
11524 this.el.on('submit', this.onSubmit, this);
11525 // this was added as random key presses on the form where triggering form submit.
11526 this.el.on('keypress', function(e) {
11527 if (e.getCharCode() != 13) {
11530 // we might need to allow it for textareas.. and some other items.
11531 // check e.getTarget().
11533 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11537 Roo.log("keypress blocked");
11539 e.preventDefault();
11545 onSubmit : function(e){
11550 * Returns true if client-side validation on the form is successful.
11553 isValid : function(){
11554 var items = this.getItems();
11556 var target = false;
11558 items.each(function(f){
11564 Roo.log('invalid field: ' + f.name);
11568 if(!target && f.el.isVisible(true)){
11574 if(this.errorMask && !valid){
11575 Roo.bootstrap.form.Form.popover.mask(this, target);
11582 * Returns true if any fields in this form have changed since their original load.
11585 isDirty : function(){
11587 var items = this.getItems();
11588 items.each(function(f){
11598 * Performs a predefined action (submit or load) or custom actions you define on this form.
11599 * @param {String} actionName The name of the action type
11600 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11601 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11602 * accept other config options):
11604 Property Type Description
11605 ---------------- --------------- ----------------------------------------------------------------------------------
11606 url String The url for the action (defaults to the form's url)
11607 method String The form method to use (defaults to the form's method, or POST if not defined)
11608 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11609 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11610 validate the form on the client (defaults to false)
11612 * @return {BasicForm} this
11614 doAction : function(action, options){
11615 if(typeof action == 'string'){
11616 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11618 if(this.fireEvent('beforeaction', this, action) !== false){
11619 this.beforeAction(action);
11620 action.run.defer(100, action);
11626 beforeAction : function(action){
11627 var o = action.options;
11632 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11634 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11637 // not really supported yet.. ??
11639 //if(this.waitMsgTarget === true){
11640 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11641 //}else if(this.waitMsgTarget){
11642 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11643 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11645 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11651 afterAction : function(action, success){
11652 this.activeAction = null;
11653 var o = action.options;
11658 Roo.get(document.body).unmask();
11664 //if(this.waitMsgTarget === true){
11665 // this.el.unmask();
11666 //}else if(this.waitMsgTarget){
11667 // this.waitMsgTarget.unmask();
11669 // Roo.MessageBox.updateProgress(1);
11670 // Roo.MessageBox.hide();
11677 Roo.callback(o.success, o.scope, [this, action]);
11678 this.fireEvent('actioncomplete', this, action);
11682 // failure condition..
11683 // we have a scenario where updates need confirming.
11684 // eg. if a locking scenario exists..
11685 // we look for { errors : { needs_confirm : true }} in the response.
11687 (typeof(action.result) != 'undefined') &&
11688 (typeof(action.result.errors) != 'undefined') &&
11689 (typeof(action.result.errors.needs_confirm) != 'undefined')
11692 Roo.log("not supported yet");
11695 Roo.MessageBox.confirm(
11696 "Change requires confirmation",
11697 action.result.errorMsg,
11702 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11712 Roo.callback(o.failure, o.scope, [this, action]);
11713 // show an error message if no failed handler is set..
11714 if (!this.hasListener('actionfailed')) {
11715 Roo.log("need to add dialog support");
11717 Roo.MessageBox.alert("Error",
11718 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11719 action.result.errorMsg :
11720 "Saving Failed, please check your entries or try again"
11725 this.fireEvent('actionfailed', this, action);
11730 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11731 * @param {String} id The value to search for
11734 findField : function(id){
11735 var items = this.getItems();
11736 var field = items.get(id);
11738 items.each(function(f){
11739 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11746 return field || null;
11749 * Mark fields in this form invalid in bulk.
11750 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11751 * @return {BasicForm} this
11753 markInvalid : function(errors){
11754 if(errors instanceof Array){
11755 for(var i = 0, len = errors.length; i < len; i++){
11756 var fieldError = errors[i];
11757 var f = this.findField(fieldError.id);
11759 f.markInvalid(fieldError.msg);
11765 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11766 field.markInvalid(errors[id]);
11770 //Roo.each(this.childForms || [], function (f) {
11771 // f.markInvalid(errors);
11778 * Set values for fields in this form in bulk.
11779 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11780 * @return {BasicForm} this
11782 setValues : function(values){
11783 if(values instanceof Array){ // array of objects
11784 for(var i = 0, len = values.length; i < len; i++){
11786 var f = this.findField(v.id);
11788 f.setValue(v.value);
11789 if(this.trackResetOnLoad){
11790 f.originalValue = f.getValue();
11794 }else{ // object hash
11797 if(typeof values[id] != 'function' && (field = this.findField(id))){
11799 if (field.setFromData &&
11800 field.valueField &&
11801 field.displayField &&
11802 // combos' with local stores can
11803 // be queried via setValue()
11804 // to set their value..
11805 (field.store && !field.store.isLocal)
11809 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11810 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11811 field.setFromData(sd);
11813 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11815 field.setFromData(values);
11818 field.setValue(values[id]);
11822 if(this.trackResetOnLoad){
11823 field.originalValue = field.getValue();
11829 //Roo.each(this.childForms || [], function (f) {
11830 // f.setValues(values);
11837 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11838 * they are returned as an array.
11839 * @param {Boolean} asString
11842 getValues : function(asString){
11843 //if (this.childForms) {
11844 // copy values from the child forms
11845 // Roo.each(this.childForms, function (f) {
11846 // this.setValues(f.getValues());
11852 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11853 if(asString === true){
11856 return Roo.urlDecode(fs);
11860 * Returns the fields in this form as an object with key/value pairs.
11861 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11864 getFieldValues : function(with_hidden)
11866 var items = this.getItems();
11868 items.each(function(f){
11870 if (!f.getName()) {
11874 var v = f.getValue();
11876 if (f.inputType =='radio') {
11877 if (typeof(ret[f.getName()]) == 'undefined') {
11878 ret[f.getName()] = ''; // empty..
11881 if (!f.el.dom.checked) {
11885 v = f.el.dom.value;
11889 if(f.xtype == 'MoneyField'){
11890 ret[f.currencyName] = f.getCurrency();
11893 // not sure if this supported any more..
11894 if ((typeof(v) == 'object') && f.getRawValue) {
11895 v = f.getRawValue() ; // dates..
11897 // combo boxes where name != hiddenName...
11898 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11899 ret[f.name] = f.getRawValue();
11901 ret[f.getName()] = v;
11908 * Clears all invalid messages in this form.
11909 * @return {BasicForm} this
11911 clearInvalid : function(){
11912 var items = this.getItems();
11914 items.each(function(f){
11922 * Resets this form.
11923 * @return {BasicForm} this
11925 reset : function(){
11926 var items = this.getItems();
11927 items.each(function(f){
11931 Roo.each(this.childForms || [], function (f) {
11939 getItems : function()
11941 var r=new Roo.util.MixedCollection(false, function(o){
11942 return o.id || (o.id = Roo.id());
11944 var iter = function(el) {
11951 Roo.each(el.items,function(e) {
11960 hideFields : function(items)
11962 Roo.each(items, function(i){
11964 var f = this.findField(i);
11975 showFields : function(items)
11977 Roo.each(items, function(i){
11979 var f = this.findField(i);
11992 Roo.apply(Roo.bootstrap.form.Form, {
12008 intervalID : false,
12014 if(this.isApplied){
12019 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12020 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12021 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12022 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12025 this.maskEl.top.enableDisplayMode("block");
12026 this.maskEl.left.enableDisplayMode("block");
12027 this.maskEl.bottom.enableDisplayMode("block");
12028 this.maskEl.right.enableDisplayMode("block");
12030 this.toolTip = new Roo.bootstrap.Tooltip({
12031 cls : 'roo-form-error-popover',
12033 'left' : ['r-l', [-2,0], 'right'],
12034 'right' : ['l-r', [2,0], 'left'],
12035 'bottom' : ['tl-bl', [0,2], 'top'],
12036 'top' : [ 'bl-tl', [0,-2], 'bottom']
12040 this.toolTip.render(Roo.get(document.body));
12042 this.toolTip.el.enableDisplayMode("block");
12044 Roo.get(document.body).on('click', function(){
12048 Roo.get(document.body).on('touchstart', function(){
12052 this.isApplied = true
12055 mask : function(form, target)
12059 this.target = target;
12061 if(!this.form.errorMask || !target.el){
12065 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12067 Roo.log(scrollable);
12069 var ot = this.target.el.calcOffsetsTo(scrollable);
12071 var scrollTo = ot[1] - this.form.maskOffset;
12073 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12075 scrollable.scrollTo('top', scrollTo);
12077 var box = this.target.el.getBox();
12079 var zIndex = Roo.bootstrap.Modal.zIndex++;
12082 this.maskEl.top.setStyle('position', 'absolute');
12083 this.maskEl.top.setStyle('z-index', zIndex);
12084 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12085 this.maskEl.top.setLeft(0);
12086 this.maskEl.top.setTop(0);
12087 this.maskEl.top.show();
12089 this.maskEl.left.setStyle('position', 'absolute');
12090 this.maskEl.left.setStyle('z-index', zIndex);
12091 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12092 this.maskEl.left.setLeft(0);
12093 this.maskEl.left.setTop(box.y - this.padding);
12094 this.maskEl.left.show();
12096 this.maskEl.bottom.setStyle('position', 'absolute');
12097 this.maskEl.bottom.setStyle('z-index', zIndex);
12098 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12099 this.maskEl.bottom.setLeft(0);
12100 this.maskEl.bottom.setTop(box.bottom + this.padding);
12101 this.maskEl.bottom.show();
12103 this.maskEl.right.setStyle('position', 'absolute');
12104 this.maskEl.right.setStyle('z-index', zIndex);
12105 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12106 this.maskEl.right.setLeft(box.right + this.padding);
12107 this.maskEl.right.setTop(box.y - this.padding);
12108 this.maskEl.right.show();
12110 this.toolTip.bindEl = this.target.el;
12112 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12114 var tip = this.target.blankText;
12116 if(this.target.getValue() !== '' ) {
12118 if (this.target.invalidText.length) {
12119 tip = this.target.invalidText;
12120 } else if (this.target.regexText.length){
12121 tip = this.target.regexText;
12125 this.toolTip.show(tip);
12127 this.intervalID = window.setInterval(function() {
12128 Roo.bootstrap.form.Form.popover.unmask();
12131 window.onwheel = function(){ return false;};
12133 (function(){ this.isMasked = true; }).defer(500, this);
12137 unmask : function()
12139 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12143 this.maskEl.top.setStyle('position', 'absolute');
12144 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12145 this.maskEl.top.hide();
12147 this.maskEl.left.setStyle('position', 'absolute');
12148 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12149 this.maskEl.left.hide();
12151 this.maskEl.bottom.setStyle('position', 'absolute');
12152 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12153 this.maskEl.bottom.hide();
12155 this.maskEl.right.setStyle('position', 'absolute');
12156 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12157 this.maskEl.right.hide();
12159 this.toolTip.hide();
12161 this.toolTip.el.hide();
12163 window.onwheel = function(){ return true;};
12165 if(this.intervalID){
12166 window.clearInterval(this.intervalID);
12167 this.intervalID = false;
12170 this.isMasked = false;
12180 * Ext JS Library 1.1.1
12181 * Copyright(c) 2006-2007, Ext JS, LLC.
12183 * Originally Released Under LGPL - original licence link has changed is not relivant.
12186 * <script type="text/javascript">
12189 * @class Roo.form.VTypes
12190 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12193 Roo.form.VTypes = function(){
12194 // closure these in so they are only created once.
12195 var alpha = /^[a-zA-Z_]+$/;
12196 var alphanum = /^[a-zA-Z0-9_]+$/;
12197 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12198 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12200 // All these messages and functions are configurable
12203 * The function used to validate email addresses
12204 * @param {String} value The email address
12206 email : function(v){
12207 return email.test(v);
12210 * The error text to display when the email validation function returns false
12213 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12215 * The keystroke filter mask to be applied on email input
12218 emailMask : /[a-z0-9_\.\-@]/i,
12221 * The function used to validate URLs
12222 * @param {String} value The URL
12225 return url.test(v);
12228 * The error text to display when the url validation function returns false
12231 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12234 * The function used to validate alpha values
12235 * @param {String} value The value
12237 alpha : function(v){
12238 return alpha.test(v);
12241 * The error text to display when the alpha validation function returns false
12244 alphaText : 'This field should only contain letters and _',
12246 * The keystroke filter mask to be applied on alpha input
12249 alphaMask : /[a-z_]/i,
12252 * The function used to validate alphanumeric values
12253 * @param {String} value The value
12255 alphanum : function(v){
12256 return alphanum.test(v);
12259 * The error text to display when the alphanumeric validation function returns false
12262 alphanumText : 'This field should only contain letters, numbers and _',
12264 * The keystroke filter mask to be applied on alphanumeric input
12267 alphanumMask : /[a-z0-9_]/i
12277 * @class Roo.bootstrap.form.Input
12278 * @extends Roo.bootstrap.Component
12279 * Bootstrap Input class
12280 * @cfg {Boolean} disabled is it disabled
12281 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12282 * @cfg {String} name name of the input
12283 * @cfg {string} fieldLabel - the label associated
12284 * @cfg {string} placeholder - placeholder to put in text.
12285 * @cfg {string} before - input group add on before
12286 * @cfg {string} after - input group add on after
12287 * @cfg {string} size - (lg|sm) or leave empty..
12288 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12289 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12290 * @cfg {Number} md colspan out of 12 for computer-sized screens
12291 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12292 * @cfg {string} value default value of the input
12293 * @cfg {Number} labelWidth set the width of label
12294 * @cfg {Number} labellg set the width of label (1-12)
12295 * @cfg {Number} labelmd set the width of label (1-12)
12296 * @cfg {Number} labelsm set the width of label (1-12)
12297 * @cfg {Number} labelxs set the width of label (1-12)
12298 * @cfg {String} labelAlign (top|left)
12299 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12300 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12301 * @cfg {String} indicatorpos (left|right) default left
12302 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12303 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12304 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12305 * @cfg {Roo.bootstrap.Button} before Button to show before
12306 * @cfg {Roo.bootstrap.Button} afterButton to show before
12307 * @cfg {String} align (left|center|right) Default left
12308 * @cfg {Boolean} forceFeedback (true|false) Default false
12311 * Create a new Input
12312 * @param {Object} config The config object
12315 Roo.bootstrap.form.Input = function(config){
12317 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12322 * Fires when this field receives input focus.
12323 * @param {Roo.form.Field} this
12328 * Fires when this field loses input focus.
12329 * @param {Roo.form.Field} this
12333 * @event specialkey
12334 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12335 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12336 * @param {Roo.form.Field} this
12337 * @param {Roo.EventObject} e The event object
12342 * Fires just before the field blurs if the field value has changed.
12343 * @param {Roo.form.Field} this
12344 * @param {Mixed} newValue The new value
12345 * @param {Mixed} oldValue The original value
12350 * Fires after the field has been marked as invalid.
12351 * @param {Roo.form.Field} this
12352 * @param {String} msg The validation message
12357 * Fires after the field has been validated with no errors.
12358 * @param {Roo.form.Field} this
12363 * Fires after the key up
12364 * @param {Roo.form.Field} this
12365 * @param {Roo.EventObject} e The event Object
12370 * Fires after the user pastes into input
12371 * @param {Roo.form.Field} this
12372 * @param {Roo.EventObject} e The event Object
12378 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12380 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12381 automatic validation (defaults to "keyup").
12383 validationEvent : "keyup",
12385 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12387 validateOnBlur : true,
12389 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12391 validationDelay : 250,
12393 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12395 focusClass : "x-form-focus", // not needed???
12399 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12401 invalidClass : "has-warning",
12404 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12406 validClass : "has-success",
12409 * @cfg {Boolean} hasFeedback (true|false) default true
12411 hasFeedback : true,
12414 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12416 invalidFeedbackClass : "glyphicon-warning-sign",
12419 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12421 validFeedbackClass : "glyphicon-ok",
12424 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12426 selectOnFocus : false,
12429 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12433 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12438 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12440 disableKeyFilter : false,
12443 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12447 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12451 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12453 blankText : "Please complete this mandatory field",
12456 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12460 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12462 maxLength : Number.MAX_VALUE,
12464 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12466 minLengthText : "The minimum length for this field is {0}",
12468 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12470 maxLengthText : "The maximum length for this field is {0}",
12474 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12475 * If available, this function will be called only after the basic validators all return true, and will be passed the
12476 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12480 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12481 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12482 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12486 * @cfg {String} regexText -- Depricated - use Invalid Text
12491 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12497 autocomplete: false,
12501 inputType : 'text',
12504 placeholder: false,
12509 preventMark: false,
12510 isFormField : true,
12513 labelAlign : false,
12516 formatedValue : false,
12517 forceFeedback : false,
12519 indicatorpos : 'left',
12529 parentLabelAlign : function()
12532 while (parent.parent()) {
12533 parent = parent.parent();
12534 if (typeof(parent.labelAlign) !='undefined') {
12535 return parent.labelAlign;
12542 getAutoCreate : function()
12544 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12550 if(this.inputType != 'hidden'){
12551 cfg.cls = 'form-group' //input-group
12557 type : this.inputType,
12558 value : this.value,
12559 cls : 'form-control',
12560 placeholder : this.placeholder || '',
12561 autocomplete : this.autocomplete || 'new-password'
12563 if (this.inputType == 'file') {
12564 input.style = 'overflow:hidden'; // why not in CSS?
12567 if(this.capture.length){
12568 input.capture = this.capture;
12571 if(this.accept.length){
12572 input.accept = this.accept + "/*";
12576 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12579 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12580 input.maxLength = this.maxLength;
12583 if (this.disabled) {
12584 input.disabled=true;
12587 if (this.readOnly) {
12588 input.readonly=true;
12592 input.name = this.name;
12596 input.cls += ' input-' + this.size;
12600 ['xs','sm','md','lg'].map(function(size){
12601 if (settings[size]) {
12602 cfg.cls += ' col-' + size + '-' + settings[size];
12606 var inputblock = input;
12610 cls: 'glyphicon form-control-feedback'
12613 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12616 cls : 'has-feedback',
12624 if (this.before || this.after) {
12627 cls : 'input-group',
12631 if (this.before && typeof(this.before) == 'string') {
12633 inputblock.cn.push({
12635 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12639 if (this.before && typeof(this.before) == 'object') {
12640 this.before = Roo.factory(this.before);
12642 inputblock.cn.push({
12644 cls : 'roo-input-before input-group-prepend input-group-' +
12645 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12649 inputblock.cn.push(input);
12651 if (this.after && typeof(this.after) == 'string') {
12652 inputblock.cn.push({
12654 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12658 if (this.after && typeof(this.after) == 'object') {
12659 this.after = Roo.factory(this.after);
12661 inputblock.cn.push({
12663 cls : 'roo-input-after input-group-append input-group-' +
12664 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12668 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12669 inputblock.cls += ' has-feedback';
12670 inputblock.cn.push(feedback);
12675 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12676 tooltip : 'This field is required'
12678 if (this.allowBlank ) {
12679 indicator.style = this.allowBlank ? ' display:none' : '';
12681 if (align ==='left' && this.fieldLabel.length) {
12683 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12690 cls : 'control-label col-form-label',
12691 html : this.fieldLabel
12702 var labelCfg = cfg.cn[1];
12703 var contentCfg = cfg.cn[2];
12705 if(this.indicatorpos == 'right'){
12710 cls : 'control-label col-form-label',
12714 html : this.fieldLabel
12728 labelCfg = cfg.cn[0];
12729 contentCfg = cfg.cn[1];
12733 if(this.labelWidth > 12){
12734 labelCfg.style = "width: " + this.labelWidth + 'px';
12737 if(this.labelWidth < 13 && this.labelmd == 0){
12738 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12741 if(this.labellg > 0){
12742 labelCfg.cls += ' col-lg-' + this.labellg;
12743 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12746 if(this.labelmd > 0){
12747 labelCfg.cls += ' col-md-' + this.labelmd;
12748 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12751 if(this.labelsm > 0){
12752 labelCfg.cls += ' col-sm-' + this.labelsm;
12753 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12756 if(this.labelxs > 0){
12757 labelCfg.cls += ' col-xs-' + this.labelxs;
12758 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12762 } else if ( this.fieldLabel.length) {
12769 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12770 tooltip : 'This field is required',
12771 style : this.allowBlank ? ' display:none' : ''
12775 //cls : 'input-group-addon',
12776 html : this.fieldLabel
12784 if(this.indicatorpos == 'right'){
12789 //cls : 'input-group-addon',
12790 html : this.fieldLabel
12795 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12796 tooltip : 'This field is required',
12797 style : this.allowBlank ? ' display:none' : ''
12817 if (this.parentType === 'Navbar' && this.parent().bar) {
12818 cfg.cls += ' navbar-form';
12821 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12822 // on BS4 we do this only if not form
12823 cfg.cls += ' navbar-form';
12831 * return the real input element.
12833 inputEl: function ()
12835 return this.el.select('input.form-control',true).first();
12838 tooltipEl : function()
12840 return this.inputEl();
12843 indicatorEl : function()
12845 if (Roo.bootstrap.version == 4) {
12846 return false; // not enabled in v4 yet.
12849 var indicator = this.el.select('i.roo-required-indicator',true).first();
12859 setDisabled : function(v)
12861 var i = this.inputEl().dom;
12863 i.removeAttribute('disabled');
12867 i.setAttribute('disabled','true');
12869 initEvents : function()
12872 this.inputEl().on("keydown" , this.fireKey, this);
12873 this.inputEl().on("focus", this.onFocus, this);
12874 this.inputEl().on("blur", this.onBlur, this);
12876 this.inputEl().relayEvent('keyup', this);
12877 this.inputEl().relayEvent('paste', this);
12879 this.indicator = this.indicatorEl();
12881 if(this.indicator){
12882 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12885 // reference to original value for reset
12886 this.originalValue = this.getValue();
12887 //Roo.form.TextField.superclass.initEvents.call(this);
12888 if(this.validationEvent == 'keyup'){
12889 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12890 this.inputEl().on('keyup', this.filterValidation, this);
12892 else if(this.validationEvent !== false){
12893 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12896 if(this.selectOnFocus){
12897 this.on("focus", this.preFocus, this);
12900 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12901 this.inputEl().on("keypress", this.filterKeys, this);
12903 this.inputEl().relayEvent('keypress', this);
12906 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12907 this.el.on("click", this.autoSize, this);
12910 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12911 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12914 if (typeof(this.before) == 'object') {
12915 this.before.render(this.el.select('.roo-input-before',true).first());
12917 if (typeof(this.after) == 'object') {
12918 this.after.render(this.el.select('.roo-input-after',true).first());
12921 this.inputEl().on('change', this.onChange, this);
12924 filterValidation : function(e){
12925 if(!e.isNavKeyPress()){
12926 this.validationTask.delay(this.validationDelay);
12930 * Validates the field value
12931 * @return {Boolean} True if the value is valid, else false
12933 validate : function(){
12934 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12935 if(this.disabled || this.validateValue(this.getRawValue())){
12940 this.markInvalid();
12946 * Validates a value according to the field's validation rules and marks the field as invalid
12947 * if the validation fails
12948 * @param {Mixed} value The value to validate
12949 * @return {Boolean} True if the value is valid, else false
12951 validateValue : function(value)
12953 if(this.getVisibilityEl().hasClass('hidden')){
12957 if(value.length < 1) { // if it's blank
12958 if(this.allowBlank){
12964 if(value.length < this.minLength){
12967 if(value.length > this.maxLength){
12971 var vt = Roo.form.VTypes;
12972 if(!vt[this.vtype](value, this)){
12976 if(typeof this.validator == "function"){
12977 var msg = this.validator(value);
12978 if (typeof(msg) == 'string') {
12979 this.invalidText = msg;
12986 if(this.regex && !this.regex.test(value)){
12994 fireKey : function(e){
12995 //Roo.log('field ' + e.getKey());
12996 if(e.isNavKeyPress()){
12997 this.fireEvent("specialkey", this, e);
13000 focus : function (selectText){
13002 this.inputEl().focus();
13003 if(selectText === true){
13004 this.inputEl().dom.select();
13010 onFocus : function(){
13011 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13012 // this.el.addClass(this.focusClass);
13014 if(!this.hasFocus){
13015 this.hasFocus = true;
13016 this.startValue = this.getValue();
13017 this.fireEvent("focus", this);
13021 beforeBlur : Roo.emptyFn,
13025 onBlur : function(){
13027 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13028 //this.el.removeClass(this.focusClass);
13030 this.hasFocus = false;
13031 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13034 var v = this.getValue();
13035 if(String(v) !== String(this.startValue)){
13036 this.fireEvent('change', this, v, this.startValue);
13038 this.fireEvent("blur", this);
13041 onChange : function(e)
13043 var v = this.getValue();
13044 if(String(v) !== String(this.startValue)){
13045 this.fireEvent('change', this, v, this.startValue);
13051 * Resets the current field value to the originally loaded value and clears any validation messages
13053 reset : function(){
13054 this.setValue(this.originalValue);
13058 * Returns the name of the field
13059 * @return {Mixed} name The name field
13061 getName: function(){
13065 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13066 * @return {Mixed} value The field value
13068 getValue : function(){
13070 var v = this.inputEl().getValue();
13075 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13076 * @return {Mixed} value The field value
13078 getRawValue : function(){
13079 var v = this.inputEl().getValue();
13085 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13086 * @param {Mixed} value The value to set
13088 setRawValue : function(v){
13089 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13092 selectText : function(start, end){
13093 var v = this.getRawValue();
13095 start = start === undefined ? 0 : start;
13096 end = end === undefined ? v.length : end;
13097 var d = this.inputEl().dom;
13098 if(d.setSelectionRange){
13099 d.setSelectionRange(start, end);
13100 }else if(d.createTextRange){
13101 var range = d.createTextRange();
13102 range.moveStart("character", start);
13103 range.moveEnd("character", v.length-end);
13110 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13111 * @param {Mixed} value The value to set
13113 setValue : function(v){
13116 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13122 processValue : function(value){
13123 if(this.stripCharsRe){
13124 var newValue = value.replace(this.stripCharsRe, '');
13125 if(newValue !== value){
13126 this.setRawValue(newValue);
13133 preFocus : function(){
13135 if(this.selectOnFocus){
13136 this.inputEl().dom.select();
13139 filterKeys : function(e){
13140 var k = e.getKey();
13141 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13144 var c = e.getCharCode(), cc = String.fromCharCode(c);
13145 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13148 if(!this.maskRe.test(cc)){
13153 * Clear any invalid styles/messages for this field
13155 clearInvalid : function(){
13157 if(!this.el || this.preventMark){ // not rendered
13162 this.el.removeClass([this.invalidClass, 'is-invalid']);
13164 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13166 var feedback = this.el.select('.form-control-feedback', true).first();
13169 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13174 if(this.indicator){
13175 this.indicator.removeClass('visible');
13176 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13179 this.fireEvent('valid', this);
13183 * Mark this field as valid
13185 markValid : function()
13187 if(!this.el || this.preventMark){ // not rendered...
13191 this.el.removeClass([this.invalidClass, this.validClass]);
13192 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13194 var feedback = this.el.select('.form-control-feedback', true).first();
13197 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13200 if(this.indicator){
13201 this.indicator.removeClass('visible');
13202 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13210 if(this.allowBlank && !this.getRawValue().length){
13213 if (Roo.bootstrap.version == 3) {
13214 this.el.addClass(this.validClass);
13216 this.inputEl().addClass('is-valid');
13219 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13221 var feedback = this.el.select('.form-control-feedback', true).first();
13224 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13225 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13230 this.fireEvent('valid', this);
13234 * Mark this field as invalid
13235 * @param {String} msg The validation message
13237 markInvalid : function(msg)
13239 if(!this.el || this.preventMark){ // not rendered
13243 this.el.removeClass([this.invalidClass, this.validClass]);
13244 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13246 var feedback = this.el.select('.form-control-feedback', true).first();
13249 this.el.select('.form-control-feedback', true).first().removeClass(
13250 [this.invalidFeedbackClass, this.validFeedbackClass]);
13257 if(this.allowBlank && !this.getRawValue().length){
13261 if(this.indicator){
13262 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13263 this.indicator.addClass('visible');
13265 if (Roo.bootstrap.version == 3) {
13266 this.el.addClass(this.invalidClass);
13268 this.inputEl().addClass('is-invalid');
13273 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13275 var feedback = this.el.select('.form-control-feedback', true).first();
13278 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13280 if(this.getValue().length || this.forceFeedback){
13281 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13288 this.fireEvent('invalid', this, msg);
13291 SafariOnKeyDown : function(event)
13293 // this is a workaround for a password hang bug on chrome/ webkit.
13294 if (this.inputEl().dom.type != 'password') {
13298 var isSelectAll = false;
13300 if(this.inputEl().dom.selectionEnd > 0){
13301 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13303 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13304 event.preventDefault();
13309 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13311 event.preventDefault();
13312 // this is very hacky as keydown always get's upper case.
13314 var cc = String.fromCharCode(event.getCharCode());
13315 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13319 adjustWidth : function(tag, w){
13320 tag = tag.toLowerCase();
13321 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13322 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13323 if(tag == 'input'){
13326 if(tag == 'textarea'){
13329 }else if(Roo.isOpera){
13330 if(tag == 'input'){
13333 if(tag == 'textarea'){
13341 setFieldLabel : function(v)
13343 if(!this.rendered){
13347 if(this.indicatorEl()){
13348 var ar = this.el.select('label > span',true);
13350 if (ar.elements.length) {
13351 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13352 this.fieldLabel = v;
13356 var br = this.el.select('label',true);
13358 if(br.elements.length) {
13359 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13360 this.fieldLabel = v;
13364 Roo.log('Cannot Found any of label > span || label in input');
13368 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13369 this.fieldLabel = v;
13384 * @class Roo.bootstrap.form.TextArea
13385 * @extends Roo.bootstrap.form.Input
13386 * Bootstrap TextArea class
13387 * @cfg {Number} cols Specifies the visible width of a text area
13388 * @cfg {Number} rows Specifies the visible number of lines in a text area
13389 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13390 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13391 * @cfg {string} html text
13394 * Create a new TextArea
13395 * @param {Object} config The config object
13398 Roo.bootstrap.form.TextArea = function(config){
13399 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13403 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13413 getAutoCreate : function(){
13415 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13421 if(this.inputType != 'hidden'){
13422 cfg.cls = 'form-group' //input-group
13430 value : this.value || '',
13431 html: this.html || '',
13432 cls : 'form-control',
13433 placeholder : this.placeholder || ''
13437 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13438 input.maxLength = this.maxLength;
13442 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13446 input.cols = this.cols;
13449 if (this.readOnly) {
13450 input.readonly = true;
13454 input.name = this.name;
13458 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13462 ['xs','sm','md','lg'].map(function(size){
13463 if (settings[size]) {
13464 cfg.cls += ' col-' + size + '-' + settings[size];
13468 var inputblock = input;
13470 if(this.hasFeedback && !this.allowBlank){
13474 cls: 'glyphicon form-control-feedback'
13478 cls : 'has-feedback',
13487 if (this.before || this.after) {
13490 cls : 'input-group',
13494 inputblock.cn.push({
13496 cls : 'input-group-addon',
13501 inputblock.cn.push(input);
13503 if(this.hasFeedback && !this.allowBlank){
13504 inputblock.cls += ' has-feedback';
13505 inputblock.cn.push(feedback);
13509 inputblock.cn.push({
13511 cls : 'input-group-addon',
13518 if (align ==='left' && this.fieldLabel.length) {
13523 cls : 'control-label',
13524 html : this.fieldLabel
13535 if(this.labelWidth > 12){
13536 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13539 if(this.labelWidth < 13 && this.labelmd == 0){
13540 this.labelmd = this.labelWidth;
13543 if(this.labellg > 0){
13544 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13545 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13548 if(this.labelmd > 0){
13549 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13550 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13553 if(this.labelsm > 0){
13554 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13555 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13558 if(this.labelxs > 0){
13559 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13560 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13563 } else if ( this.fieldLabel.length) {
13568 //cls : 'input-group-addon',
13569 html : this.fieldLabel
13587 if (this.disabled) {
13588 input.disabled=true;
13595 * return the real textarea element.
13597 inputEl: function ()
13599 return this.el.select('textarea.form-control',true).first();
13603 * Clear any invalid styles/messages for this field
13605 clearInvalid : function()
13608 if(!this.el || this.preventMark){ // not rendered
13612 var label = this.el.select('label', true).first();
13613 var icon = this.el.select('i.fa-star', true).first();
13618 this.el.removeClass( this.validClass);
13619 this.inputEl().removeClass('is-invalid');
13621 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13623 var feedback = this.el.select('.form-control-feedback', true).first();
13626 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13631 this.fireEvent('valid', this);
13635 * Mark this field as valid
13637 markValid : function()
13639 if(!this.el || this.preventMark){ // not rendered
13643 this.el.removeClass([this.invalidClass, this.validClass]);
13644 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13646 var feedback = this.el.select('.form-control-feedback', true).first();
13649 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13652 if(this.disabled || this.allowBlank){
13656 var label = this.el.select('label', true).first();
13657 var icon = this.el.select('i.fa-star', true).first();
13662 if (Roo.bootstrap.version == 3) {
13663 this.el.addClass(this.validClass);
13665 this.inputEl().addClass('is-valid');
13669 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13671 var feedback = this.el.select('.form-control-feedback', true).first();
13674 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13675 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13680 this.fireEvent('valid', this);
13684 * Mark this field as invalid
13685 * @param {String} msg The validation message
13687 markInvalid : function(msg)
13689 if(!this.el || this.preventMark){ // not rendered
13693 this.el.removeClass([this.invalidClass, this.validClass]);
13694 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13696 var feedback = this.el.select('.form-control-feedback', true).first();
13699 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13702 if(this.disabled || this.allowBlank){
13706 var label = this.el.select('label', true).first();
13707 var icon = this.el.select('i.fa-star', true).first();
13709 if(!this.getValue().length && label && !icon){
13710 this.el.createChild({
13712 cls : 'text-danger fa fa-lg fa-star',
13713 tooltip : 'This field is required',
13714 style : 'margin-right:5px;'
13718 if (Roo.bootstrap.version == 3) {
13719 this.el.addClass(this.invalidClass);
13721 this.inputEl().addClass('is-invalid');
13724 // fixme ... this may be depricated need to test..
13725 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13727 var feedback = this.el.select('.form-control-feedback', true).first();
13730 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13732 if(this.getValue().length || this.forceFeedback){
13733 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13740 this.fireEvent('invalid', this, msg);
13748 * trigger field - base class for combo..
13753 * @class Roo.bootstrap.form.TriggerField
13754 * @extends Roo.bootstrap.form.Input
13755 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13756 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13757 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13758 * for which you can provide a custom implementation. For example:
13760 var trigger = new Roo.bootstrap.form.TriggerField();
13761 trigger.onTriggerClick = myTriggerFn;
13762 trigger.applyTo('my-field');
13765 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13766 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13767 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13768 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13769 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13772 * Create a new TriggerField.
13773 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13774 * to the base TextField)
13776 Roo.bootstrap.form.TriggerField = function(config){
13777 this.mimicing = false;
13778 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13781 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13783 * @cfg {String} triggerClass A CSS class to apply to the trigger
13786 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13791 * @cfg {Boolean} removable (true|false) special filter default false
13795 /** @cfg {Boolean} grow @hide */
13796 /** @cfg {Number} growMin @hide */
13797 /** @cfg {Number} growMax @hide */
13803 autoSize: Roo.emptyFn,
13807 deferHeight : true,
13810 actionMode : 'wrap',
13815 getAutoCreate : function(){
13817 var align = this.labelAlign || this.parentLabelAlign();
13822 cls: 'form-group' //input-group
13829 type : this.inputType,
13830 cls : 'form-control',
13831 autocomplete: 'new-password',
13832 placeholder : this.placeholder || ''
13836 input.name = this.name;
13839 input.cls += ' input-' + this.size;
13842 if (this.disabled) {
13843 input.disabled=true;
13846 var inputblock = input;
13848 if(this.hasFeedback && !this.allowBlank){
13852 cls: 'glyphicon form-control-feedback'
13855 if(this.removable && !this.editable ){
13857 cls : 'has-feedback',
13863 cls : 'roo-combo-removable-btn close'
13870 cls : 'has-feedback',
13879 if(this.removable && !this.editable ){
13881 cls : 'roo-removable',
13887 cls : 'roo-combo-removable-btn close'
13894 if (this.before || this.after) {
13897 cls : 'input-group',
13901 inputblock.cn.push({
13903 cls : 'input-group-addon input-group-prepend input-group-text',
13908 inputblock.cn.push(input);
13910 if(this.hasFeedback && !this.allowBlank){
13911 inputblock.cls += ' has-feedback';
13912 inputblock.cn.push(feedback);
13916 inputblock.cn.push({
13918 cls : 'input-group-addon input-group-append input-group-text',
13927 var ibwrap = inputblock;
13932 cls: 'roo-select2-choices',
13936 cls: 'roo-select2-search-field',
13948 cls: 'roo-select2-container input-group',
13953 cls: 'form-hidden-field'
13959 if(!this.multiple && this.showToggleBtn){
13965 if (this.caret != false) {
13968 cls: 'fa fa-' + this.caret
13975 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13977 Roo.bootstrap.version == 3 ? caret : '',
13980 cls: 'combobox-clear',
13994 combobox.cls += ' roo-select2-container-multi';
13998 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13999 tooltip : 'This field is required'
14001 if (Roo.bootstrap.version == 4) {
14004 style : 'display:none'
14009 if (align ==='left' && this.fieldLabel.length) {
14011 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14018 cls : 'control-label',
14019 html : this.fieldLabel
14031 var labelCfg = cfg.cn[1];
14032 var contentCfg = cfg.cn[2];
14034 if(this.indicatorpos == 'right'){
14039 cls : 'control-label',
14043 html : this.fieldLabel
14057 labelCfg = cfg.cn[0];
14058 contentCfg = cfg.cn[1];
14061 if(this.labelWidth > 12){
14062 labelCfg.style = "width: " + this.labelWidth + 'px';
14065 if(this.labelWidth < 13 && this.labelmd == 0){
14066 this.labelmd = this.labelWidth;
14069 if(this.labellg > 0){
14070 labelCfg.cls += ' col-lg-' + this.labellg;
14071 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14074 if(this.labelmd > 0){
14075 labelCfg.cls += ' col-md-' + this.labelmd;
14076 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14079 if(this.labelsm > 0){
14080 labelCfg.cls += ' col-sm-' + this.labelsm;
14081 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14084 if(this.labelxs > 0){
14085 labelCfg.cls += ' col-xs-' + this.labelxs;
14086 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14089 } else if ( this.fieldLabel.length) {
14090 // Roo.log(" label");
14095 //cls : 'input-group-addon',
14096 html : this.fieldLabel
14104 if(this.indicatorpos == 'right'){
14112 html : this.fieldLabel
14126 // Roo.log(" no label && no align");
14133 ['xs','sm','md','lg'].map(function(size){
14134 if (settings[size]) {
14135 cfg.cls += ' col-' + size + '-' + settings[size];
14146 onResize : function(w, h){
14147 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14148 // if(typeof w == 'number'){
14149 // var x = w - this.trigger.getWidth();
14150 // this.inputEl().setWidth(this.adjustWidth('input', x));
14151 // this.trigger.setStyle('left', x+'px');
14156 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14159 getResizeEl : function(){
14160 return this.inputEl();
14164 getPositionEl : function(){
14165 return this.inputEl();
14169 alignErrorIcon : function(){
14170 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14174 initEvents : function(){
14178 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14179 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14180 if(!this.multiple && this.showToggleBtn){
14181 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14182 if(this.hideTrigger){
14183 this.trigger.setDisplayed(false);
14185 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14189 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14192 if(this.removable && !this.editable && !this.tickable){
14193 var close = this.closeTriggerEl();
14196 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14197 close.on('click', this.removeBtnClick, this, close);
14201 //this.trigger.addClassOnOver('x-form-trigger-over');
14202 //this.trigger.addClassOnClick('x-form-trigger-click');
14205 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14209 closeTriggerEl : function()
14211 var close = this.el.select('.roo-combo-removable-btn', true).first();
14212 return close ? close : false;
14215 removeBtnClick : function(e, h, el)
14217 e.preventDefault();
14219 if(this.fireEvent("remove", this) !== false){
14221 this.fireEvent("afterremove", this)
14225 createList : function()
14227 this.list = Roo.get(document.body).createChild({
14228 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14229 cls: 'typeahead typeahead-long dropdown-menu shadow',
14230 style: 'display:none'
14233 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14238 initTrigger : function(){
14243 onDestroy : function(){
14245 this.trigger.removeAllListeners();
14246 // this.trigger.remove();
14249 // this.wrap.remove();
14251 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14255 onFocus : function(){
14256 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14258 if(!this.mimicing){
14259 this.wrap.addClass('x-trigger-wrap-focus');
14260 this.mimicing = true;
14261 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14262 if(this.monitorTab){
14263 this.el.on("keydown", this.checkTab, this);
14270 checkTab : function(e){
14271 if(e.getKey() == e.TAB){
14272 this.triggerBlur();
14277 onBlur : function(){
14282 mimicBlur : function(e, t){
14284 if(!this.wrap.contains(t) && this.validateBlur()){
14285 this.triggerBlur();
14291 triggerBlur : function(){
14292 this.mimicing = false;
14293 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14294 if(this.monitorTab){
14295 this.el.un("keydown", this.checkTab, this);
14297 //this.wrap.removeClass('x-trigger-wrap-focus');
14298 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14302 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14303 validateBlur : function(e, t){
14308 onDisable : function(){
14309 this.inputEl().dom.disabled = true;
14310 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14312 // this.wrap.addClass('x-item-disabled');
14317 onEnable : function(){
14318 this.inputEl().dom.disabled = false;
14319 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14321 // this.el.removeClass('x-item-disabled');
14326 onShow : function(){
14327 var ae = this.getActionEl();
14330 ae.dom.style.display = '';
14331 ae.dom.style.visibility = 'visible';
14337 onHide : function(){
14338 var ae = this.getActionEl();
14339 ae.dom.style.display = 'none';
14343 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14344 * by an implementing function.
14346 * @param {EventObject} e
14348 onTriggerClick : Roo.emptyFn
14356 * @class Roo.bootstrap.form.CardUploader
14357 * @extends Roo.bootstrap.Button
14358 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14359 * @cfg {Number} errorTimeout default 3000
14360 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14361 * @cfg {Array} html The button text.
14365 * Create a new CardUploader
14366 * @param {Object} config The config object
14369 Roo.bootstrap.form.CardUploader = function(config){
14373 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14376 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14384 * When a image is clicked on - and needs to display a slideshow or similar..
14385 * @param {Roo.bootstrap.Card} this
14386 * @param {Object} The image information data
14392 * When a the download link is clicked
14393 * @param {Roo.bootstrap.Card} this
14394 * @param {Object} The image information data contains
14401 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14404 errorTimeout : 3000,
14408 fileCollection : false,
14411 getAutoCreate : function()
14415 cls :'form-group' ,
14420 //cls : 'input-group-addon',
14421 html : this.fieldLabel
14429 value : this.value,
14430 cls : 'd-none form-control'
14435 multiple : 'multiple',
14437 cls : 'd-none roo-card-upload-selector'
14441 cls : 'roo-card-uploader-button-container w-100 mb-2'
14444 cls : 'card-columns roo-card-uploader-container'
14454 getChildContainer : function() /// what children are added to.
14456 return this.containerEl;
14459 getButtonContainer : function() /// what children are added to.
14461 return this.el.select(".roo-card-uploader-button-container").first();
14464 initEvents : function()
14467 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14471 xns: Roo.bootstrap,
14474 container_method : 'getButtonContainer' ,
14475 html : this.html, // fix changable?
14478 'click' : function(btn, e) {
14487 this.urlAPI = (window.createObjectURL && window) ||
14488 (window.URL && URL.revokeObjectURL && URL) ||
14489 (window.webkitURL && webkitURL);
14494 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14496 this.selectorEl.on('change', this.onFileSelected, this);
14499 this.images.forEach(function(img) {
14502 this.images = false;
14504 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14510 onClick : function(e)
14512 e.preventDefault();
14514 this.selectorEl.dom.click();
14518 onFileSelected : function(e)
14520 e.preventDefault();
14522 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14526 Roo.each(this.selectorEl.dom.files, function(file){
14527 this.addFile(file);
14536 addFile : function(file)
14539 if(typeof(file) === 'string'){
14540 throw "Add file by name?"; // should not happen
14544 if(!file || !this.urlAPI){
14554 var url = _this.urlAPI.createObjectURL( file);
14557 id : Roo.bootstrap.form.CardUploader.ID--,
14558 is_uploaded : false,
14562 mimetype : file.type,
14570 * addCard - add an Attachment to the uploader
14571 * @param data - the data about the image to upload
14575 title : "Title of file",
14576 is_uploaded : false,
14577 src : "http://.....",
14578 srcfile : { the File upload object },
14579 mimetype : file.type,
14582 .. any other data...
14588 addCard : function (data)
14590 // hidden input element?
14591 // if the file is not an image...
14592 //then we need to use something other that and header_image
14597 xns : Roo.bootstrap,
14598 xtype : 'CardFooter',
14601 xns : Roo.bootstrap,
14607 xns : Roo.bootstrap,
14609 html : String.format("<small>{0}</small>", data.title),
14610 cls : 'col-10 text-left',
14615 click : function() {
14617 t.fireEvent( "download", t, data );
14623 xns : Roo.bootstrap,
14625 style: 'max-height: 28px; ',
14631 click : function() {
14632 t.removeCard(data.id)
14644 var cn = this.addxtype(
14647 xns : Roo.bootstrap,
14650 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14651 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14652 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14657 initEvents : function() {
14658 Roo.bootstrap.Card.prototype.initEvents.call(this);
14660 this.imgEl = this.el.select('.card-img-top').first();
14662 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14663 this.imgEl.set({ 'pointer' : 'cursor' });
14666 this.getCardFooter().addClass('p-1');
14673 // dont' really need ot update items.
14674 // this.items.push(cn);
14675 this.fileCollection.add(cn);
14677 if (!data.srcfile) {
14678 this.updateInput();
14683 var reader = new FileReader();
14684 reader.addEventListener("load", function() {
14685 data.srcdata = reader.result;
14688 reader.readAsDataURL(data.srcfile);
14693 removeCard : function(id)
14696 var card = this.fileCollection.get(id);
14697 card.data.is_deleted = 1;
14698 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14699 //this.fileCollection.remove(card);
14700 //this.items = this.items.filter(function(e) { return e != card });
14701 // dont' really need ot update items.
14702 card.el.dom.parentNode.removeChild(card.el.dom);
14703 this.updateInput();
14709 this.fileCollection.each(function(card) {
14710 if (card.el.dom && card.el.dom.parentNode) {
14711 card.el.dom.parentNode.removeChild(card.el.dom);
14714 this.fileCollection.clear();
14715 this.updateInput();
14718 updateInput : function()
14721 this.fileCollection.each(function(e) {
14725 this.inputEl().dom.value = JSON.stringify(data);
14735 Roo.bootstrap.form.CardUploader.ID = -1;/*
14737 * Ext JS Library 1.1.1
14738 * Copyright(c) 2006-2007, Ext JS, LLC.
14740 * Originally Released Under LGPL - original licence link has changed is not relivant.
14743 * <script type="text/javascript">
14748 * @class Roo.data.SortTypes
14750 * Defines the default sorting (casting?) comparison functions used when sorting data.
14752 Roo.data.SortTypes = {
14754 * Default sort that does nothing
14755 * @param {Mixed} s The value being converted
14756 * @return {Mixed} The comparison value
14758 none : function(s){
14763 * The regular expression used to strip tags
14767 stripTagsRE : /<\/?[^>]+>/gi,
14770 * Strips all HTML tags to sort on text only
14771 * @param {Mixed} s The value being converted
14772 * @return {String} The comparison value
14774 asText : function(s){
14775 return String(s).replace(this.stripTagsRE, "");
14779 * Strips all HTML tags to sort on text only - Case insensitive
14780 * @param {Mixed} s The value being converted
14781 * @return {String} The comparison value
14783 asUCText : function(s){
14784 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14788 * Case insensitive string
14789 * @param {Mixed} s The value being converted
14790 * @return {String} The comparison value
14792 asUCString : function(s) {
14793 return String(s).toUpperCase();
14798 * @param {Mixed} s The value being converted
14799 * @return {Number} The comparison value
14801 asDate : function(s) {
14805 if(s instanceof Date){
14806 return s.getTime();
14808 return Date.parse(String(s));
14813 * @param {Mixed} s The value being converted
14814 * @return {Float} The comparison value
14816 asFloat : function(s) {
14817 var val = parseFloat(String(s).replace(/,/g, ""));
14826 * @param {Mixed} s The value being converted
14827 * @return {Number} The comparison value
14829 asInt : function(s) {
14830 var val = parseInt(String(s).replace(/,/g, ""));
14838 * Ext JS Library 1.1.1
14839 * Copyright(c) 2006-2007, Ext JS, LLC.
14841 * Originally Released Under LGPL - original licence link has changed is not relivant.
14844 * <script type="text/javascript">
14848 * @class Roo.data.Record
14849 * Instances of this class encapsulate both record <em>definition</em> information, and record
14850 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14851 * to access Records cached in an {@link Roo.data.Store} object.<br>
14853 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14854 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14857 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14859 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14860 * {@link #create}. The parameters are the same.
14861 * @param {Array} data An associative Array of data values keyed by the field name.
14862 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14863 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14864 * not specified an integer id is generated.
14866 Roo.data.Record = function(data, id){
14867 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14872 * Generate a constructor for a specific record layout.
14873 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14874 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14875 * Each field definition object may contain the following properties: <ul>
14876 * <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,
14877 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14878 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14879 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14880 * is being used, then this is a string containing the javascript expression to reference the data relative to
14881 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14882 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14883 * this may be omitted.</p></li>
14884 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14885 * <ul><li>auto (Default, implies no conversion)</li>
14890 * <li>date</li></ul></p></li>
14891 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14892 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14893 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14894 * by the Reader into an object that will be stored in the Record. It is passed the
14895 * following parameters:<ul>
14896 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14898 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14900 * <br>usage:<br><pre><code>
14901 var TopicRecord = Roo.data.Record.create(
14902 {name: 'title', mapping: 'topic_title'},
14903 {name: 'author', mapping: 'username'},
14904 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14905 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14906 {name: 'lastPoster', mapping: 'user2'},
14907 {name: 'excerpt', mapping: 'post_text'}
14910 var myNewRecord = new TopicRecord({
14911 title: 'Do my job please',
14914 lastPost: new Date(),
14915 lastPoster: 'Animal',
14916 excerpt: 'No way dude!'
14918 myStore.add(myNewRecord);
14923 Roo.data.Record.create = function(o){
14924 var f = function(){
14925 f.superclass.constructor.apply(this, arguments);
14927 Roo.extend(f, Roo.data.Record);
14928 var p = f.prototype;
14929 p.fields = new Roo.util.MixedCollection(false, function(field){
14932 for(var i = 0, len = o.length; i < len; i++){
14933 p.fields.add(new Roo.data.Field(o[i]));
14935 f.getField = function(name){
14936 return p.fields.get(name);
14941 Roo.data.Record.AUTO_ID = 1000;
14942 Roo.data.Record.EDIT = 'edit';
14943 Roo.data.Record.REJECT = 'reject';
14944 Roo.data.Record.COMMIT = 'commit';
14946 Roo.data.Record.prototype = {
14948 * Readonly flag - true if this record has been modified.
14957 join : function(store){
14958 this.store = store;
14962 * Set the named field to the specified value.
14963 * @param {String} name The name of the field to set.
14964 * @param {Object} value The value to set the field to.
14966 set : function(name, value){
14967 if(this.data[name] == value){
14971 if(!this.modified){
14972 this.modified = {};
14974 if(typeof this.modified[name] == 'undefined'){
14975 this.modified[name] = this.data[name];
14977 this.data[name] = value;
14978 if(!this.editing && this.store){
14979 this.store.afterEdit(this);
14984 * Get the value of the named field.
14985 * @param {String} name The name of the field to get the value of.
14986 * @return {Object} The value of the field.
14988 get : function(name){
14989 return this.data[name];
14993 beginEdit : function(){
14994 this.editing = true;
14995 this.modified = {};
14999 cancelEdit : function(){
15000 this.editing = false;
15001 delete this.modified;
15005 endEdit : function(){
15006 this.editing = false;
15007 if(this.dirty && this.store){
15008 this.store.afterEdit(this);
15013 * Usually called by the {@link Roo.data.Store} which owns the Record.
15014 * Rejects all changes made to the Record since either creation, or the last commit operation.
15015 * Modified fields are reverted to their original values.
15017 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15018 * of reject operations.
15020 reject : function(){
15021 var m = this.modified;
15023 if(typeof m[n] != "function"){
15024 this.data[n] = m[n];
15027 this.dirty = false;
15028 delete this.modified;
15029 this.editing = false;
15031 this.store.afterReject(this);
15036 * Usually called by the {@link Roo.data.Store} which owns the Record.
15037 * Commits all changes made to the Record since either creation, or the last commit operation.
15039 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15040 * of commit operations.
15042 commit : function(){
15043 this.dirty = false;
15044 delete this.modified;
15045 this.editing = false;
15047 this.store.afterCommit(this);
15052 hasError : function(){
15053 return this.error != null;
15057 clearError : function(){
15062 * Creates a copy of this record.
15063 * @param {String} id (optional) A new record id if you don't want to use this record's id
15066 copy : function(newId) {
15067 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15071 * Ext JS Library 1.1.1
15072 * Copyright(c) 2006-2007, Ext JS, LLC.
15074 * Originally Released Under LGPL - original licence link has changed is not relivant.
15077 * <script type="text/javascript">
15083 * @class Roo.data.Store
15084 * @extends Roo.util.Observable
15085 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15086 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15088 * 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
15089 * has no knowledge of the format of the data returned by the Proxy.<br>
15091 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15092 * instances from the data object. These records are cached and made available through accessor functions.
15094 * Creates a new Store.
15095 * @param {Object} config A config object containing the objects needed for the Store to access data,
15096 * and read the data into Records.
15098 Roo.data.Store = function(config){
15099 this.data = new Roo.util.MixedCollection(false);
15100 this.data.getKey = function(o){
15103 this.baseParams = {};
15105 this.paramNames = {
15110 "multisort" : "_multisort"
15113 if(config && config.data){
15114 this.inlineData = config.data;
15115 delete config.data;
15118 Roo.apply(this, config);
15120 if(this.reader){ // reader passed
15121 this.reader = Roo.factory(this.reader, Roo.data);
15122 this.reader.xmodule = this.xmodule || false;
15123 if(!this.recordType){
15124 this.recordType = this.reader.recordType;
15126 if(this.reader.onMetaChange){
15127 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15131 if(this.recordType){
15132 this.fields = this.recordType.prototype.fields;
15134 this.modified = [];
15138 * @event datachanged
15139 * Fires when the data cache has changed, and a widget which is using this Store
15140 * as a Record cache should refresh its view.
15141 * @param {Store} this
15143 datachanged : true,
15145 * @event metachange
15146 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15147 * @param {Store} this
15148 * @param {Object} meta The JSON metadata
15153 * Fires when Records have been added to the Store
15154 * @param {Store} this
15155 * @param {Roo.data.Record[]} records The array of Records added
15156 * @param {Number} index The index at which the record(s) were added
15161 * Fires when a Record has been removed from the Store
15162 * @param {Store} this
15163 * @param {Roo.data.Record} record The Record that was removed
15164 * @param {Number} index The index at which the record was removed
15169 * Fires when a Record has been updated
15170 * @param {Store} this
15171 * @param {Roo.data.Record} record The Record that was updated
15172 * @param {String} operation The update operation being performed. Value may be one of:
15174 Roo.data.Record.EDIT
15175 Roo.data.Record.REJECT
15176 Roo.data.Record.COMMIT
15182 * Fires when the data cache has been cleared.
15183 * @param {Store} this
15187 * @event beforeload
15188 * Fires before a request is made for a new data object. If the beforeload handler returns false
15189 * the load action will be canceled.
15190 * @param {Store} this
15191 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15195 * @event beforeloadadd
15196 * Fires after a new set of Records has been loaded.
15197 * @param {Store} this
15198 * @param {Roo.data.Record[]} records The Records that were loaded
15199 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15201 beforeloadadd : true,
15204 * Fires after a new set of Records has been loaded, before they are added to the store.
15205 * @param {Store} this
15206 * @param {Roo.data.Record[]} records The Records that were loaded
15207 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15208 * @params {Object} return from reader
15212 * @event loadexception
15213 * Fires if an exception occurs in the Proxy during loading.
15214 * Called with the signature of the Proxy's "loadexception" event.
15215 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15218 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15219 * @param {Object} load options
15220 * @param {Object} jsonData from your request (normally this contains the Exception)
15222 loadexception : true
15226 this.proxy = Roo.factory(this.proxy, Roo.data);
15227 this.proxy.xmodule = this.xmodule || false;
15228 this.relayEvents(this.proxy, ["loadexception"]);
15230 this.sortToggle = {};
15231 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15233 Roo.data.Store.superclass.constructor.call(this);
15235 if(this.inlineData){
15236 this.loadData(this.inlineData);
15237 delete this.inlineData;
15241 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15243 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15244 * without a remote query - used by combo/forms at present.
15248 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15251 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15254 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15255 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15258 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15259 * on any HTTP request
15262 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15265 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15269 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15270 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15272 remoteSort : false,
15275 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15276 * loaded or when a record is removed. (defaults to false).
15278 pruneModifiedRecords : false,
15281 lastOptions : null,
15284 * Add Records to the Store and fires the add event.
15285 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15287 add : function(records){
15288 records = [].concat(records);
15289 for(var i = 0, len = records.length; i < len; i++){
15290 records[i].join(this);
15292 var index = this.data.length;
15293 this.data.addAll(records);
15294 this.fireEvent("add", this, records, index);
15298 * Remove a Record from the Store and fires the remove event.
15299 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15301 remove : function(record){
15302 var index = this.data.indexOf(record);
15303 this.data.removeAt(index);
15305 if(this.pruneModifiedRecords){
15306 this.modified.remove(record);
15308 this.fireEvent("remove", this, record, index);
15312 * Remove all Records from the Store and fires the clear event.
15314 removeAll : function(){
15316 if(this.pruneModifiedRecords){
15317 this.modified = [];
15319 this.fireEvent("clear", this);
15323 * Inserts Records to the Store at the given index and fires the add event.
15324 * @param {Number} index The start index at which to insert the passed Records.
15325 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15327 insert : function(index, records){
15328 records = [].concat(records);
15329 for(var i = 0, len = records.length; i < len; i++){
15330 this.data.insert(index, records[i]);
15331 records[i].join(this);
15333 this.fireEvent("add", this, records, index);
15337 * Get the index within the cache of the passed Record.
15338 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15339 * @return {Number} The index of the passed Record. Returns -1 if not found.
15341 indexOf : function(record){
15342 return this.data.indexOf(record);
15346 * Get the index within the cache of the Record with the passed id.
15347 * @param {String} id The id of the Record to find.
15348 * @return {Number} The index of the Record. Returns -1 if not found.
15350 indexOfId : function(id){
15351 return this.data.indexOfKey(id);
15355 * Get the Record with the specified id.
15356 * @param {String} id The id of the Record to find.
15357 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15359 getById : function(id){
15360 return this.data.key(id);
15364 * Get the Record at the specified index.
15365 * @param {Number} index The index of the Record to find.
15366 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15368 getAt : function(index){
15369 return this.data.itemAt(index);
15373 * Returns a range of Records between specified indices.
15374 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15375 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15376 * @return {Roo.data.Record[]} An array of Records
15378 getRange : function(start, end){
15379 return this.data.getRange(start, end);
15383 storeOptions : function(o){
15384 o = Roo.apply({}, o);
15387 this.lastOptions = o;
15391 * Loads the Record cache from the configured Proxy using the configured Reader.
15393 * If using remote paging, then the first load call must specify the <em>start</em>
15394 * and <em>limit</em> properties in the options.params property to establish the initial
15395 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15397 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15398 * and this call will return before the new data has been loaded. Perform any post-processing
15399 * in a callback function, or in a "load" event handler.</strong>
15401 * @param {Object} options An object containing properties which control loading options:<ul>
15402 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15403 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15406 data : data, // array of key=>value data like JsonReader
15407 total : data.length,
15413 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15414 * passed the following arguments:<ul>
15415 * <li>r : Roo.data.Record[]</li>
15416 * <li>options: Options object from the load call</li>
15417 * <li>success: Boolean success indicator</li></ul></li>
15418 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15419 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15422 load : function(options){
15423 options = options || {};
15424 if(this.fireEvent("beforeload", this, options) !== false){
15425 this.storeOptions(options);
15426 var p = Roo.apply(options.params || {}, this.baseParams);
15427 // if meta was not loaded from remote source.. try requesting it.
15428 if (!this.reader.metaFromRemote) {
15429 p._requestMeta = 1;
15431 if(this.sortInfo && this.remoteSort){
15432 var pn = this.paramNames;
15433 p[pn["sort"]] = this.sortInfo.field;
15434 p[pn["dir"]] = this.sortInfo.direction;
15436 if (this.multiSort) {
15437 var pn = this.paramNames;
15438 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15441 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15446 * Reloads the Record cache from the configured Proxy using the configured Reader and
15447 * the options from the last load operation performed.
15448 * @param {Object} options (optional) An object containing properties which may override the options
15449 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15450 * the most recently used options are reused).
15452 reload : function(options){
15453 this.load(Roo.applyIf(options||{}, this.lastOptions));
15457 // Called as a callback by the Reader during a load operation.
15458 loadRecords : function(o, options, success){
15461 if(success !== false){
15462 this.fireEvent("load", this, [], options, o);
15464 if(options.callback){
15465 options.callback.call(options.scope || this, [], options, false);
15469 // if data returned failure - throw an exception.
15470 if (o.success === false) {
15471 // show a message if no listener is registered.
15472 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15473 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15475 // loadmask wil be hooked into this..
15476 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15479 var r = o.records, t = o.totalRecords || r.length;
15481 this.fireEvent("beforeloadadd", this, r, options, o);
15483 if(!options || options.add !== true){
15484 if(this.pruneModifiedRecords){
15485 this.modified = [];
15487 for(var i = 0, len = r.length; i < len; i++){
15491 this.data = this.snapshot;
15492 delete this.snapshot;
15495 this.data.addAll(r);
15496 this.totalLength = t;
15498 this.fireEvent("datachanged", this);
15500 this.totalLength = Math.max(t, this.data.length+r.length);
15504 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15506 var e = new Roo.data.Record({});
15508 e.set(this.parent.displayField, this.parent.emptyTitle);
15509 e.set(this.parent.valueField, '');
15514 this.fireEvent("load", this, r, options, o);
15515 if(options.callback){
15516 options.callback.call(options.scope || this, r, options, true);
15522 * Loads data from a passed data block. A Reader which understands the format of the data
15523 * must have been configured in the constructor.
15524 * @param {Object} data The data block from which to read the Records. The format of the data expected
15525 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15526 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15528 loadData : function(o, append){
15529 var r = this.reader.readRecords(o);
15530 this.loadRecords(r, {add: append}, true);
15534 * using 'cn' the nested child reader read the child array into it's child stores.
15535 * @param {Object} rec The record with a 'children array
15537 loadDataFromChildren : function(rec)
15539 this.loadData(this.reader.toLoadData(rec));
15544 * Gets the number of cached records.
15546 * <em>If using paging, this may not be the total size of the dataset. If the data object
15547 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15548 * the data set size</em>
15550 getCount : function(){
15551 return this.data.length || 0;
15555 * Gets the total number of records in the dataset as returned by the server.
15557 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15558 * the dataset size</em>
15560 getTotalCount : function(){
15561 return this.totalLength || 0;
15565 * Returns the sort state of the Store as an object with two properties:
15567 field {String} The name of the field by which the Records are sorted
15568 direction {String} The sort order, "ASC" or "DESC"
15571 getSortState : function(){
15572 return this.sortInfo;
15576 applySort : function(){
15577 if(this.sortInfo && !this.remoteSort){
15578 var s = this.sortInfo, f = s.field;
15579 var st = this.fields.get(f).sortType;
15580 var fn = function(r1, r2){
15581 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15582 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15584 this.data.sort(s.direction, fn);
15585 if(this.snapshot && this.snapshot != this.data){
15586 this.snapshot.sort(s.direction, fn);
15592 * Sets the default sort column and order to be used by the next load operation.
15593 * @param {String} fieldName The name of the field to sort by.
15594 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15596 setDefaultSort : function(field, dir){
15597 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15601 * Sort the Records.
15602 * If remote sorting is used, the sort is performed on the server, and the cache is
15603 * reloaded. If local sorting is used, the cache is sorted internally.
15604 * @param {String} fieldName The name of the field to sort by.
15605 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15607 sort : function(fieldName, dir){
15608 var f = this.fields.get(fieldName);
15610 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15612 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15613 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15618 this.sortToggle[f.name] = dir;
15619 this.sortInfo = {field: f.name, direction: dir};
15620 if(!this.remoteSort){
15622 this.fireEvent("datachanged", this);
15624 this.load(this.lastOptions);
15629 * Calls the specified function for each of the Records in the cache.
15630 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15631 * Returning <em>false</em> aborts and exits the iteration.
15632 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15634 each : function(fn, scope){
15635 this.data.each(fn, scope);
15639 * Gets all records modified since the last commit. Modified records are persisted across load operations
15640 * (e.g., during paging).
15641 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15643 getModifiedRecords : function(){
15644 return this.modified;
15648 createFilterFn : function(property, value, anyMatch){
15649 if(!value.exec){ // not a regex
15650 value = String(value);
15651 if(value.length == 0){
15654 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15656 return function(r){
15657 return value.test(r.data[property]);
15662 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15663 * @param {String} property A field on your records
15664 * @param {Number} start The record index to start at (defaults to 0)
15665 * @param {Number} end The last record index to include (defaults to length - 1)
15666 * @return {Number} The sum
15668 sum : function(property, start, end){
15669 var rs = this.data.items, v = 0;
15670 start = start || 0;
15671 end = (end || end === 0) ? end : rs.length-1;
15673 for(var i = start; i <= end; i++){
15674 v += (rs[i].data[property] || 0);
15680 * Filter the records by a specified property.
15681 * @param {String} field A field on your records
15682 * @param {String/RegExp} value Either a string that the field
15683 * should start with or a RegExp to test against the field
15684 * @param {Boolean} anyMatch True to match any part not just the beginning
15686 filter : function(property, value, anyMatch){
15687 var fn = this.createFilterFn(property, value, anyMatch);
15688 return fn ? this.filterBy(fn) : this.clearFilter();
15692 * Filter by a function. The specified function will be called with each
15693 * record in this data source. If the function returns true the record is included,
15694 * otherwise it is filtered.
15695 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15696 * @param {Object} scope (optional) The scope of the function (defaults to this)
15698 filterBy : function(fn, scope){
15699 this.snapshot = this.snapshot || this.data;
15700 this.data = this.queryBy(fn, scope||this);
15701 this.fireEvent("datachanged", this);
15705 * Query the records by a specified property.
15706 * @param {String} field A field on your records
15707 * @param {String/RegExp} value Either a string that the field
15708 * should start with or a RegExp to test against the field
15709 * @param {Boolean} anyMatch True to match any part not just the beginning
15710 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15712 query : function(property, value, anyMatch){
15713 var fn = this.createFilterFn(property, value, anyMatch);
15714 return fn ? this.queryBy(fn) : this.data.clone();
15718 * Query by a function. The specified function will be called with each
15719 * record in this data source. If the function returns true the record is included
15721 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15722 * @param {Object} scope (optional) The scope of the function (defaults to this)
15723 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15725 queryBy : function(fn, scope){
15726 var data = this.snapshot || this.data;
15727 return data.filterBy(fn, scope||this);
15731 * Collects unique values for a particular dataIndex from this store.
15732 * @param {String} dataIndex The property to collect
15733 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15734 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15735 * @return {Array} An array of the unique values
15737 collect : function(dataIndex, allowNull, bypassFilter){
15738 var d = (bypassFilter === true && this.snapshot) ?
15739 this.snapshot.items : this.data.items;
15740 var v, sv, r = [], l = {};
15741 for(var i = 0, len = d.length; i < len; i++){
15742 v = d[i].data[dataIndex];
15744 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15753 * Revert to a view of the Record cache with no filtering applied.
15754 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15756 clearFilter : function(suppressEvent){
15757 if(this.snapshot && this.snapshot != this.data){
15758 this.data = this.snapshot;
15759 delete this.snapshot;
15760 if(suppressEvent !== true){
15761 this.fireEvent("datachanged", this);
15767 afterEdit : function(record){
15768 if(this.modified.indexOf(record) == -1){
15769 this.modified.push(record);
15771 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15775 afterReject : function(record){
15776 this.modified.remove(record);
15777 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15781 afterCommit : function(record){
15782 this.modified.remove(record);
15783 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15787 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15788 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15790 commitChanges : function(){
15791 var m = this.modified.slice(0);
15792 this.modified = [];
15793 for(var i = 0, len = m.length; i < len; i++){
15799 * Cancel outstanding changes on all changed records.
15801 rejectChanges : function(){
15802 var m = this.modified.slice(0);
15803 this.modified = [];
15804 for(var i = 0, len = m.length; i < len; i++){
15809 onMetaChange : function(meta, rtype, o){
15810 this.recordType = rtype;
15811 this.fields = rtype.prototype.fields;
15812 delete this.snapshot;
15813 this.sortInfo = meta.sortInfo || this.sortInfo;
15814 this.modified = [];
15815 this.fireEvent('metachange', this, this.reader.meta);
15818 moveIndex : function(data, type)
15820 var index = this.indexOf(data);
15822 var newIndex = index + type;
15826 this.insert(newIndex, data);
15831 * Ext JS Library 1.1.1
15832 * Copyright(c) 2006-2007, Ext JS, LLC.
15834 * Originally Released Under LGPL - original licence link has changed is not relivant.
15837 * <script type="text/javascript">
15841 * @class Roo.data.SimpleStore
15842 * @extends Roo.data.Store
15843 * Small helper class to make creating Stores from Array data easier.
15844 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15845 * @cfg {Array} fields An array of field definition objects, or field name strings.
15846 * @cfg {Object} an existing reader (eg. copied from another store)
15847 * @cfg {Array} data The multi-dimensional array of data
15848 * @cfg {Roo.data.DataProxy} proxy [not-required]
15849 * @cfg {Roo.data.Reader} reader [not-required]
15851 * @param {Object} config
15853 Roo.data.SimpleStore = function(config)
15855 Roo.data.SimpleStore.superclass.constructor.call(this, {
15857 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15860 Roo.data.Record.create(config.fields)
15862 proxy : new Roo.data.MemoryProxy(config.data)
15866 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15868 * Ext JS Library 1.1.1
15869 * Copyright(c) 2006-2007, Ext JS, LLC.
15871 * Originally Released Under LGPL - original licence link has changed is not relivant.
15874 * <script type="text/javascript">
15879 * @extends Roo.data.Store
15880 * @class Roo.data.JsonStore
15881 * Small helper class to make creating Stores for JSON data easier. <br/>
15883 var store = new Roo.data.JsonStore({
15884 url: 'get-images.php',
15886 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15889 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15890 * JsonReader and HttpProxy (unless inline data is provided).</b>
15891 * @cfg {Array} fields An array of field definition objects, or field name strings.
15893 * @param {Object} config
15895 Roo.data.JsonStore = function(c){
15896 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15897 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15898 reader: new Roo.data.JsonReader(c, c.fields)
15901 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15903 * Ext JS Library 1.1.1
15904 * Copyright(c) 2006-2007, Ext JS, LLC.
15906 * Originally Released Under LGPL - original licence link has changed is not relivant.
15909 * <script type="text/javascript">
15913 Roo.data.Field = function(config){
15914 if(typeof config == "string"){
15915 config = {name: config};
15917 Roo.apply(this, config);
15920 this.type = "auto";
15923 var st = Roo.data.SortTypes;
15924 // named sortTypes are supported, here we look them up
15925 if(typeof this.sortType == "string"){
15926 this.sortType = st[this.sortType];
15929 // set default sortType for strings and dates
15930 if(!this.sortType){
15933 this.sortType = st.asUCString;
15936 this.sortType = st.asDate;
15939 this.sortType = st.none;
15944 var stripRe = /[\$,%]/g;
15946 // prebuilt conversion function for this field, instead of
15947 // switching every time we're reading a value
15949 var cv, dateFormat = this.dateFormat;
15954 cv = function(v){ return v; };
15957 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15961 return v !== undefined && v !== null && v !== '' ?
15962 parseInt(String(v).replace(stripRe, ""), 10) : '';
15967 return v !== undefined && v !== null && v !== '' ?
15968 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15973 cv = function(v){ return v === true || v === "true" || v == 1; };
15980 if(v instanceof Date){
15984 if(dateFormat == "timestamp"){
15985 return new Date(v*1000);
15987 return Date.parseDate(v, dateFormat);
15989 var parsed = Date.parse(v);
15990 return parsed ? new Date(parsed) : null;
15999 Roo.data.Field.prototype = {
16007 * Ext JS Library 1.1.1
16008 * Copyright(c) 2006-2007, Ext JS, LLC.
16010 * Originally Released Under LGPL - original licence link has changed is not relivant.
16013 * <script type="text/javascript">
16016 // Base class for reading structured data from a data source. This class is intended to be
16017 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16020 * @class Roo.data.DataReader
16022 * Base class for reading structured data from a data source. This class is intended to be
16023 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16026 Roo.data.DataReader = function(meta, recordType){
16030 this.recordType = recordType instanceof Array ?
16031 Roo.data.Record.create(recordType) : recordType;
16034 Roo.data.DataReader.prototype = {
16037 readerType : 'Data',
16039 * Create an empty record
16040 * @param {Object} data (optional) - overlay some values
16041 * @return {Roo.data.Record} record created.
16043 newRow : function(d) {
16045 this.recordType.prototype.fields.each(function(c) {
16047 case 'int' : da[c.name] = 0; break;
16048 case 'date' : da[c.name] = new Date(); break;
16049 case 'float' : da[c.name] = 0.0; break;
16050 case 'boolean' : da[c.name] = false; break;
16051 default : da[c.name] = ""; break;
16055 return new this.recordType(Roo.apply(da, d));
16061 * Ext JS Library 1.1.1
16062 * Copyright(c) 2006-2007, Ext JS, LLC.
16064 * Originally Released Under LGPL - original licence link has changed is not relivant.
16067 * <script type="text/javascript">
16071 * @class Roo.data.DataProxy
16072 * @extends Roo.util.Observable
16074 * This class is an abstract base class for implementations which provide retrieval of
16075 * unformatted data objects.<br>
16077 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16078 * (of the appropriate type which knows how to parse the data object) to provide a block of
16079 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16081 * Custom implementations must implement the load method as described in
16082 * {@link Roo.data.HttpProxy#load}.
16084 Roo.data.DataProxy = function(){
16087 * @event beforeload
16088 * Fires before a network request is made to retrieve a data object.
16089 * @param {Object} This DataProxy object.
16090 * @param {Object} params The params parameter to the load function.
16095 * Fires before the load method's callback is called.
16096 * @param {Object} This DataProxy object.
16097 * @param {Object} o The data object.
16098 * @param {Object} arg The callback argument object passed to the load function.
16102 * @event loadexception
16103 * Fires if an Exception occurs during data retrieval.
16104 * @param {Object} This DataProxy object.
16105 * @param {Object} o The data object.
16106 * @param {Object} arg The callback argument object passed to the load function.
16107 * @param {Object} e The Exception.
16109 loadexception : true
16111 Roo.data.DataProxy.superclass.constructor.call(this);
16114 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16117 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16121 * Ext JS Library 1.1.1
16122 * Copyright(c) 2006-2007, Ext JS, LLC.
16124 * Originally Released Under LGPL - original licence link has changed is not relivant.
16127 * <script type="text/javascript">
16130 * @class Roo.data.MemoryProxy
16131 * @extends Roo.data.DataProxy
16132 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16133 * to the Reader when its load method is called.
16135 * @param {Object} config A config object containing the objects needed for the Store to access data,
16137 Roo.data.MemoryProxy = function(config){
16139 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16140 data = config.data;
16142 Roo.data.MemoryProxy.superclass.constructor.call(this);
16146 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16149 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16152 * Load data from the requested source (in this case an in-memory
16153 * data object passed to the constructor), read the data object into
16154 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16155 * process that block using the passed callback.
16156 * @param {Object} params This parameter is not used by the MemoryProxy class.
16157 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16158 * object into a block of Roo.data.Records.
16159 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16160 * The function must be passed <ul>
16161 * <li>The Record block object</li>
16162 * <li>The "arg" argument from the load function</li>
16163 * <li>A boolean success indicator</li>
16165 * @param {Object} scope The scope in which to call the callback
16166 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16168 load : function(params, reader, callback, scope, arg){
16169 params = params || {};
16172 result = reader.readRecords(params.data ? params.data :this.data);
16174 this.fireEvent("loadexception", this, arg, null, e);
16175 callback.call(scope, null, arg, false);
16178 callback.call(scope, result, arg, true);
16182 update : function(params, records){
16187 * Ext JS Library 1.1.1
16188 * Copyright(c) 2006-2007, Ext JS, LLC.
16190 * Originally Released Under LGPL - original licence link has changed is not relivant.
16193 * <script type="text/javascript">
16196 * @class Roo.data.HttpProxy
16197 * @extends Roo.data.DataProxy
16198 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16199 * configured to reference a certain URL.<br><br>
16201 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16202 * from which the running page was served.<br><br>
16204 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16206 * Be aware that to enable the browser to parse an XML document, the server must set
16207 * the Content-Type header in the HTTP response to "text/xml".
16209 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16210 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16211 * will be used to make the request.
16213 Roo.data.HttpProxy = function(conn){
16214 Roo.data.HttpProxy.superclass.constructor.call(this);
16215 // is conn a conn config or a real conn?
16217 this.useAjax = !conn || !conn.events;
16221 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16222 // thse are take from connection...
16225 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16228 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16229 * extra parameters to each request made by this object. (defaults to undefined)
16232 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16233 * to each request made by this object. (defaults to undefined)
16236 * @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)
16239 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16242 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16248 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16252 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16253 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16254 * a finer-grained basis than the DataProxy events.
16256 getConnection : function(){
16257 return this.useAjax ? Roo.Ajax : this.conn;
16261 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16262 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16263 * process that block using the passed callback.
16264 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16265 * for the request to the remote server.
16266 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16267 * object into a block of Roo.data.Records.
16268 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16269 * The function must be passed <ul>
16270 * <li>The Record block object</li>
16271 * <li>The "arg" argument from the load function</li>
16272 * <li>A boolean success indicator</li>
16274 * @param {Object} scope The scope in which to call the callback
16275 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16277 load : function(params, reader, callback, scope, arg){
16278 if(this.fireEvent("beforeload", this, params) !== false){
16280 params : params || {},
16282 callback : callback,
16287 callback : this.loadResponse,
16291 Roo.applyIf(o, this.conn);
16292 if(this.activeRequest){
16293 Roo.Ajax.abort(this.activeRequest);
16295 this.activeRequest = Roo.Ajax.request(o);
16297 this.conn.request(o);
16300 callback.call(scope||this, null, arg, false);
16305 loadResponse : function(o, success, response){
16306 delete this.activeRequest;
16308 this.fireEvent("loadexception", this, o, response);
16309 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16314 result = o.reader.read(response);
16317 o.raw = { errorMsg : response.responseText };
16318 this.fireEvent("loadexception", this, o, response, e);
16319 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16323 this.fireEvent("load", this, o, o.request.arg);
16324 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16328 update : function(dataSet){
16333 updateResponse : function(dataSet){
16338 * Ext JS Library 1.1.1
16339 * Copyright(c) 2006-2007, Ext JS, LLC.
16341 * Originally Released Under LGPL - original licence link has changed is not relivant.
16344 * <script type="text/javascript">
16348 * @class Roo.data.ScriptTagProxy
16349 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16350 * other than the originating domain of the running page.<br><br>
16352 * <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
16353 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16355 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16356 * source code that is used as the source inside a <script> tag.<br><br>
16358 * In order for the browser to process the returned data, the server must wrap the data object
16359 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16360 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16361 * depending on whether the callback name was passed:
16364 boolean scriptTag = false;
16365 String cb = request.getParameter("callback");
16368 response.setContentType("text/javascript");
16370 response.setContentType("application/x-json");
16372 Writer out = response.getWriter();
16374 out.write(cb + "(");
16376 out.print(dataBlock.toJsonString());
16383 * @param {Object} config A configuration object.
16385 Roo.data.ScriptTagProxy = function(config){
16386 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16387 Roo.apply(this, config);
16388 this.head = document.getElementsByTagName("head")[0];
16391 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16393 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16395 * @cfg {String} url The URL from which to request the data object.
16398 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16402 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16403 * the server the name of the callback function set up by the load call to process the returned data object.
16404 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16405 * javascript output which calls this named function passing the data object as its only parameter.
16407 callbackParam : "callback",
16409 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16410 * name to the request.
16415 * Load data from the configured URL, read the data object into
16416 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16417 * process that block using the passed callback.
16418 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16419 * for the request to the remote server.
16420 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16421 * object into a block of Roo.data.Records.
16422 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16423 * The function must be passed <ul>
16424 * <li>The Record block object</li>
16425 * <li>The "arg" argument from the load function</li>
16426 * <li>A boolean success indicator</li>
16428 * @param {Object} scope The scope in which to call the callback
16429 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16431 load : function(params, reader, callback, scope, arg){
16432 if(this.fireEvent("beforeload", this, params) !== false){
16434 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16436 var url = this.url;
16437 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16439 url += "&_dc=" + (new Date().getTime());
16441 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16444 cb : "stcCallback"+transId,
16445 scriptId : "stcScript"+transId,
16449 callback : callback,
16455 window[trans.cb] = function(o){
16456 conn.handleResponse(o, trans);
16459 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16461 if(this.autoAbort !== false){
16465 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16467 var script = document.createElement("script");
16468 script.setAttribute("src", url);
16469 script.setAttribute("type", "text/javascript");
16470 script.setAttribute("id", trans.scriptId);
16471 this.head.appendChild(script);
16473 this.trans = trans;
16475 callback.call(scope||this, null, arg, false);
16480 isLoading : function(){
16481 return this.trans ? true : false;
16485 * Abort the current server request.
16487 abort : function(){
16488 if(this.isLoading()){
16489 this.destroyTrans(this.trans);
16494 destroyTrans : function(trans, isLoaded){
16495 this.head.removeChild(document.getElementById(trans.scriptId));
16496 clearTimeout(trans.timeoutId);
16498 window[trans.cb] = undefined;
16500 delete window[trans.cb];
16503 // if hasn't been loaded, wait for load to remove it to prevent script error
16504 window[trans.cb] = function(){
16505 window[trans.cb] = undefined;
16507 delete window[trans.cb];
16514 handleResponse : function(o, trans){
16515 this.trans = false;
16516 this.destroyTrans(trans, true);
16519 result = trans.reader.readRecords(o);
16521 this.fireEvent("loadexception", this, o, trans.arg, e);
16522 trans.callback.call(trans.scope||window, null, trans.arg, false);
16525 this.fireEvent("load", this, o, trans.arg);
16526 trans.callback.call(trans.scope||window, result, trans.arg, true);
16530 handleFailure : function(trans){
16531 this.trans = false;
16532 this.destroyTrans(trans, false);
16533 this.fireEvent("loadexception", this, null, trans.arg);
16534 trans.callback.call(trans.scope||window, null, trans.arg, false);
16538 * Ext JS Library 1.1.1
16539 * Copyright(c) 2006-2007, Ext JS, LLC.
16541 * Originally Released Under LGPL - original licence link has changed is not relivant.
16544 * <script type="text/javascript">
16548 * @class Roo.data.JsonReader
16549 * @extends Roo.data.DataReader
16550 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16551 * based on mappings in a provided Roo.data.Record constructor.
16553 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16554 * in the reply previously.
16559 var RecordDef = Roo.data.Record.create([
16560 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16561 {name: 'occupation'} // This field will use "occupation" as the mapping.
16563 var myReader = new Roo.data.JsonReader({
16564 totalProperty: "results", // The property which contains the total dataset size (optional)
16565 root: "rows", // The property which contains an Array of row objects
16566 id: "id" // The property within each row object that provides an ID for the record (optional)
16570 * This would consume a JSON file like this:
16572 { 'results': 2, 'rows': [
16573 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16574 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16577 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16578 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16579 * paged from the remote server.
16580 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16581 * @cfg {String} root name of the property which contains the Array of row objects.
16582 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16583 * @cfg {Array} fields Array of field definition objects
16585 * Create a new JsonReader
16586 * @param {Object} meta Metadata configuration options
16587 * @param {Object} recordType Either an Array of field definition objects,
16588 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16590 Roo.data.JsonReader = function(meta, recordType){
16593 // set some defaults:
16594 Roo.applyIf(meta, {
16595 totalProperty: 'total',
16596 successProperty : 'success',
16601 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16603 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16605 readerType : 'Json',
16608 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16609 * Used by Store query builder to append _requestMeta to params.
16612 metaFromRemote : false,
16614 * This method is only used by a DataProxy which has retrieved data from a remote server.
16615 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16616 * @return {Object} data A data block which is used by an Roo.data.Store object as
16617 * a cache of Roo.data.Records.
16619 read : function(response){
16620 var json = response.responseText;
16622 var o = /* eval:var:o */ eval("("+json+")");
16624 throw {message: "JsonReader.read: Json object not found"};
16630 this.metaFromRemote = true;
16631 this.meta = o.metaData;
16632 this.recordType = Roo.data.Record.create(o.metaData.fields);
16633 this.onMetaChange(this.meta, this.recordType, o);
16635 return this.readRecords(o);
16638 // private function a store will implement
16639 onMetaChange : function(meta, recordType, o){
16646 simpleAccess: function(obj, subsc) {
16653 getJsonAccessor: function(){
16655 return function(expr) {
16657 return(re.test(expr))
16658 ? new Function("obj", "return obj." + expr)
16663 return Roo.emptyFn;
16668 * Create a data block containing Roo.data.Records from an XML document.
16669 * @param {Object} o An object which contains an Array of row objects in the property specified
16670 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16671 * which contains the total size of the dataset.
16672 * @return {Object} data A data block which is used by an Roo.data.Store object as
16673 * a cache of Roo.data.Records.
16675 readRecords : function(o){
16677 * After any data loads, the raw JSON data is available for further custom processing.
16681 var s = this.meta, Record = this.recordType,
16682 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16684 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16686 if(s.totalProperty) {
16687 this.getTotal = this.getJsonAccessor(s.totalProperty);
16689 if(s.successProperty) {
16690 this.getSuccess = this.getJsonAccessor(s.successProperty);
16692 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16694 var g = this.getJsonAccessor(s.id);
16695 this.getId = function(rec) {
16697 return (r === undefined || r === "") ? null : r;
16700 this.getId = function(){return null;};
16703 for(var jj = 0; jj < fl; jj++){
16705 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16706 this.ef[jj] = this.getJsonAccessor(map);
16710 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16711 if(s.totalProperty){
16712 var vt = parseInt(this.getTotal(o), 10);
16717 if(s.successProperty){
16718 var vs = this.getSuccess(o);
16719 if(vs === false || vs === 'false'){
16724 for(var i = 0; i < c; i++){
16727 var id = this.getId(n);
16728 for(var j = 0; j < fl; j++){
16730 var v = this.ef[j](n);
16732 Roo.log('missing convert for ' + f.name);
16736 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16740 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16746 var record = new Record(values, id);
16748 records[i] = record;
16754 totalRecords : totalRecords
16757 // used when loading children.. @see loadDataFromChildren
16758 toLoadData: function(rec)
16760 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16761 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16762 return { data : data, total : data.length };
16767 * Ext JS Library 1.1.1
16768 * Copyright(c) 2006-2007, Ext JS, LLC.
16770 * Originally Released Under LGPL - original licence link has changed is not relivant.
16773 * <script type="text/javascript">
16777 * @class Roo.data.ArrayReader
16778 * @extends Roo.data.DataReader
16779 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16780 * Each element of that Array represents a row of data fields. The
16781 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16782 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16786 var RecordDef = Roo.data.Record.create([
16787 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16788 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16790 var myReader = new Roo.data.ArrayReader({
16791 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16795 * This would consume an Array like this:
16797 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16801 * Create a new JsonReader
16802 * @param {Object} meta Metadata configuration options.
16803 * @param {Object|Array} recordType Either an Array of field definition objects
16805 * @cfg {Array} fields Array of field definition objects
16806 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16807 * as specified to {@link Roo.data.Record#create},
16808 * or an {@link Roo.data.Record} object
16811 * created using {@link Roo.data.Record#create}.
16813 Roo.data.ArrayReader = function(meta, recordType)
16815 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16818 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16821 * Create a data block containing Roo.data.Records from an XML document.
16822 * @param {Object} o An Array of row objects which represents the dataset.
16823 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16824 * a cache of Roo.data.Records.
16826 readRecords : function(o)
16828 var sid = this.meta ? this.meta.id : null;
16829 var recordType = this.recordType, fields = recordType.prototype.fields;
16832 for(var i = 0; i < root.length; i++){
16835 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16836 for(var j = 0, jlen = fields.length; j < jlen; j++){
16837 var f = fields.items[j];
16838 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16839 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16841 values[f.name] = v;
16843 var record = new recordType(values, id);
16845 records[records.length] = record;
16849 totalRecords : records.length
16852 // used when loading children.. @see loadDataFromChildren
16853 toLoadData: function(rec)
16855 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16856 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16867 * @class Roo.bootstrap.form.ComboBox
16868 * @extends Roo.bootstrap.form.TriggerField
16869 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16870 * @cfg {Boolean} append (true|false) default false
16871 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16872 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16873 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16874 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16875 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16876 * @cfg {Boolean} animate default true
16877 * @cfg {Boolean} emptyResultText only for touch device
16878 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16879 * @cfg {String} emptyTitle default ''
16880 * @cfg {Number} width fixed with? experimental
16882 * Create a new ComboBox.
16883 * @param {Object} config Configuration options
16885 Roo.bootstrap.form.ComboBox = function(config){
16886 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16890 * Fires when the dropdown list is expanded
16891 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896 * Fires when the dropdown list is collapsed
16897 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16901 * @event beforeselect
16902 * Fires before a list item is selected. Return false to cancel the selection.
16903 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904 * @param {Roo.data.Record} record The data record returned from the underlying store
16905 * @param {Number} index The index of the selected item in the dropdown list
16907 'beforeselect' : true,
16910 * Fires when a list item is selected
16911 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16913 * @param {Number} index The index of the selected item in the dropdown list
16917 * @event beforequery
16918 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16919 * The event object passed has these properties:
16920 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921 * @param {String} query The query
16922 * @param {Boolean} forceAll true to force "all" query
16923 * @param {Boolean} cancel true to cancel the query
16924 * @param {Object} e The query event object
16926 'beforequery': true,
16929 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16930 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16935 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16936 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16937 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16942 * Fires when the remove value from the combobox array
16943 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16947 * @event afterremove
16948 * Fires when the remove value from the combobox array
16949 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16951 'afterremove' : true,
16953 * @event specialfilter
16954 * Fires when specialfilter
16955 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16957 'specialfilter' : true,
16960 * Fires when tick the element
16961 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16965 * @event touchviewdisplay
16966 * Fires when touch view require special display (default is using displayField)
16967 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16968 * @param {Object} cfg set html .
16970 'touchviewdisplay' : true
16975 this.tickItems = [];
16977 this.selectedIndex = -1;
16978 if(this.mode == 'local'){
16979 if(config.queryDelay === undefined){
16980 this.queryDelay = 10;
16982 if(config.minChars === undefined){
16988 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16991 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16992 * rendering into an Roo.Editor, defaults to false)
16995 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16996 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16999 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17002 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17003 * the dropdown list (defaults to undefined, with no header element)
17007 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
17011 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17013 listWidth: undefined,
17015 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17016 * mode = 'remote' or 'text' if mode = 'local')
17018 displayField: undefined,
17021 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17022 * mode = 'remote' or 'value' if mode = 'local').
17023 * Note: use of a valueField requires the user make a selection
17024 * in order for a value to be mapped.
17026 valueField: undefined,
17028 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17033 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17034 * field's data value (defaults to the underlying DOM element's name)
17036 hiddenName: undefined,
17038 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17042 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17044 selectedClass: 'active',
17047 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17051 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17052 * anchor positions (defaults to 'tl-bl')
17054 listAlign: 'tl-bl?',
17056 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17060 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17061 * query specified by the allQuery config option (defaults to 'query')
17063 triggerAction: 'query',
17065 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17066 * (defaults to 4, does not apply if editable = false)
17070 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17071 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17075 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17076 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17080 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17081 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17085 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17086 * when editable = true (defaults to false)
17088 selectOnFocus:false,
17090 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17092 queryParam: 'query',
17094 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17095 * when mode = 'remote' (defaults to 'Loading...')
17097 loadingText: 'Loading...',
17099 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17103 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17107 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17108 * traditional select (defaults to true)
17112 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17116 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17120 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17121 * listWidth has a higher value)
17125 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17126 * allow the user to set arbitrary text into the field (defaults to false)
17128 forceSelection:false,
17130 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17131 * if typeAhead = true (defaults to 250)
17133 typeAheadDelay : 250,
17135 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17136 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17138 valueNotFoundText : undefined,
17140 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17142 blockFocus : false,
17145 * @cfg {Boolean} disableClear Disable showing of clear button.
17147 disableClear : false,
17149 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17151 alwaysQuery : false,
17154 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17159 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17161 invalidClass : "has-warning",
17164 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17166 validClass : "has-success",
17169 * @cfg {Boolean} specialFilter (true|false) special filter default false
17171 specialFilter : false,
17174 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17176 mobileTouchView : true,
17179 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17181 useNativeIOS : false,
17184 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17186 mobile_restrict_height : false,
17188 ios_options : false,
17200 btnPosition : 'right',
17201 triggerList : true,
17202 showToggleBtn : true,
17204 emptyResultText: 'Empty',
17205 triggerText : 'Select',
17209 // element that contains real text value.. (when hidden is used..)
17211 getAutoCreate : function()
17216 * Render classic select for iso
17219 if(Roo.isIOS && this.useNativeIOS){
17220 cfg = this.getAutoCreateNativeIOS();
17228 if(Roo.isTouch && this.mobileTouchView){
17229 cfg = this.getAutoCreateTouchView();
17236 if(!this.tickable){
17237 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17242 * ComboBox with tickable selections
17245 var align = this.labelAlign || this.parentLabelAlign();
17248 cls : 'form-group roo-combobox-tickable' //input-group
17251 var btn_text_select = '';
17252 var btn_text_done = '';
17253 var btn_text_cancel = '';
17255 if (this.btn_text_show) {
17256 btn_text_select = 'Select';
17257 btn_text_done = 'Done';
17258 btn_text_cancel = 'Cancel';
17263 cls : 'tickable-buttons',
17268 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17269 //html : this.triggerText
17270 html: btn_text_select
17276 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17278 html: btn_text_done
17284 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17286 html: btn_text_cancel
17292 buttons.cn.unshift({
17294 cls: 'roo-select2-search-field-input'
17300 Roo.each(buttons.cn, function(c){
17302 c.cls += ' btn-' + _this.size;
17305 if (_this.disabled) {
17312 style : 'display: contents',
17317 cls: 'form-hidden-field'
17321 cls: 'roo-select2-choices',
17325 cls: 'roo-select2-search-field',
17336 cls: 'roo-select2-container input-group roo-select2-container-multi',
17342 // cls: 'typeahead typeahead-long dropdown-menu',
17343 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17348 if(this.hasFeedback && !this.allowBlank){
17352 cls: 'glyphicon form-control-feedback'
17355 combobox.cn.push(feedback);
17362 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17363 tooltip : 'This field is required'
17365 if (Roo.bootstrap.version == 4) {
17368 style : 'display:none'
17371 if (align ==='left' && this.fieldLabel.length) {
17373 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17380 cls : 'control-label col-form-label',
17381 html : this.fieldLabel
17393 var labelCfg = cfg.cn[1];
17394 var contentCfg = cfg.cn[2];
17397 if(this.indicatorpos == 'right'){
17403 cls : 'control-label col-form-label',
17407 html : this.fieldLabel
17423 labelCfg = cfg.cn[0];
17424 contentCfg = cfg.cn[1];
17428 if(this.labelWidth > 12){
17429 labelCfg.style = "width: " + this.labelWidth + 'px';
17431 if(this.width * 1 > 0){
17432 contentCfg.style = "width: " + this.width + 'px';
17434 if(this.labelWidth < 13 && this.labelmd == 0){
17435 this.labelmd = this.labelWidth;
17438 if(this.labellg > 0){
17439 labelCfg.cls += ' col-lg-' + this.labellg;
17440 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17443 if(this.labelmd > 0){
17444 labelCfg.cls += ' col-md-' + this.labelmd;
17445 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17448 if(this.labelsm > 0){
17449 labelCfg.cls += ' col-sm-' + this.labelsm;
17450 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17453 if(this.labelxs > 0){
17454 labelCfg.cls += ' col-xs-' + this.labelxs;
17455 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17459 } else if ( this.fieldLabel.length) {
17460 // Roo.log(" label");
17465 //cls : 'input-group-addon',
17466 html : this.fieldLabel
17471 if(this.indicatorpos == 'right'){
17475 //cls : 'input-group-addon',
17476 html : this.fieldLabel
17486 // Roo.log(" no label && no align");
17493 ['xs','sm','md','lg'].map(function(size){
17494 if (settings[size]) {
17495 cfg.cls += ' col-' + size + '-' + settings[size];
17503 _initEventsCalled : false,
17506 initEvents: function()
17508 if (this._initEventsCalled) { // as we call render... prevent looping...
17511 this._initEventsCalled = true;
17514 throw "can not find store for combo";
17517 this.indicator = this.indicatorEl();
17519 this.store = Roo.factory(this.store, Roo.data);
17520 this.store.parent = this;
17522 // if we are building from html. then this element is so complex, that we can not really
17523 // use the rendered HTML.
17524 // so we have to trash and replace the previous code.
17525 if (Roo.XComponent.build_from_html) {
17526 // remove this element....
17527 var e = this.el.dom, k=0;
17528 while (e ) { e = e.previousSibling; ++k;}
17533 this.rendered = false;
17535 this.render(this.parent().getChildContainer(true), k);
17538 if(Roo.isIOS && this.useNativeIOS){
17539 this.initIOSView();
17547 if(Roo.isTouch && this.mobileTouchView){
17548 this.initTouchView();
17553 this.initTickableEvents();
17557 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17559 if(this.hiddenName){
17561 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17563 this.hiddenField.dom.value =
17564 this.hiddenValue !== undefined ? this.hiddenValue :
17565 this.value !== undefined ? this.value : '';
17567 // prevent input submission
17568 this.el.dom.removeAttribute('name');
17569 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17574 // this.el.dom.setAttribute('autocomplete', 'off');
17577 var cls = 'x-combo-list';
17579 //this.list = new Roo.Layer({
17580 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17586 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17587 _this.list.setWidth(lw);
17590 this.list.on('mouseover', this.onViewOver, this);
17591 this.list.on('mousemove', this.onViewMove, this);
17592 this.list.on('scroll', this.onViewScroll, this);
17595 this.list.swallowEvent('mousewheel');
17596 this.assetHeight = 0;
17599 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17600 this.assetHeight += this.header.getHeight();
17603 this.innerList = this.list.createChild({cls:cls+'-inner'});
17604 this.innerList.on('mouseover', this.onViewOver, this);
17605 this.innerList.on('mousemove', this.onViewMove, this);
17606 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17608 if(this.allowBlank && !this.pageSize && !this.disableClear){
17609 this.footer = this.list.createChild({cls:cls+'-ft'});
17610 this.pageTb = new Roo.Toolbar(this.footer);
17614 this.footer = this.list.createChild({cls:cls+'-ft'});
17615 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17616 {pageSize: this.pageSize});
17620 if (this.pageTb && this.allowBlank && !this.disableClear) {
17622 this.pageTb.add(new Roo.Toolbar.Fill(), {
17623 cls: 'x-btn-icon x-btn-clear',
17625 handler: function()
17628 _this.clearValue();
17629 _this.onSelect(false, -1);
17634 this.assetHeight += this.footer.getHeight();
17639 this.tpl = Roo.bootstrap.version == 4 ?
17640 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17641 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17644 this.view = new Roo.View(this.list, this.tpl, {
17645 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17647 //this.view.wrapEl.setDisplayed(false);
17648 this.view.on('click', this.onViewClick, this);
17651 this.store.on('beforeload', this.onBeforeLoad, this);
17652 this.store.on('load', this.onLoad, this);
17653 this.store.on('loadexception', this.onLoadException, this);
17655 if(this.resizable){
17656 this.resizer = new Roo.Resizable(this.list, {
17657 pinned:true, handles:'se'
17659 this.resizer.on('resize', function(r, w, h){
17660 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17661 this.listWidth = w;
17662 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17663 this.restrictHeight();
17665 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17668 if(!this.editable){
17669 this.editable = true;
17670 this.setEditable(false);
17675 if (typeof(this.events.add.listeners) != 'undefined') {
17677 this.addicon = this.wrap.createChild(
17678 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17680 this.addicon.on('click', function(e) {
17681 this.fireEvent('add', this);
17684 if (typeof(this.events.edit.listeners) != 'undefined') {
17686 this.editicon = this.wrap.createChild(
17687 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17688 if (this.addicon) {
17689 this.editicon.setStyle('margin-left', '40px');
17691 this.editicon.on('click', function(e) {
17693 // we fire even if inothing is selected..
17694 this.fireEvent('edit', this, this.lastData );
17700 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17701 "up" : function(e){
17702 this.inKeyMode = true;
17706 "down" : function(e){
17707 if(!this.isExpanded()){
17708 this.onTriggerClick();
17710 this.inKeyMode = true;
17715 "enter" : function(e){
17716 // this.onViewClick();
17720 if(this.fireEvent("specialkey", this, e)){
17721 this.onViewClick(false);
17727 "esc" : function(e){
17731 "tab" : function(e){
17734 if(this.fireEvent("specialkey", this, e)){
17735 this.onViewClick(false);
17743 doRelay : function(foo, bar, hname){
17744 if(hname == 'down' || this.scope.isExpanded()){
17745 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17754 this.queryDelay = Math.max(this.queryDelay || 10,
17755 this.mode == 'local' ? 10 : 250);
17758 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17760 if(this.typeAhead){
17761 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17763 if(this.editable !== false){
17764 this.inputEl().on("keyup", this.onKeyUp, this);
17766 if(this.forceSelection){
17767 this.inputEl().on('blur', this.doForce, this);
17771 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17772 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17776 initTickableEvents: function()
17780 if(this.hiddenName){
17782 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17784 this.hiddenField.dom.value =
17785 this.hiddenValue !== undefined ? this.hiddenValue :
17786 this.value !== undefined ? this.value : '';
17788 // prevent input submission
17789 this.el.dom.removeAttribute('name');
17790 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17795 // this.list = this.el.select('ul.dropdown-menu',true).first();
17797 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17798 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17799 if(this.triggerList){
17800 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17803 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17804 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17806 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17807 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17809 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17810 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17812 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17813 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17814 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17817 this.cancelBtn.hide();
17822 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17823 _this.list.setWidth(lw);
17826 this.list.on('mouseover', this.onViewOver, this);
17827 this.list.on('mousemove', this.onViewMove, this);
17829 this.list.on('scroll', this.onViewScroll, this);
17832 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17833 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17836 this.view = new Roo.View(this.list, this.tpl, {
17841 selectedClass: this.selectedClass
17844 //this.view.wrapEl.setDisplayed(false);
17845 this.view.on('click', this.onViewClick, this);
17849 this.store.on('beforeload', this.onBeforeLoad, this);
17850 this.store.on('load', this.onLoad, this);
17851 this.store.on('loadexception', this.onLoadException, this);
17854 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17855 "up" : function(e){
17856 this.inKeyMode = true;
17860 "down" : function(e){
17861 this.inKeyMode = true;
17865 "enter" : function(e){
17866 if(this.fireEvent("specialkey", this, e)){
17867 this.onViewClick(false);
17873 "esc" : function(e){
17874 this.onTickableFooterButtonClick(e, false, false);
17877 "tab" : function(e){
17878 this.fireEvent("specialkey", this, e);
17880 this.onTickableFooterButtonClick(e, false, false);
17887 doRelay : function(e, fn, key){
17888 if(this.scope.isExpanded()){
17889 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17898 this.queryDelay = Math.max(this.queryDelay || 10,
17899 this.mode == 'local' ? 10 : 250);
17902 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17904 if(this.typeAhead){
17905 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17908 if(this.editable !== false){
17909 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17912 this.indicator = this.indicatorEl();
17914 if(this.indicator){
17915 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17916 this.indicator.hide();
17921 onDestroy : function(){
17923 this.view.setStore(null);
17924 this.view.el.removeAllListeners();
17925 this.view.el.remove();
17926 this.view.purgeListeners();
17929 this.list.dom.innerHTML = '';
17933 this.store.un('beforeload', this.onBeforeLoad, this);
17934 this.store.un('load', this.onLoad, this);
17935 this.store.un('loadexception', this.onLoadException, this);
17937 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17941 fireKey : function(e){
17942 if(e.isNavKeyPress() && !this.list.isVisible()){
17943 this.fireEvent("specialkey", this, e);
17948 onResize: function(w, h)
17952 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17954 // if(typeof w != 'number'){
17955 // // we do not handle it!?!?
17958 // var tw = this.trigger.getWidth();
17959 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17960 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17962 // this.inputEl().setWidth( this.adjustWidth('input', x));
17964 // //this.trigger.setStyle('left', x+'px');
17966 // if(this.list && this.listWidth === undefined){
17967 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17968 // this.list.setWidth(lw);
17969 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17977 * Allow or prevent the user from directly editing the field text. If false is passed,
17978 * the user will only be able to select from the items defined in the dropdown list. This method
17979 * is the runtime equivalent of setting the 'editable' config option at config time.
17980 * @param {Boolean} value True to allow the user to directly edit the field text
17982 setEditable : function(value){
17983 if(value == this.editable){
17986 this.editable = value;
17988 this.inputEl().dom.setAttribute('readOnly', true);
17989 this.inputEl().on('mousedown', this.onTriggerClick, this);
17990 this.inputEl().addClass('x-combo-noedit');
17992 this.inputEl().dom.removeAttribute('readOnly');
17993 this.inputEl().un('mousedown', this.onTriggerClick, this);
17994 this.inputEl().removeClass('x-combo-noedit');
18000 onBeforeLoad : function(combo,opts){
18001 if(!this.hasFocus){
18005 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18007 this.restrictHeight();
18008 this.selectedIndex = -1;
18012 onLoad : function(){
18014 this.hasQuery = false;
18016 if(!this.hasFocus){
18020 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18021 this.loading.hide();
18024 if(this.store.getCount() > 0){
18027 this.restrictHeight();
18028 if(this.lastQuery == this.allQuery){
18029 if(this.editable && !this.tickable){
18030 this.inputEl().dom.select();
18034 !this.selectByValue(this.value, true) &&
18037 !this.store.lastOptions ||
18038 typeof(this.store.lastOptions.add) == 'undefined' ||
18039 this.store.lastOptions.add != true
18042 this.select(0, true);
18045 if(this.autoFocus){
18048 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18049 this.taTask.delay(this.typeAheadDelay);
18053 this.onEmptyResults();
18059 onLoadException : function()
18061 this.hasQuery = false;
18063 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18064 this.loading.hide();
18067 if(this.tickable && this.editable){
18072 // only causes errors at present
18073 //Roo.log(this.store.reader.jsonData);
18074 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18076 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18082 onTypeAhead : function(){
18083 if(this.store.getCount() > 0){
18084 var r = this.store.getAt(0);
18085 var newValue = r.data[this.displayField];
18086 var len = newValue.length;
18087 var selStart = this.getRawValue().length;
18089 if(selStart != len){
18090 this.setRawValue(newValue);
18091 this.selectText(selStart, newValue.length);
18097 onSelect : function(record, index){
18099 if(this.fireEvent('beforeselect', this, record, index) !== false){
18101 this.setFromData(index > -1 ? record.data : false);
18104 this.fireEvent('select', this, record, index);
18109 * Returns the currently selected field value or empty string if no value is set.
18110 * @return {String} value The selected value
18112 getValue : function()
18114 if(Roo.isIOS && this.useNativeIOS){
18115 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18119 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18122 if(this.valueField){
18123 return typeof this.value != 'undefined' ? this.value : '';
18125 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18129 getRawValue : function()
18131 if(Roo.isIOS && this.useNativeIOS){
18132 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18135 var v = this.inputEl().getValue();
18141 * Clears any text/value currently set in the field
18143 clearValue : function(){
18145 if(this.hiddenField){
18146 this.hiddenField.dom.value = '';
18149 this.setRawValue('');
18150 this.lastSelectionText = '';
18151 this.lastData = false;
18153 var close = this.closeTriggerEl();
18164 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18165 * will be displayed in the field. If the value does not match the data value of an existing item,
18166 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18167 * Otherwise the field will be blank (although the value will still be set).
18168 * @param {String} value The value to match
18170 setValue : function(v)
18172 if(Roo.isIOS && this.useNativeIOS){
18173 this.setIOSValue(v);
18183 if(this.valueField){
18184 var r = this.findRecord(this.valueField, v);
18186 text = r.data[this.displayField];
18187 }else if(this.valueNotFoundText !== undefined){
18188 text = this.valueNotFoundText;
18191 this.lastSelectionText = text;
18192 if(this.hiddenField){
18193 this.hiddenField.dom.value = v;
18195 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18198 var close = this.closeTriggerEl();
18201 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18207 * @property {Object} the last set data for the element
18212 * Sets the value of the field based on a object which is related to the record format for the store.
18213 * @param {Object} value the value to set as. or false on reset?
18215 setFromData : function(o){
18222 var dv = ''; // display value
18223 var vv = ''; // value value..
18225 if (this.displayField) {
18226 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18228 // this is an error condition!!!
18229 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18232 if(this.valueField){
18233 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18236 var close = this.closeTriggerEl();
18239 if(dv.length || vv * 1 > 0){
18241 this.blockFocus=true;
18247 if(this.hiddenField){
18248 this.hiddenField.dom.value = vv;
18250 this.lastSelectionText = dv;
18251 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18255 // no hidden field.. - we store the value in 'value', but still display
18256 // display field!!!!
18257 this.lastSelectionText = dv;
18258 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18265 reset : function(){
18266 // overridden so that last data is reset..
18273 this.setValue(this.originalValue);
18274 //this.clearInvalid();
18275 this.lastData = false;
18277 this.view.clearSelections();
18283 findRecord : function(prop, value){
18285 if(this.store.getCount() > 0){
18286 this.store.each(function(r){
18287 if(r.data[prop] == value){
18297 getName: function()
18299 // returns hidden if it's set..
18300 if (!this.rendered) {return ''};
18301 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18305 onViewMove : function(e, t){
18306 this.inKeyMode = false;
18310 onViewOver : function(e, t){
18311 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18314 var item = this.view.findItemFromChild(t);
18317 var index = this.view.indexOf(item);
18318 this.select(index, false);
18323 onViewClick : function(view, doFocus, el, e)
18325 var index = this.view.getSelectedIndexes()[0];
18327 var r = this.store.getAt(index);
18331 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18338 Roo.each(this.tickItems, function(v,k){
18340 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18342 _this.tickItems.splice(k, 1);
18344 if(typeof(e) == 'undefined' && view == false){
18345 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18357 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18358 this.tickItems.push(r.data);
18361 if(typeof(e) == 'undefined' && view == false){
18362 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18369 this.onSelect(r, index);
18371 if(doFocus !== false && !this.blockFocus){
18372 this.inputEl().focus();
18377 restrictHeight : function(){
18378 //this.innerList.dom.style.height = '';
18379 //var inner = this.innerList.dom;
18380 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18381 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18382 //this.list.beginUpdate();
18383 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18384 this.list.alignTo(this.inputEl(), this.listAlign);
18385 this.list.alignTo(this.inputEl(), this.listAlign);
18386 //this.list.endUpdate();
18390 onEmptyResults : function(){
18392 if(this.tickable && this.editable){
18393 this.hasFocus = false;
18394 this.restrictHeight();
18402 * Returns true if the dropdown list is expanded, else false.
18404 isExpanded : function(){
18405 return this.list.isVisible();
18409 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18410 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18411 * @param {String} value The data value of the item to select
18412 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18413 * selected item if it is not currently in view (defaults to true)
18414 * @return {Boolean} True if the value matched an item in the list, else false
18416 selectByValue : function(v, scrollIntoView){
18417 if(v !== undefined && v !== null){
18418 var r = this.findRecord(this.valueField || this.displayField, v);
18420 this.select(this.store.indexOf(r), scrollIntoView);
18428 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18429 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18430 * @param {Number} index The zero-based index of the list item to select
18431 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18432 * selected item if it is not currently in view (defaults to true)
18434 select : function(index, scrollIntoView){
18435 this.selectedIndex = index;
18436 this.view.select(index);
18437 if(scrollIntoView !== false){
18438 var el = this.view.getNode(index);
18440 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18443 this.list.scrollChildIntoView(el, false);
18449 selectNext : function(){
18450 var ct = this.store.getCount();
18452 if(this.selectedIndex == -1){
18454 }else if(this.selectedIndex < ct-1){
18455 this.select(this.selectedIndex+1);
18461 selectPrev : function(){
18462 var ct = this.store.getCount();
18464 if(this.selectedIndex == -1){
18466 }else if(this.selectedIndex != 0){
18467 this.select(this.selectedIndex-1);
18473 onKeyUp : function(e){
18474 if(this.editable !== false && !e.isSpecialKey()){
18475 this.lastKey = e.getKey();
18476 this.dqTask.delay(this.queryDelay);
18481 validateBlur : function(){
18482 return !this.list || !this.list.isVisible();
18486 initQuery : function(){
18488 var v = this.getRawValue();
18490 if(this.tickable && this.editable){
18491 v = this.tickableInputEl().getValue();
18498 doForce : function(){
18499 if(this.inputEl().dom.value.length > 0){
18500 this.inputEl().dom.value =
18501 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18507 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18508 * query allowing the query action to be canceled if needed.
18509 * @param {String} query The SQL query to execute
18510 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18511 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18512 * saved in the current store (defaults to false)
18514 doQuery : function(q, forceAll){
18516 if(q === undefined || q === null){
18521 forceAll: forceAll,
18525 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18530 forceAll = qe.forceAll;
18531 if(forceAll === true || (q.length >= this.minChars)){
18533 this.hasQuery = true;
18535 if(this.lastQuery != q || this.alwaysQuery){
18536 this.lastQuery = q;
18537 if(this.mode == 'local'){
18538 this.selectedIndex = -1;
18540 this.store.clearFilter();
18543 if(this.specialFilter){
18544 this.fireEvent('specialfilter', this);
18549 this.store.filter(this.displayField, q);
18552 this.store.fireEvent("datachanged", this.store);
18559 this.store.baseParams[this.queryParam] = q;
18561 var options = {params : this.getParams(q)};
18564 options.add = true;
18565 options.params.start = this.page * this.pageSize;
18568 this.store.load(options);
18571 * this code will make the page width larger, at the beginning, the list not align correctly,
18572 * we should expand the list on onLoad
18573 * so command out it
18578 this.selectedIndex = -1;
18583 this.loadNext = false;
18587 getParams : function(q){
18589 //p[this.queryParam] = q;
18593 p.limit = this.pageSize;
18599 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18601 collapse : function(){
18602 if(!this.isExpanded()){
18608 this.hasFocus = false;
18612 this.cancelBtn.hide();
18613 this.trigger.show();
18616 this.tickableInputEl().dom.value = '';
18617 this.tickableInputEl().blur();
18622 Roo.get(document).un('mousedown', this.collapseIf, this);
18623 Roo.get(document).un('mousewheel', this.collapseIf, this);
18624 if (!this.editable) {
18625 Roo.get(document).un('keydown', this.listKeyPress, this);
18627 this.fireEvent('collapse', this);
18633 collapseIf : function(e){
18634 var in_combo = e.within(this.el);
18635 var in_list = e.within(this.list);
18636 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18638 if (in_combo || in_list || is_list) {
18639 //e.stopPropagation();
18644 this.onTickableFooterButtonClick(e, false, false);
18652 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18654 expand : function(){
18656 if(this.isExpanded() || !this.hasFocus){
18660 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18661 this.list.setWidth(lw);
18667 this.restrictHeight();
18671 this.tickItems = Roo.apply([], this.item);
18674 this.cancelBtn.show();
18675 this.trigger.hide();
18678 this.tickableInputEl().focus();
18683 Roo.get(document).on('mousedown', this.collapseIf, this);
18684 Roo.get(document).on('mousewheel', this.collapseIf, this);
18685 if (!this.editable) {
18686 Roo.get(document).on('keydown', this.listKeyPress, this);
18689 this.fireEvent('expand', this);
18693 // Implements the default empty TriggerField.onTriggerClick function
18694 onTriggerClick : function(e)
18696 Roo.log('trigger click');
18698 if(this.disabled || !this.triggerList){
18703 this.loadNext = false;
18705 if(this.isExpanded()){
18707 if (!this.blockFocus) {
18708 this.inputEl().focus();
18712 this.hasFocus = true;
18713 if(this.triggerAction == 'all') {
18714 this.doQuery(this.allQuery, true);
18716 this.doQuery(this.getRawValue());
18718 if (!this.blockFocus) {
18719 this.inputEl().focus();
18724 onTickableTriggerClick : function(e)
18731 this.loadNext = false;
18732 this.hasFocus = true;
18734 if(this.triggerAction == 'all') {
18735 this.doQuery(this.allQuery, true);
18737 this.doQuery(this.getRawValue());
18741 onSearchFieldClick : function(e)
18743 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18744 this.onTickableFooterButtonClick(e, false, false);
18748 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18753 this.loadNext = false;
18754 this.hasFocus = true;
18756 if(this.triggerAction == 'all') {
18757 this.doQuery(this.allQuery, true);
18759 this.doQuery(this.getRawValue());
18763 listKeyPress : function(e)
18765 //Roo.log('listkeypress');
18766 // scroll to first matching element based on key pres..
18767 if (e.isSpecialKey()) {
18770 var k = String.fromCharCode(e.getKey()).toUpperCase();
18773 var csel = this.view.getSelectedNodes();
18774 var cselitem = false;
18776 var ix = this.view.indexOf(csel[0]);
18777 cselitem = this.store.getAt(ix);
18778 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18784 this.store.each(function(v) {
18786 // start at existing selection.
18787 if (cselitem.id == v.id) {
18793 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18794 match = this.store.indexOf(v);
18800 if (match === false) {
18801 return true; // no more action?
18804 this.view.select(match);
18805 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18806 sn.scrollIntoView(sn.dom.parentNode, false);
18809 onViewScroll : function(e, t){
18811 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){
18815 this.hasQuery = true;
18817 this.loading = this.list.select('.loading', true).first();
18819 if(this.loading === null){
18820 this.list.createChild({
18822 cls: 'loading roo-select2-more-results roo-select2-active',
18823 html: 'Loading more results...'
18826 this.loading = this.list.select('.loading', true).first();
18828 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18830 this.loading.hide();
18833 this.loading.show();
18838 this.loadNext = true;
18840 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18845 addItem : function(o)
18847 var dv = ''; // display value
18849 if (this.displayField) {
18850 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18852 // this is an error condition!!!
18853 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18860 var choice = this.choices.createChild({
18862 cls: 'roo-select2-search-choice',
18871 cls: 'roo-select2-search-choice-close fa fa-times',
18876 }, this.searchField);
18878 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18880 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18888 this.inputEl().dom.value = '';
18893 onRemoveItem : function(e, _self, o)
18895 e.preventDefault();
18897 this.lastItem = Roo.apply([], this.item);
18899 var index = this.item.indexOf(o.data) * 1;
18902 Roo.log('not this item?!');
18906 this.item.splice(index, 1);
18911 this.fireEvent('remove', this, e);
18917 syncValue : function()
18919 if(!this.item.length){
18926 Roo.each(this.item, function(i){
18927 if(_this.valueField){
18928 value.push(i[_this.valueField]);
18935 this.value = value.join(',');
18937 if(this.hiddenField){
18938 this.hiddenField.dom.value = this.value;
18941 this.store.fireEvent("datachanged", this.store);
18946 clearItem : function()
18948 if(!this.multiple){
18954 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18962 if(this.tickable && !Roo.isTouch){
18963 this.view.refresh();
18967 inputEl: function ()
18969 if(Roo.isIOS && this.useNativeIOS){
18970 return this.el.select('select.roo-ios-select', true).first();
18973 if(Roo.isTouch && this.mobileTouchView){
18974 return this.el.select('input.form-control',true).first();
18978 return this.searchField;
18981 return this.el.select('input.form-control',true).first();
18984 onTickableFooterButtonClick : function(e, btn, el)
18986 e.preventDefault();
18988 this.lastItem = Roo.apply([], this.item);
18990 if(btn && btn.name == 'cancel'){
18991 this.tickItems = Roo.apply([], this.item);
19000 Roo.each(this.tickItems, function(o){
19008 validate : function()
19010 if(this.getVisibilityEl().hasClass('hidden')){
19014 var v = this.getRawValue();
19017 v = this.getValue();
19020 if(this.disabled || this.allowBlank || v.length){
19025 this.markInvalid();
19029 tickableInputEl : function()
19031 if(!this.tickable || !this.editable){
19032 return this.inputEl();
19035 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19039 getAutoCreateTouchView : function()
19044 cls: 'form-group' //input-group
19050 type : this.inputType,
19051 cls : 'form-control x-combo-noedit',
19052 autocomplete: 'new-password',
19053 placeholder : this.placeholder || '',
19058 input.name = this.name;
19062 input.cls += ' input-' + this.size;
19065 if (this.disabled) {
19066 input.disabled = true;
19070 cls : 'roo-combobox-wrap',
19077 inputblock.cls += ' input-group';
19079 inputblock.cn.unshift({
19081 cls : 'input-group-addon input-group-prepend input-group-text',
19086 if(this.removable && !this.multiple){
19087 inputblock.cls += ' roo-removable';
19089 inputblock.cn.push({
19092 cls : 'roo-combo-removable-btn close'
19096 if(this.hasFeedback && !this.allowBlank){
19098 inputblock.cls += ' has-feedback';
19100 inputblock.cn.push({
19102 cls: 'glyphicon form-control-feedback'
19109 inputblock.cls += (this.before) ? '' : ' input-group';
19111 inputblock.cn.push({
19113 cls : 'input-group-addon input-group-append input-group-text',
19119 var ibwrap = inputblock;
19124 cls: 'roo-select2-choices',
19128 cls: 'roo-select2-search-field',
19141 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19146 cls: 'form-hidden-field'
19152 if(!this.multiple && this.showToggleBtn){
19158 if (this.caret != false) {
19161 cls: 'fa fa-' + this.caret
19168 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19170 Roo.bootstrap.version == 3 ? caret : '',
19173 cls: 'combobox-clear',
19187 combobox.cls += ' roo-select2-container-multi';
19190 var required = this.allowBlank ? {
19192 style: 'display: none'
19195 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19196 tooltip : 'This field is required'
19199 var align = this.labelAlign || this.parentLabelAlign();
19201 if (align ==='left' && this.fieldLabel.length) {
19207 cls : 'control-label col-form-label',
19208 html : this.fieldLabel
19212 cls : 'roo-combobox-wrap ',
19219 var labelCfg = cfg.cn[1];
19220 var contentCfg = cfg.cn[2];
19223 if(this.indicatorpos == 'right'){
19228 cls : 'control-label col-form-label',
19232 html : this.fieldLabel
19238 cls : "roo-combobox-wrap ",
19246 labelCfg = cfg.cn[0];
19247 contentCfg = cfg.cn[1];
19252 if(this.labelWidth > 12){
19253 labelCfg.style = "width: " + this.labelWidth + 'px';
19256 if(this.labelWidth < 13 && this.labelmd == 0){
19257 this.labelmd = this.labelWidth;
19260 if(this.labellg > 0){
19261 labelCfg.cls += ' col-lg-' + this.labellg;
19262 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19265 if(this.labelmd > 0){
19266 labelCfg.cls += ' col-md-' + this.labelmd;
19267 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19270 if(this.labelsm > 0){
19271 labelCfg.cls += ' col-sm-' + this.labelsm;
19272 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19275 if(this.labelxs > 0){
19276 labelCfg.cls += ' col-xs-' + this.labelxs;
19277 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19281 } else if ( this.fieldLabel.length) {
19286 cls : 'control-label',
19287 html : this.fieldLabel
19298 if(this.indicatorpos == 'right'){
19302 cls : 'control-label',
19303 html : this.fieldLabel,
19321 var settings = this;
19323 ['xs','sm','md','lg'].map(function(size){
19324 if (settings[size]) {
19325 cfg.cls += ' col-' + size + '-' + settings[size];
19332 initTouchView : function()
19334 this.renderTouchView();
19336 this.touchViewEl.on('scroll', function(){
19337 this.el.dom.scrollTop = 0;
19340 this.originalValue = this.getValue();
19342 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19344 this.inputEl().on("click", this.showTouchView, this);
19345 if (this.triggerEl) {
19346 this.triggerEl.on("click", this.showTouchView, this);
19350 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19351 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19353 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19355 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19356 this.store.on('load', this.onTouchViewLoad, this);
19357 this.store.on('loadexception', this.onTouchViewLoadException, this);
19359 if(this.hiddenName){
19361 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19363 this.hiddenField.dom.value =
19364 this.hiddenValue !== undefined ? this.hiddenValue :
19365 this.value !== undefined ? this.value : '';
19367 this.el.dom.removeAttribute('name');
19368 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19372 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19373 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19376 if(this.removable && !this.multiple){
19377 var close = this.closeTriggerEl();
19379 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19380 close.on('click', this.removeBtnClick, this, close);
19384 * fix the bug in Safari iOS8
19386 this.inputEl().on("focus", function(e){
19387 document.activeElement.blur();
19390 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19397 renderTouchView : function()
19399 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19400 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19402 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19403 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19405 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19406 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19407 this.touchViewBodyEl.setStyle('overflow', 'auto');
19409 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19410 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19412 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19413 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19417 showTouchView : function()
19423 this.touchViewHeaderEl.hide();
19425 if(this.modalTitle.length){
19426 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19427 this.touchViewHeaderEl.show();
19430 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19431 this.touchViewEl.show();
19433 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19435 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19436 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19438 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19440 if(this.modalTitle.length){
19441 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19444 this.touchViewBodyEl.setHeight(bodyHeight);
19448 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19450 this.touchViewEl.addClass(['in','show']);
19453 if(this._touchViewMask){
19454 Roo.get(document.body).addClass("x-body-masked");
19455 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19456 this._touchViewMask.setStyle('z-index', 10000);
19457 this._touchViewMask.addClass('show');
19460 this.doTouchViewQuery();
19464 hideTouchView : function()
19466 this.touchViewEl.removeClass(['in','show']);
19470 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19472 this.touchViewEl.setStyle('display', 'none');
19475 if(this._touchViewMask){
19476 this._touchViewMask.removeClass('show');
19477 Roo.get(document.body).removeClass("x-body-masked");
19481 setTouchViewValue : function()
19488 Roo.each(this.tickItems, function(o){
19493 this.hideTouchView();
19496 doTouchViewQuery : function()
19505 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19509 if(!this.alwaysQuery || this.mode == 'local'){
19510 this.onTouchViewLoad();
19517 onTouchViewBeforeLoad : function(combo,opts)
19523 onTouchViewLoad : function()
19525 if(this.store.getCount() < 1){
19526 this.onTouchViewEmptyResults();
19530 this.clearTouchView();
19532 var rawValue = this.getRawValue();
19534 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19536 this.tickItems = [];
19538 this.store.data.each(function(d, rowIndex){
19539 var row = this.touchViewListGroup.createChild(template);
19541 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19542 row.addClass(d.data.cls);
19545 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19548 html : d.data[this.displayField]
19551 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19552 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19555 row.removeClass('selected');
19556 if(!this.multiple && this.valueField &&
19557 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19560 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19561 row.addClass('selected');
19564 if(this.multiple && this.valueField &&
19565 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19569 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19570 this.tickItems.push(d.data);
19573 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19577 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19579 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19581 if(this.modalTitle.length){
19582 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19585 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19587 if(this.mobile_restrict_height && listHeight < bodyHeight){
19588 this.touchViewBodyEl.setHeight(listHeight);
19593 if(firstChecked && listHeight > bodyHeight){
19594 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19599 onTouchViewLoadException : function()
19601 this.hideTouchView();
19604 onTouchViewEmptyResults : function()
19606 this.clearTouchView();
19608 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19610 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19614 clearTouchView : function()
19616 this.touchViewListGroup.dom.innerHTML = '';
19619 onTouchViewClick : function(e, el, o)
19621 e.preventDefault();
19624 var rowIndex = o.rowIndex;
19626 var r = this.store.getAt(rowIndex);
19628 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19630 if(!this.multiple){
19631 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19632 c.dom.removeAttribute('checked');
19635 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19637 this.setFromData(r.data);
19639 var close = this.closeTriggerEl();
19645 this.hideTouchView();
19647 this.fireEvent('select', this, r, rowIndex);
19652 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19653 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19654 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19658 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19659 this.addItem(r.data);
19660 this.tickItems.push(r.data);
19664 getAutoCreateNativeIOS : function()
19667 cls: 'form-group' //input-group,
19672 cls : 'roo-ios-select'
19676 combobox.name = this.name;
19679 if (this.disabled) {
19680 combobox.disabled = true;
19683 var settings = this;
19685 ['xs','sm','md','lg'].map(function(size){
19686 if (settings[size]) {
19687 cfg.cls += ' col-' + size + '-' + settings[size];
19697 initIOSView : function()
19699 this.store.on('load', this.onIOSViewLoad, this);
19704 onIOSViewLoad : function()
19706 if(this.store.getCount() < 1){
19710 this.clearIOSView();
19712 if(this.allowBlank) {
19714 var default_text = '-- SELECT --';
19716 if(this.placeholder.length){
19717 default_text = this.placeholder;
19720 if(this.emptyTitle.length){
19721 default_text += ' - ' + this.emptyTitle + ' -';
19724 var opt = this.inputEl().createChild({
19727 html : default_text
19731 o[this.valueField] = 0;
19732 o[this.displayField] = default_text;
19734 this.ios_options.push({
19741 this.store.data.each(function(d, rowIndex){
19745 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19746 html = d.data[this.displayField];
19751 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19752 value = d.data[this.valueField];
19761 if(this.value == d.data[this.valueField]){
19762 option['selected'] = true;
19765 var opt = this.inputEl().createChild(option);
19767 this.ios_options.push({
19774 this.inputEl().on('change', function(){
19775 this.fireEvent('select', this);
19780 clearIOSView: function()
19782 this.inputEl().dom.innerHTML = '';
19784 this.ios_options = [];
19787 setIOSValue: function(v)
19791 if(!this.ios_options){
19795 Roo.each(this.ios_options, function(opts){
19797 opts.el.dom.removeAttribute('selected');
19799 if(opts.data[this.valueField] != v){
19803 opts.el.dom.setAttribute('selected', true);
19809 * @cfg {Boolean} grow
19813 * @cfg {Number} growMin
19817 * @cfg {Number} growMax
19826 Roo.apply(Roo.bootstrap.form.ComboBox, {
19830 cls: 'modal-header',
19852 cls: 'list-group-item',
19856 cls: 'roo-combobox-list-group-item-value'
19860 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19874 listItemCheckbox : {
19876 cls: 'list-group-item',
19880 cls: 'roo-combobox-list-group-item-value'
19884 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19900 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19905 cls: 'modal-footer',
19913 cls: 'col-xs-6 text-left',
19916 cls: 'btn btn-danger roo-touch-view-cancel',
19922 cls: 'col-xs-6 text-right',
19925 cls: 'btn btn-success roo-touch-view-ok',
19936 Roo.apply(Roo.bootstrap.form.ComboBox, {
19938 touchViewTemplate : {
19940 cls: 'modal fade roo-combobox-touch-view',
19944 cls: 'modal-dialog',
19945 style : 'position:fixed', // we have to fix position....
19949 cls: 'modal-content',
19951 Roo.bootstrap.form.ComboBox.header,
19952 Roo.bootstrap.form.ComboBox.body,
19953 Roo.bootstrap.form.ComboBox.footer
19962 * Ext JS Library 1.1.1
19963 * Copyright(c) 2006-2007, Ext JS, LLC.
19965 * Originally Released Under LGPL - original licence link has changed is not relivant.
19968 * <script type="text/javascript">
19973 * @extends Roo.util.Observable
19974 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19975 * This class also supports single and multi selection modes. <br>
19976 * Create a data model bound view:
19978 var store = new Roo.data.Store(...);
19980 var view = new Roo.View({
19982 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19984 singleSelect: true,
19985 selectedClass: "ydataview-selected",
19989 // listen for node click?
19990 view.on("click", function(vw, index, node, e){
19991 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19995 dataModel.load("foobar.xml");
19997 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19999 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20000 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20002 * Note: old style constructor is still suported (container, template, config)
20005 * Create a new View
20006 * @param {Object} config The config object
20009 Roo.View = function(config, depreciated_tpl, depreciated_config){
20011 this.parent = false;
20013 if (typeof(depreciated_tpl) == 'undefined') {
20014 // new way.. - universal constructor.
20015 Roo.apply(this, config);
20016 this.el = Roo.get(this.el);
20019 this.el = Roo.get(config);
20020 this.tpl = depreciated_tpl;
20021 Roo.apply(this, depreciated_config);
20023 this.wrapEl = this.el.wrap().wrap();
20024 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20027 if(typeof(this.tpl) == "string"){
20028 this.tpl = new Roo.Template(this.tpl);
20030 // support xtype ctors..
20031 this.tpl = new Roo.factory(this.tpl, Roo);
20035 this.tpl.compile();
20040 * @event beforeclick
20041 * Fires before a click is processed. Returns false to cancel the default action.
20042 * @param {Roo.View} this
20043 * @param {Number} index The index of the target node
20044 * @param {HTMLElement} node The target node
20045 * @param {Roo.EventObject} e The raw event object
20047 "beforeclick" : true,
20050 * Fires when a template node is clicked.
20051 * @param {Roo.View} this
20052 * @param {Number} index The index of the target node
20053 * @param {HTMLElement} node The target node
20054 * @param {Roo.EventObject} e The raw event object
20059 * Fires when a template node is double clicked.
20060 * @param {Roo.View} this
20061 * @param {Number} index The index of the target node
20062 * @param {HTMLElement} node The target node
20063 * @param {Roo.EventObject} e The raw event object
20067 * @event contextmenu
20068 * Fires when a template node is right clicked.
20069 * @param {Roo.View} this
20070 * @param {Number} index The index of the target node
20071 * @param {HTMLElement} node The target node
20072 * @param {Roo.EventObject} e The raw event object
20074 "contextmenu" : true,
20076 * @event selectionchange
20077 * Fires when the selected nodes change.
20078 * @param {Roo.View} this
20079 * @param {Array} selections Array of the selected nodes
20081 "selectionchange" : true,
20084 * @event beforeselect
20085 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20086 * @param {Roo.View} this
20087 * @param {HTMLElement} node The node to be selected
20088 * @param {Array} selections Array of currently selected nodes
20090 "beforeselect" : true,
20092 * @event preparedata
20093 * Fires on every row to render, to allow you to change the data.
20094 * @param {Roo.View} this
20095 * @param {Object} data to be rendered (change this)
20097 "preparedata" : true
20105 "click": this.onClick,
20106 "dblclick": this.onDblClick,
20107 "contextmenu": this.onContextMenu,
20111 this.selections = [];
20113 this.cmp = new Roo.CompositeElementLite([]);
20115 this.store = Roo.factory(this.store, Roo.data);
20116 this.setStore(this.store, true);
20119 if ( this.footer && this.footer.xtype) {
20121 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20123 this.footer.dataSource = this.store;
20124 this.footer.container = fctr;
20125 this.footer = Roo.factory(this.footer, Roo);
20126 fctr.insertFirst(this.el);
20128 // this is a bit insane - as the paging toolbar seems to detach the el..
20129 // dom.parentNode.parentNode.parentNode
20130 // they get detached?
20134 Roo.View.superclass.constructor.call(this);
20139 Roo.extend(Roo.View, Roo.util.Observable, {
20142 * @cfg {Roo.data.Store} store Data store to load data from.
20147 * @cfg {String|Roo.Element} el The container element.
20152 * @cfg {String|Roo.Template} tpl The template used by this View
20156 * @cfg {String} dataName the named area of the template to use as the data area
20157 * Works with domtemplates roo-name="name"
20161 * @cfg {String} selectedClass The css class to add to selected nodes
20163 selectedClass : "x-view-selected",
20165 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20170 * @cfg {String} text to display on mask (default Loading)
20174 * @cfg {Boolean} multiSelect Allow multiple selection
20176 multiSelect : false,
20178 * @cfg {Boolean} singleSelect Allow single selection
20180 singleSelect: false,
20183 * @cfg {Boolean} toggleSelect - selecting
20185 toggleSelect : false,
20188 * @cfg {Boolean} tickable - selecting
20193 * Returns the element this view is bound to.
20194 * @return {Roo.Element}
20196 getEl : function(){
20197 return this.wrapEl;
20203 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20205 refresh : function(){
20206 //Roo.log('refresh');
20209 // if we are using something like 'domtemplate', then
20210 // the what gets used is:
20211 // t.applySubtemplate(NAME, data, wrapping data..)
20212 // the outer template then get' applied with
20213 // the store 'extra data'
20214 // and the body get's added to the
20215 // roo-name="data" node?
20216 // <span class='roo-tpl-{name}'></span> ?????
20220 this.clearSelections();
20221 this.el.update("");
20223 var records = this.store.getRange();
20224 if(records.length < 1) {
20226 // is this valid?? = should it render a template??
20228 this.el.update(this.emptyText);
20232 if (this.dataName) {
20233 this.el.update(t.apply(this.store.meta)); //????
20234 el = this.el.child('.roo-tpl-' + this.dataName);
20237 for(var i = 0, len = records.length; i < len; i++){
20238 var data = this.prepareData(records[i].data, i, records[i]);
20239 this.fireEvent("preparedata", this, data, i, records[i]);
20241 var d = Roo.apply({}, data);
20244 Roo.apply(d, {'roo-id' : Roo.id()});
20248 Roo.each(this.parent.item, function(item){
20249 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20252 Roo.apply(d, {'roo-data-checked' : 'checked'});
20256 html[html.length] = Roo.util.Format.trim(
20258 t.applySubtemplate(this.dataName, d, this.store.meta) :
20265 el.update(html.join(""));
20266 this.nodes = el.dom.childNodes;
20267 this.updateIndexes(0);
20272 * Function to override to reformat the data that is sent to
20273 * the template for each node.
20274 * DEPRICATED - use the preparedata event handler.
20275 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20276 * a JSON object for an UpdateManager bound view).
20278 prepareData : function(data, index, record)
20280 this.fireEvent("preparedata", this, data, index, record);
20284 onUpdate : function(ds, record){
20285 // Roo.log('on update');
20286 this.clearSelections();
20287 var index = this.store.indexOf(record);
20288 var n = this.nodes[index];
20289 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20290 n.parentNode.removeChild(n);
20291 this.updateIndexes(index, index);
20297 onAdd : function(ds, records, index)
20299 //Roo.log(['on Add', ds, records, index] );
20300 this.clearSelections();
20301 if(this.nodes.length == 0){
20305 var n = this.nodes[index];
20306 for(var i = 0, len = records.length; i < len; i++){
20307 var d = this.prepareData(records[i].data, i, records[i]);
20309 this.tpl.insertBefore(n, d);
20312 this.tpl.append(this.el, d);
20315 this.updateIndexes(index);
20318 onRemove : function(ds, record, index){
20319 // Roo.log('onRemove');
20320 this.clearSelections();
20321 var el = this.dataName ?
20322 this.el.child('.roo-tpl-' + this.dataName) :
20325 el.dom.removeChild(this.nodes[index]);
20326 this.updateIndexes(index);
20330 * Refresh an individual node.
20331 * @param {Number} index
20333 refreshNode : function(index){
20334 this.onUpdate(this.store, this.store.getAt(index));
20337 updateIndexes : function(startIndex, endIndex){
20338 var ns = this.nodes;
20339 startIndex = startIndex || 0;
20340 endIndex = endIndex || ns.length - 1;
20341 for(var i = startIndex; i <= endIndex; i++){
20342 ns[i].nodeIndex = i;
20347 * Changes the data store this view uses and refresh the view.
20348 * @param {Store} store
20350 setStore : function(store, initial){
20351 if(!initial && this.store){
20352 this.store.un("datachanged", this.refresh);
20353 this.store.un("add", this.onAdd);
20354 this.store.un("remove", this.onRemove);
20355 this.store.un("update", this.onUpdate);
20356 this.store.un("clear", this.refresh);
20357 this.store.un("beforeload", this.onBeforeLoad);
20358 this.store.un("load", this.onLoad);
20359 this.store.un("loadexception", this.onLoad);
20363 store.on("datachanged", this.refresh, this);
20364 store.on("add", this.onAdd, this);
20365 store.on("remove", this.onRemove, this);
20366 store.on("update", this.onUpdate, this);
20367 store.on("clear", this.refresh, this);
20368 store.on("beforeload", this.onBeforeLoad, this);
20369 store.on("load", this.onLoad, this);
20370 store.on("loadexception", this.onLoad, this);
20378 * onbeforeLoad - masks the loading area.
20381 onBeforeLoad : function(store,opts)
20383 //Roo.log('onBeforeLoad');
20385 this.el.update("");
20387 this.el.mask(this.mask ? this.mask : "Loading" );
20389 onLoad : function ()
20396 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20397 * @param {HTMLElement} node
20398 * @return {HTMLElement} The template node
20400 findItemFromChild : function(node){
20401 var el = this.dataName ?
20402 this.el.child('.roo-tpl-' + this.dataName,true) :
20405 if(!node || node.parentNode == el){
20408 var p = node.parentNode;
20409 while(p && p != el){
20410 if(p.parentNode == el){
20419 onClick : function(e){
20420 var item = this.findItemFromChild(e.getTarget());
20422 var index = this.indexOf(item);
20423 if(this.onItemClick(item, index, e) !== false){
20424 this.fireEvent("click", this, index, item, e);
20427 this.clearSelections();
20432 onContextMenu : function(e){
20433 var item = this.findItemFromChild(e.getTarget());
20435 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20440 onDblClick : function(e){
20441 var item = this.findItemFromChild(e.getTarget());
20443 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20447 onItemClick : function(item, index, e)
20449 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20452 if (this.toggleSelect) {
20453 var m = this.isSelected(item) ? 'unselect' : 'select';
20456 _t[m](item, true, false);
20459 if(this.multiSelect || this.singleSelect){
20460 if(this.multiSelect && e.shiftKey && this.lastSelection){
20461 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20463 this.select(item, this.multiSelect && e.ctrlKey);
20464 this.lastSelection = item;
20467 if(!this.tickable){
20468 e.preventDefault();
20476 * Get the number of selected nodes.
20479 getSelectionCount : function(){
20480 return this.selections.length;
20484 * Get the currently selected nodes.
20485 * @return {Array} An array of HTMLElements
20487 getSelectedNodes : function(){
20488 return this.selections;
20492 * Get the indexes of the selected nodes.
20495 getSelectedIndexes : function(){
20496 var indexes = [], s = this.selections;
20497 for(var i = 0, len = s.length; i < len; i++){
20498 indexes.push(s[i].nodeIndex);
20504 * Clear all selections
20505 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20507 clearSelections : function(suppressEvent){
20508 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20509 this.cmp.elements = this.selections;
20510 this.cmp.removeClass(this.selectedClass);
20511 this.selections = [];
20512 if(!suppressEvent){
20513 this.fireEvent("selectionchange", this, this.selections);
20519 * Returns true if the passed node is selected
20520 * @param {HTMLElement/Number} node The node or node index
20521 * @return {Boolean}
20523 isSelected : function(node){
20524 var s = this.selections;
20528 node = this.getNode(node);
20529 return s.indexOf(node) !== -1;
20534 * @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
20535 * @param {Boolean} keepExisting (optional) true to keep existing selections
20536 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20538 select : function(nodeInfo, keepExisting, suppressEvent){
20539 if(nodeInfo instanceof Array){
20541 this.clearSelections(true);
20543 for(var i = 0, len = nodeInfo.length; i < len; i++){
20544 this.select(nodeInfo[i], true, true);
20548 var node = this.getNode(nodeInfo);
20549 if(!node || this.isSelected(node)){
20550 return; // already selected.
20553 this.clearSelections(true);
20556 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20557 Roo.fly(node).addClass(this.selectedClass);
20558 this.selections.push(node);
20559 if(!suppressEvent){
20560 this.fireEvent("selectionchange", this, this.selections);
20568 * @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
20569 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20570 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20572 unselect : function(nodeInfo, keepExisting, suppressEvent)
20574 if(nodeInfo instanceof Array){
20575 Roo.each(this.selections, function(s) {
20576 this.unselect(s, nodeInfo);
20580 var node = this.getNode(nodeInfo);
20581 if(!node || !this.isSelected(node)){
20582 //Roo.log("not selected");
20583 return; // not selected.
20587 Roo.each(this.selections, function(s) {
20589 Roo.fly(node).removeClass(this.selectedClass);
20596 this.selections= ns;
20597 this.fireEvent("selectionchange", this, this.selections);
20601 * Gets a template node.
20602 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20603 * @return {HTMLElement} The node or null if it wasn't found
20605 getNode : function(nodeInfo){
20606 if(typeof nodeInfo == "string"){
20607 return document.getElementById(nodeInfo);
20608 }else if(typeof nodeInfo == "number"){
20609 return this.nodes[nodeInfo];
20615 * Gets a range template nodes.
20616 * @param {Number} startIndex
20617 * @param {Number} endIndex
20618 * @return {Array} An array of nodes
20620 getNodes : function(start, end){
20621 var ns = this.nodes;
20622 start = start || 0;
20623 end = typeof end == "undefined" ? ns.length - 1 : end;
20626 for(var i = start; i <= end; i++){
20630 for(var i = start; i >= end; i--){
20638 * Finds the index of the passed node
20639 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20640 * @return {Number} The index of the node or -1
20642 indexOf : function(node){
20643 node = this.getNode(node);
20644 if(typeof node.nodeIndex == "number"){
20645 return node.nodeIndex;
20647 var ns = this.nodes;
20648 for(var i = 0, len = ns.length; i < len; i++){
20659 * based on jquery fullcalendar
20663 Roo.bootstrap = Roo.bootstrap || {};
20665 * @class Roo.bootstrap.Calendar
20666 * @extends Roo.bootstrap.Component
20667 * Bootstrap Calendar class
20668 * @cfg {Boolean} loadMask (true|false) default false
20669 * @cfg {Object} header generate the user specific header of the calendar, default false
20672 * Create a new Container
20673 * @param {Object} config The config object
20678 Roo.bootstrap.Calendar = function(config){
20679 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20683 * Fires when a date is selected
20684 * @param {DatePicker} this
20685 * @param {Date} date The selected date
20689 * @event monthchange
20690 * Fires when the displayed month changes
20691 * @param {DatePicker} this
20692 * @param {Date} date The selected month
20694 'monthchange': true,
20696 * @event evententer
20697 * Fires when mouse over an event
20698 * @param {Calendar} this
20699 * @param {event} Event
20701 'evententer': true,
20703 * @event eventleave
20704 * Fires when the mouse leaves an
20705 * @param {Calendar} this
20708 'eventleave': true,
20710 * @event eventclick
20711 * Fires when the mouse click an
20712 * @param {Calendar} this
20721 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20724 * @cfg {Roo.data.Store} store
20725 * The data source for the calendar
20729 * @cfg {Number} startDay
20730 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20738 getAutoCreate : function(){
20741 var fc_button = function(name, corner, style, content ) {
20742 return Roo.apply({},{
20744 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20746 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20749 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20760 style : 'width:100%',
20767 cls : 'fc-header-left',
20769 fc_button('prev', 'left', 'arrow', '‹' ),
20770 fc_button('next', 'right', 'arrow', '›' ),
20771 { tag: 'span', cls: 'fc-header-space' },
20772 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20780 cls : 'fc-header-center',
20784 cls: 'fc-header-title',
20787 html : 'month / year'
20795 cls : 'fc-header-right',
20797 /* fc_button('month', 'left', '', 'month' ),
20798 fc_button('week', '', '', 'week' ),
20799 fc_button('day', 'right', '', 'day' )
20811 header = this.header;
20814 var cal_heads = function() {
20816 // fixme - handle this.
20818 for (var i =0; i < Date.dayNames.length; i++) {
20819 var d = Date.dayNames[i];
20822 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20823 html : d.substring(0,3)
20827 ret[0].cls += ' fc-first';
20828 ret[6].cls += ' fc-last';
20831 var cal_cell = function(n) {
20834 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20839 cls: 'fc-day-number',
20843 cls: 'fc-day-content',
20847 style: 'position: relative;' // height: 17px;
20859 var cal_rows = function() {
20862 for (var r = 0; r < 6; r++) {
20869 for (var i =0; i < Date.dayNames.length; i++) {
20870 var d = Date.dayNames[i];
20871 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20874 row.cn[0].cls+=' fc-first';
20875 row.cn[0].cn[0].style = 'min-height:90px';
20876 row.cn[6].cls+=' fc-last';
20880 ret[0].cls += ' fc-first';
20881 ret[4].cls += ' fc-prev-last';
20882 ret[5].cls += ' fc-last';
20889 cls: 'fc-border-separate',
20890 style : 'width:100%',
20898 cls : 'fc-first fc-last',
20916 cls : 'fc-content',
20917 style : "position: relative;",
20920 cls : 'fc-view fc-view-month fc-grid',
20921 style : 'position: relative',
20922 unselectable : 'on',
20925 cls : 'fc-event-container',
20926 style : 'position:absolute;z-index:8;top:0;left:0;'
20944 initEvents : function()
20947 throw "can not find store for calendar";
20953 style: "text-align:center",
20957 style: "background-color:white;width:50%;margin:250 auto",
20961 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20972 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20974 var size = this.el.select('.fc-content', true).first().getSize();
20975 this.maskEl.setSize(size.width, size.height);
20976 this.maskEl.enableDisplayMode("block");
20977 if(!this.loadMask){
20978 this.maskEl.hide();
20981 this.store = Roo.factory(this.store, Roo.data);
20982 this.store.on('load', this.onLoad, this);
20983 this.store.on('beforeload', this.onBeforeLoad, this);
20987 this.cells = this.el.select('.fc-day',true);
20988 //Roo.log(this.cells);
20989 this.textNodes = this.el.query('.fc-day-number');
20990 this.cells.addClassOnOver('fc-state-hover');
20992 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20993 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20994 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20995 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20997 this.on('monthchange', this.onMonthChange, this);
20999 this.update(new Date().clearTime());
21002 resize : function() {
21003 var sz = this.el.getSize();
21005 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21006 this.el.select('.fc-day-content div',true).setHeight(34);
21011 showPrevMonth : function(e){
21012 this.update(this.activeDate.add("mo", -1));
21014 showToday : function(e){
21015 this.update(new Date().clearTime());
21018 showNextMonth : function(e){
21019 this.update(this.activeDate.add("mo", 1));
21023 showPrevYear : function(){
21024 this.update(this.activeDate.add("y", -1));
21028 showNextYear : function(){
21029 this.update(this.activeDate.add("y", 1));
21034 update : function(date)
21036 var vd = this.activeDate;
21037 this.activeDate = date;
21038 // if(vd && this.el){
21039 // var t = date.getTime();
21040 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21041 // Roo.log('using add remove');
21043 // this.fireEvent('monthchange', this, date);
21045 // this.cells.removeClass("fc-state-highlight");
21046 // this.cells.each(function(c){
21047 // if(c.dateValue == t){
21048 // c.addClass("fc-state-highlight");
21049 // setTimeout(function(){
21050 // try{c.dom.firstChild.focus();}catch(e){}
21060 var days = date.getDaysInMonth();
21062 var firstOfMonth = date.getFirstDateOfMonth();
21063 var startingPos = firstOfMonth.getDay()-this.startDay;
21065 if(startingPos < this.startDay){
21069 var pm = date.add(Date.MONTH, -1);
21070 var prevStart = pm.getDaysInMonth()-startingPos;
21072 this.cells = this.el.select('.fc-day',true);
21073 this.textNodes = this.el.query('.fc-day-number');
21074 this.cells.addClassOnOver('fc-state-hover');
21076 var cells = this.cells.elements;
21077 var textEls = this.textNodes;
21079 Roo.each(cells, function(cell){
21080 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21083 days += startingPos;
21085 // convert everything to numbers so it's fast
21086 var day = 86400000;
21087 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21090 //Roo.log(prevStart);
21092 var today = new Date().clearTime().getTime();
21093 var sel = date.clearTime().getTime();
21094 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21095 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21096 var ddMatch = this.disabledDatesRE;
21097 var ddText = this.disabledDatesText;
21098 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21099 var ddaysText = this.disabledDaysText;
21100 var format = this.format;
21102 var setCellClass = function(cal, cell){
21106 //Roo.log('set Cell Class');
21108 var t = d.getTime();
21112 cell.dateValue = t;
21114 cell.className += " fc-today";
21115 cell.className += " fc-state-highlight";
21116 cell.title = cal.todayText;
21119 // disable highlight in other month..
21120 //cell.className += " fc-state-highlight";
21125 cell.className = " fc-state-disabled";
21126 cell.title = cal.minText;
21130 cell.className = " fc-state-disabled";
21131 cell.title = cal.maxText;
21135 if(ddays.indexOf(d.getDay()) != -1){
21136 cell.title = ddaysText;
21137 cell.className = " fc-state-disabled";
21140 if(ddMatch && format){
21141 var fvalue = d.dateFormat(format);
21142 if(ddMatch.test(fvalue)){
21143 cell.title = ddText.replace("%0", fvalue);
21144 cell.className = " fc-state-disabled";
21148 if (!cell.initialClassName) {
21149 cell.initialClassName = cell.dom.className;
21152 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21157 for(; i < startingPos; i++) {
21158 textEls[i].innerHTML = (++prevStart);
21159 d.setDate(d.getDate()+1);
21161 cells[i].className = "fc-past fc-other-month";
21162 setCellClass(this, cells[i]);
21167 for(; i < days; i++){
21168 intDay = i - startingPos + 1;
21169 textEls[i].innerHTML = (intDay);
21170 d.setDate(d.getDate()+1);
21172 cells[i].className = ''; // "x-date-active";
21173 setCellClass(this, cells[i]);
21177 for(; i < 42; i++) {
21178 textEls[i].innerHTML = (++extraDays);
21179 d.setDate(d.getDate()+1);
21181 cells[i].className = "fc-future fc-other-month";
21182 setCellClass(this, cells[i]);
21185 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21187 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21189 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21190 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21192 if(totalRows != 6){
21193 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21194 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21197 this.fireEvent('monthchange', this, date);
21201 if(!this.internalRender){
21202 var main = this.el.dom.firstChild;
21203 var w = main.offsetWidth;
21204 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21205 Roo.fly(main).setWidth(w);
21206 this.internalRender = true;
21207 // opera does not respect the auto grow header center column
21208 // then, after it gets a width opera refuses to recalculate
21209 // without a second pass
21210 if(Roo.isOpera && !this.secondPass){
21211 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21212 this.secondPass = true;
21213 this.update.defer(10, this, [date]);
21220 findCell : function(dt) {
21221 dt = dt.clearTime().getTime();
21223 this.cells.each(function(c){
21224 //Roo.log("check " +c.dateValue + '?=' + dt);
21225 if(c.dateValue == dt){
21235 findCells : function(ev) {
21236 var s = ev.start.clone().clearTime().getTime();
21238 var e= ev.end.clone().clearTime().getTime();
21241 this.cells.each(function(c){
21242 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21244 if(c.dateValue > e){
21247 if(c.dateValue < s){
21256 // findBestRow: function(cells)
21260 // for (var i =0 ; i < cells.length;i++) {
21261 // ret = Math.max(cells[i].rows || 0,ret);
21268 addItem : function(ev)
21270 // look for vertical location slot in
21271 var cells = this.findCells(ev);
21273 // ev.row = this.findBestRow(cells);
21275 // work out the location.
21279 for(var i =0; i < cells.length; i++) {
21281 cells[i].row = cells[0].row;
21284 cells[i].row = cells[i].row + 1;
21294 if (crow.start.getY() == cells[i].getY()) {
21296 crow.end = cells[i];
21313 cells[0].events.push(ev);
21315 this.calevents.push(ev);
21318 clearEvents: function() {
21320 if(!this.calevents){
21324 Roo.each(this.cells.elements, function(c){
21330 Roo.each(this.calevents, function(e) {
21331 Roo.each(e.els, function(el) {
21332 el.un('mouseenter' ,this.onEventEnter, this);
21333 el.un('mouseleave' ,this.onEventLeave, this);
21338 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21344 renderEvents: function()
21348 this.cells.each(function(c) {
21357 if(c.row != c.events.length){
21358 r = 4 - (4 - (c.row - c.events.length));
21361 c.events = ev.slice(0, r);
21362 c.more = ev.slice(r);
21364 if(c.more.length && c.more.length == 1){
21365 c.events.push(c.more.pop());
21368 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21372 this.cells.each(function(c) {
21374 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21377 for (var e = 0; e < c.events.length; e++){
21378 var ev = c.events[e];
21379 var rows = ev.rows;
21381 for(var i = 0; i < rows.length; i++) {
21383 // how many rows should it span..
21386 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21387 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21389 unselectable : "on",
21392 cls: 'fc-event-inner',
21396 // cls: 'fc-event-time',
21397 // html : cells.length > 1 ? '' : ev.time
21401 cls: 'fc-event-title',
21402 html : String.format('{0}', ev.title)
21409 cls: 'ui-resizable-handle ui-resizable-e',
21410 html : '  '
21417 cfg.cls += ' fc-event-start';
21419 if ((i+1) == rows.length) {
21420 cfg.cls += ' fc-event-end';
21423 var ctr = _this.el.select('.fc-event-container',true).first();
21424 var cg = ctr.createChild(cfg);
21426 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21427 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21429 var r = (c.more.length) ? 1 : 0;
21430 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21431 cg.setWidth(ebox.right - sbox.x -2);
21433 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21434 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21435 cg.on('click', _this.onEventClick, _this, ev);
21446 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21447 style : 'position: absolute',
21448 unselectable : "on",
21451 cls: 'fc-event-inner',
21455 cls: 'fc-event-title',
21463 cls: 'ui-resizable-handle ui-resizable-e',
21464 html : '  '
21470 var ctr = _this.el.select('.fc-event-container',true).first();
21471 var cg = ctr.createChild(cfg);
21473 var sbox = c.select('.fc-day-content',true).first().getBox();
21474 var ebox = c.select('.fc-day-content',true).first().getBox();
21476 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21477 cg.setWidth(ebox.right - sbox.x -2);
21479 cg.on('click', _this.onMoreEventClick, _this, c.more);
21489 onEventEnter: function (e, el,event,d) {
21490 this.fireEvent('evententer', this, el, event);
21493 onEventLeave: function (e, el,event,d) {
21494 this.fireEvent('eventleave', this, el, event);
21497 onEventClick: function (e, el,event,d) {
21498 this.fireEvent('eventclick', this, el, event);
21501 onMonthChange: function () {
21505 onMoreEventClick: function(e, el, more)
21509 this.calpopover.placement = 'right';
21510 this.calpopover.setTitle('More');
21512 this.calpopover.setContent('');
21514 var ctr = this.calpopover.el.select('.popover-content', true).first();
21516 Roo.each(more, function(m){
21518 cls : 'fc-event-hori fc-event-draggable',
21521 var cg = ctr.createChild(cfg);
21523 cg.on('click', _this.onEventClick, _this, m);
21526 this.calpopover.show(el);
21531 onLoad: function ()
21533 this.calevents = [];
21536 if(this.store.getCount() > 0){
21537 this.store.data.each(function(d){
21540 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21541 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21542 time : d.data.start_time,
21543 title : d.data.title,
21544 description : d.data.description,
21545 venue : d.data.venue
21550 this.renderEvents();
21552 if(this.calevents.length && this.loadMask){
21553 this.maskEl.hide();
21557 onBeforeLoad: function()
21559 this.clearEvents();
21561 this.maskEl.show();
21575 * @class Roo.bootstrap.Popover
21576 * @extends Roo.bootstrap.Component
21577 * @parent none builder
21578 * @children Roo.bootstrap.Component
21579 * Bootstrap Popover class
21580 * @cfg {String} html contents of the popover (or false to use children..)
21581 * @cfg {String} title of popover (or false to hide)
21582 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21583 * @cfg {String} trigger click || hover (or false to trigger manually)
21584 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21585 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21586 * - if false and it has a 'parent' then it will be automatically added to that element
21587 * - if string - Roo.get will be called
21588 * @cfg {Number} delay - delay before showing
21591 * Create a new Popover
21592 * @param {Object} config The config object
21595 Roo.bootstrap.Popover = function(config){
21596 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21602 * After the popover show
21604 * @param {Roo.bootstrap.Popover} this
21609 * After the popover hide
21611 * @param {Roo.bootstrap.Popover} this
21617 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21622 placement : 'right',
21623 trigger : 'hover', // hover
21629 can_build_overlaid : false,
21631 maskEl : false, // the mask element
21634 alignEl : false, // when show is called with an element - this get's stored.
21636 getChildContainer : function()
21638 return this.contentEl;
21641 getPopoverHeader : function()
21643 this.title = true; // flag not to hide it..
21644 this.headerEl.addClass('p-0');
21645 return this.headerEl
21649 getAutoCreate : function(){
21652 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21653 style: 'display:block',
21659 cls : 'popover-inner ',
21663 cls: 'popover-title popover-header',
21664 html : this.title === false ? '' : this.title
21667 cls : 'popover-content popover-body ' + (this.cls || ''),
21668 html : this.html || ''
21679 * @param {string} the title
21681 setTitle: function(str)
21685 this.headerEl.dom.innerHTML = str;
21690 * @param {string} the body content
21692 setContent: function(str)
21695 if (this.contentEl) {
21696 this.contentEl.dom.innerHTML = str;
21700 // as it get's added to the bottom of the page.
21701 onRender : function(ct, position)
21703 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21708 var cfg = Roo.apply({}, this.getAutoCreate());
21712 cfg.cls += ' ' + this.cls;
21715 cfg.style = this.style;
21717 //Roo.log("adding to ");
21718 this.el = Roo.get(document.body).createChild(cfg, position);
21719 // Roo.log(this.el);
21722 this.contentEl = this.el.select('.popover-content',true).first();
21723 this.headerEl = this.el.select('.popover-title',true).first();
21726 if(typeof(this.items) != 'undefined'){
21727 var items = this.items;
21730 for(var i =0;i < items.length;i++) {
21731 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21735 this.items = nitems;
21737 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21738 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21745 resizeMask : function()
21747 this.maskEl.setSize(
21748 Roo.lib.Dom.getViewWidth(true),
21749 Roo.lib.Dom.getViewHeight(true)
21753 initEvents : function()
21757 Roo.bootstrap.Popover.register(this);
21760 this.arrowEl = this.el.select('.arrow',true).first();
21761 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21762 this.el.enableDisplayMode('block');
21766 if (this.over === false && !this.parent()) {
21769 if (this.triggers === false) {
21774 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21775 var triggers = this.trigger ? this.trigger.split(' ') : [];
21776 Roo.each(triggers, function(trigger) {
21778 if (trigger == 'click') {
21779 on_el.on('click', this.toggle, this);
21780 } else if (trigger != 'manual') {
21781 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21782 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21784 on_el.on(eventIn ,this.enter, this);
21785 on_el.on(eventOut, this.leave, this);
21795 toggle : function () {
21796 this.hoverState == 'in' ? this.leave() : this.enter();
21799 enter : function () {
21801 clearTimeout(this.timeout);
21803 this.hoverState = 'in';
21805 if (!this.delay || !this.delay.show) {
21810 this.timeout = setTimeout(function () {
21811 if (_t.hoverState == 'in') {
21814 }, this.delay.show)
21817 leave : function() {
21818 clearTimeout(this.timeout);
21820 this.hoverState = 'out';
21822 if (!this.delay || !this.delay.hide) {
21827 this.timeout = setTimeout(function () {
21828 if (_t.hoverState == 'out') {
21831 }, this.delay.hide)
21835 * update the position of the dialog
21836 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21841 doAlign : function()
21844 if (this.alignEl) {
21845 this.updatePosition(this.placement, true);
21848 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21849 var es = this.el.getSize();
21850 var x = Roo.lib.Dom.getViewWidth()/2;
21851 var y = Roo.lib.Dom.getViewHeight()/2;
21852 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21864 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21865 * @param {string} (left|right|top|bottom) position
21867 show : function (on_el, placement)
21869 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21870 on_el = on_el || false; // default to false
21873 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21874 on_el = this.parent().el;
21875 } else if (this.over) {
21876 on_el = Roo.get(this.over);
21881 this.alignEl = Roo.get( on_el );
21884 this.render(document.body);
21890 if (this.title === false) {
21891 this.headerEl.hide();
21896 this.el.dom.style.display = 'block';
21900 //var arrow = this.el.select('.arrow',true).first();
21901 //arrow.set(align[2],
21903 this.el.addClass('in');
21907 this.hoverState = 'in';
21910 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21911 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21912 this.maskEl.dom.style.display = 'block';
21913 this.maskEl.addClass('show');
21915 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21917 this.fireEvent('show', this);
21921 * fire this manually after loading a grid in the table for example
21922 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21923 * @param {Boolean} try and move it if we cant get right position.
21925 updatePosition : function(placement, try_move)
21927 // allow for calling with no parameters
21928 placement = placement ? placement : this.placement;
21929 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21931 this.el.removeClass([
21932 'fade','top','bottom', 'left', 'right','in',
21933 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21935 this.el.addClass(placement + ' bs-popover-' + placement);
21937 if (!this.alignEl ) {
21941 switch (placement) {
21943 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21944 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21945 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21946 //normal display... or moved up/down.
21947 this.el.setXY(offset);
21948 var xy = this.alignEl.getAnchorXY('tr', false);
21950 this.arrowEl.setXY(xy);
21953 // continue through...
21954 return this.updatePosition('left', false);
21958 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21959 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21960 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21961 //normal display... or moved up/down.
21962 this.el.setXY(offset);
21963 var xy = this.alignEl.getAnchorXY('tl', false);
21964 xy[0]-=10;xy[1]+=5; // << fix me
21965 this.arrowEl.setXY(xy);
21969 return this.updatePosition('right', false);
21972 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21973 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21974 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21975 //normal display... or moved up/down.
21976 this.el.setXY(offset);
21977 var xy = this.alignEl.getAnchorXY('t', false);
21978 xy[1]-=10; // << fix me
21979 this.arrowEl.setXY(xy);
21983 return this.updatePosition('bottom', false);
21986 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21987 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21988 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21989 //normal display... or moved up/down.
21990 this.el.setXY(offset);
21991 var xy = this.alignEl.getAnchorXY('b', false);
21992 xy[1]+=2; // << fix me
21993 this.arrowEl.setXY(xy);
21997 return this.updatePosition('top', false);
22008 this.el.setXY([0,0]);
22009 this.el.removeClass('in');
22011 this.hoverState = null;
22012 this.maskEl.hide(); // always..
22013 this.fireEvent('hide', this);
22019 Roo.apply(Roo.bootstrap.Popover, {
22022 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22023 'right' : ['l-br', [10,0], 'right bs-popover-right'],
22024 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22025 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22030 clickHander : false,
22034 onMouseDown : function(e)
22036 if (this.popups.length && !e.getTarget(".roo-popover")) {
22037 /// what is nothing is showing..
22046 register : function(popup)
22048 if (!Roo.bootstrap.Popover.clickHandler) {
22049 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22051 // hide other popups.
22052 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22053 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22054 this.hideAll(); //<< why?
22055 //this.popups.push(popup);
22057 hideAll : function()
22059 this.popups.forEach(function(p) {
22063 onShow : function() {
22064 Roo.bootstrap.Popover.popups.push(this);
22066 onHide : function() {
22067 Roo.bootstrap.Popover.popups.remove(this);
22072 * @class Roo.bootstrap.PopoverNav
22073 * @extends Roo.bootstrap.nav.Simplebar
22074 * @parent Roo.bootstrap.Popover
22075 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22077 * Bootstrap Popover header navigation class
22078 * FIXME? should this go under nav?
22082 * Create a new Popover Header Navigation
22083 * @param {Object} config The config object
22086 Roo.bootstrap.PopoverNav = function(config){
22087 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22090 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22093 container_method : 'getPopoverHeader'
22111 * @class Roo.bootstrap.Progress
22112 * @extends Roo.bootstrap.Component
22113 * @children Roo.bootstrap.ProgressBar
22114 * Bootstrap Progress class
22115 * @cfg {Boolean} striped striped of the progress bar
22116 * @cfg {Boolean} active animated of the progress bar
22120 * Create a new Progress
22121 * @param {Object} config The config object
22124 Roo.bootstrap.Progress = function(config){
22125 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22128 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22133 getAutoCreate : function(){
22141 cfg.cls += ' progress-striped';
22145 cfg.cls += ' active';
22164 * @class Roo.bootstrap.ProgressBar
22165 * @extends Roo.bootstrap.Component
22166 * Bootstrap ProgressBar class
22167 * @cfg {Number} aria_valuenow aria-value now
22168 * @cfg {Number} aria_valuemin aria-value min
22169 * @cfg {Number} aria_valuemax aria-value max
22170 * @cfg {String} label label for the progress bar
22171 * @cfg {String} panel (success | info | warning | danger )
22172 * @cfg {String} role role of the progress bar
22173 * @cfg {String} sr_only text
22177 * Create a new ProgressBar
22178 * @param {Object} config The config object
22181 Roo.bootstrap.ProgressBar = function(config){
22182 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22185 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22189 aria_valuemax : 100,
22195 getAutoCreate : function()
22200 cls: 'progress-bar',
22201 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22213 cfg.role = this.role;
22216 if(this.aria_valuenow){
22217 cfg['aria-valuenow'] = this.aria_valuenow;
22220 if(this.aria_valuemin){
22221 cfg['aria-valuemin'] = this.aria_valuemin;
22224 if(this.aria_valuemax){
22225 cfg['aria-valuemax'] = this.aria_valuemax;
22228 if(this.label && !this.sr_only){
22229 cfg.html = this.label;
22233 cfg.cls += ' progress-bar-' + this.panel;
22239 update : function(aria_valuenow)
22241 this.aria_valuenow = aria_valuenow;
22243 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22251 * @class Roo.bootstrap.TabGroup
22252 * @extends Roo.bootstrap.Column
22253 * @children Roo.bootstrap.TabPanel
22254 * Bootstrap Column class
22255 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22256 * @cfg {Boolean} carousel true to make the group behave like a carousel
22257 * @cfg {Boolean} bullets show bullets for the panels
22258 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22259 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22260 * @cfg {Boolean} showarrow (true|false) show arrow default true
22263 * Create a new TabGroup
22264 * @param {Object} config The config object
22267 Roo.bootstrap.TabGroup = function(config){
22268 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22270 this.navId = Roo.id();
22273 Roo.bootstrap.TabGroup.register(this);
22277 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22280 transition : false,
22285 slideOnTouch : false,
22288 getAutoCreate : function()
22290 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22292 cfg.cls += ' tab-content';
22294 if (this.carousel) {
22295 cfg.cls += ' carousel slide';
22298 cls : 'carousel-inner',
22302 if(this.bullets && !Roo.isTouch){
22305 cls : 'carousel-bullets',
22309 if(this.bullets_cls){
22310 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22317 cfg.cn[0].cn.push(bullets);
22320 if(this.showarrow){
22321 cfg.cn[0].cn.push({
22323 class : 'carousel-arrow',
22327 class : 'carousel-prev',
22331 class : 'fa fa-chevron-left'
22337 class : 'carousel-next',
22341 class : 'fa fa-chevron-right'
22354 initEvents: function()
22356 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22357 // this.el.on("touchstart", this.onTouchStart, this);
22360 if(this.autoslide){
22363 this.slideFn = window.setInterval(function() {
22364 _this.showPanelNext();
22368 if(this.showarrow){
22369 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22370 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22376 // onTouchStart : function(e, el, o)
22378 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22382 // this.showPanelNext();
22386 getChildContainer : function()
22388 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22392 * register a Navigation item
22393 * @param {Roo.bootstrap.nav.Item} the navitem to add
22395 register : function(item)
22397 this.tabs.push( item);
22398 item.navId = this.navId; // not really needed..
22403 getActivePanel : function()
22406 Roo.each(this.tabs, function(t) {
22416 getPanelByName : function(n)
22419 Roo.each(this.tabs, function(t) {
22420 if (t.tabId == n) {
22428 indexOfPanel : function(p)
22431 Roo.each(this.tabs, function(t,i) {
22432 if (t.tabId == p.tabId) {
22441 * show a specific panel
22442 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22443 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22445 showPanel : function (pan)
22447 if(this.transition || typeof(pan) == 'undefined'){
22448 Roo.log("waiting for the transitionend");
22452 if (typeof(pan) == 'number') {
22453 pan = this.tabs[pan];
22456 if (typeof(pan) == 'string') {
22457 pan = this.getPanelByName(pan);
22460 var cur = this.getActivePanel();
22463 Roo.log('pan or acitve pan is undefined');
22467 if (pan.tabId == this.getActivePanel().tabId) {
22471 if (false === cur.fireEvent('beforedeactivate')) {
22475 if(this.bullets > 0 && !Roo.isTouch){
22476 this.setActiveBullet(this.indexOfPanel(pan));
22479 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22481 //class="carousel-item carousel-item-next carousel-item-left"
22483 this.transition = true;
22484 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22485 var lr = dir == 'next' ? 'left' : 'right';
22486 pan.el.addClass(dir); // or prev
22487 pan.el.addClass('carousel-item-' + dir); // or prev
22488 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22489 cur.el.addClass(lr); // or right
22490 pan.el.addClass(lr);
22491 cur.el.addClass('carousel-item-' +lr); // or right
22492 pan.el.addClass('carousel-item-' +lr);
22496 cur.el.on('transitionend', function() {
22497 Roo.log("trans end?");
22499 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22500 pan.setActive(true);
22502 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22503 cur.setActive(false);
22505 _this.transition = false;
22507 }, this, { single: true } );
22512 cur.setActive(false);
22513 pan.setActive(true);
22518 showPanelNext : function()
22520 var i = this.indexOfPanel(this.getActivePanel());
22522 if (i >= this.tabs.length - 1 && !this.autoslide) {
22526 if (i >= this.tabs.length - 1 && this.autoslide) {
22530 this.showPanel(this.tabs[i+1]);
22533 showPanelPrev : function()
22535 var i = this.indexOfPanel(this.getActivePanel());
22537 if (i < 1 && !this.autoslide) {
22541 if (i < 1 && this.autoslide) {
22542 i = this.tabs.length;
22545 this.showPanel(this.tabs[i-1]);
22549 addBullet: function()
22551 if(!this.bullets || Roo.isTouch){
22554 var ctr = this.el.select('.carousel-bullets',true).first();
22555 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22556 var bullet = ctr.createChild({
22557 cls : 'bullet bullet-' + i
22558 },ctr.dom.lastChild);
22563 bullet.on('click', (function(e, el, o, ii, t){
22565 e.preventDefault();
22567 this.showPanel(ii);
22569 if(this.autoslide && this.slideFn){
22570 clearInterval(this.slideFn);
22571 this.slideFn = window.setInterval(function() {
22572 _this.showPanelNext();
22576 }).createDelegate(this, [i, bullet], true));
22581 setActiveBullet : function(i)
22587 Roo.each(this.el.select('.bullet', true).elements, function(el){
22588 el.removeClass('selected');
22591 var bullet = this.el.select('.bullet-' + i, true).first();
22597 bullet.addClass('selected');
22608 Roo.apply(Roo.bootstrap.TabGroup, {
22612 * register a Navigation Group
22613 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22615 register : function(navgrp)
22617 this.groups[navgrp.navId] = navgrp;
22621 * fetch a Navigation Group based on the navigation ID
22622 * if one does not exist , it will get created.
22623 * @param {string} the navgroup to add
22624 * @returns {Roo.bootstrap.nav.Group} the navgroup
22626 get: function(navId) {
22627 if (typeof(this.groups[navId]) == 'undefined') {
22628 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22630 return this.groups[navId] ;
22645 * @class Roo.bootstrap.TabPanel
22646 * @extends Roo.bootstrap.Component
22647 * @children Roo.bootstrap.Component
22648 * Bootstrap TabPanel class
22649 * @cfg {Boolean} active panel active
22650 * @cfg {String} html panel content
22651 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22652 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22653 * @cfg {String} href click to link..
22654 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22658 * Create a new TabPanel
22659 * @param {Object} config The config object
22662 Roo.bootstrap.TabPanel = function(config){
22663 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22667 * Fires when the active status changes
22668 * @param {Roo.bootstrap.TabPanel} this
22669 * @param {Boolean} state the new state
22674 * @event beforedeactivate
22675 * Fires before a tab is de-activated - can be used to do validation on a form.
22676 * @param {Roo.bootstrap.TabPanel} this
22677 * @return {Boolean} false if there is an error
22680 'beforedeactivate': true
22683 this.tabId = this.tabId || Roo.id();
22687 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22694 touchSlide : false,
22695 getAutoCreate : function(){
22700 // item is needed for carousel - not sure if it has any effect otherwise
22701 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22702 html: this.html || ''
22706 cfg.cls += ' active';
22710 cfg.tabId = this.tabId;
22718 initEvents: function()
22720 var p = this.parent();
22722 this.navId = this.navId || p.navId;
22724 if (typeof(this.navId) != 'undefined') {
22725 // not really needed.. but just in case.. parent should be a NavGroup.
22726 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22730 var i = tg.tabs.length - 1;
22732 if(this.active && tg.bullets > 0 && i < tg.bullets){
22733 tg.setActiveBullet(i);
22737 this.el.on('click', this.onClick, this);
22739 if(Roo.isTouch && this.touchSlide){
22740 this.el.on("touchstart", this.onTouchStart, this);
22741 this.el.on("touchmove", this.onTouchMove, this);
22742 this.el.on("touchend", this.onTouchEnd, this);
22747 onRender : function(ct, position)
22749 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22752 setActive : function(state)
22754 Roo.log("panel - set active " + this.tabId + "=" + state);
22756 this.active = state;
22758 this.el.removeClass('active');
22760 } else if (!this.el.hasClass('active')) {
22761 this.el.addClass('active');
22764 this.fireEvent('changed', this, state);
22767 onClick : function(e)
22769 e.preventDefault();
22771 if(!this.href.length){
22775 window.location.href = this.href;
22784 onTouchStart : function(e)
22786 this.swiping = false;
22788 this.startX = e.browserEvent.touches[0].clientX;
22789 this.startY = e.browserEvent.touches[0].clientY;
22792 onTouchMove : function(e)
22794 this.swiping = true;
22796 this.endX = e.browserEvent.touches[0].clientX;
22797 this.endY = e.browserEvent.touches[0].clientY;
22800 onTouchEnd : function(e)
22807 var tabGroup = this.parent();
22809 if(this.endX > this.startX){ // swiping right
22810 tabGroup.showPanelPrev();
22814 if(this.startX > this.endX){ // swiping left
22815 tabGroup.showPanelNext();
22834 * @class Roo.bootstrap.form.DateField
22835 * @extends Roo.bootstrap.form.Input
22836 * Bootstrap DateField class
22837 * @cfg {Number} weekStart default 0
22838 * @cfg {String} viewMode default empty, (months|years)
22839 * @cfg {String} minViewMode default empty, (months|years)
22840 * @cfg {Number} startDate default -Infinity
22841 * @cfg {Number} endDate default Infinity
22842 * @cfg {Boolean} todayHighlight default false
22843 * @cfg {Boolean} todayBtn default false
22844 * @cfg {Boolean} calendarWeeks default false
22845 * @cfg {Object} daysOfWeekDisabled default empty
22846 * @cfg {Boolean} singleMode default false (true | false)
22848 * @cfg {Boolean} keyboardNavigation default true
22849 * @cfg {String} language default en
22852 * Create a new DateField
22853 * @param {Object} config The config object
22856 Roo.bootstrap.form.DateField = function(config){
22857 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22861 * Fires when this field show.
22862 * @param {Roo.bootstrap.form.DateField} this
22863 * @param {Mixed} date The date value
22868 * Fires when this field hide.
22869 * @param {Roo.bootstrap.form.DateField} this
22870 * @param {Mixed} date The date value
22875 * Fires when select a date.
22876 * @param {Roo.bootstrap.form.DateField} this
22877 * @param {Mixed} date The date value
22881 * @event beforeselect
22882 * Fires when before select a date.
22883 * @param {Roo.bootstrap.form.DateField} this
22884 * @param {Mixed} date The date value
22886 beforeselect : true
22890 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22893 * @cfg {String} format
22894 * The default date format string which can be overriden for localization support. The format must be
22895 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22899 * @cfg {String} altFormats
22900 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22901 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22903 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22911 todayHighlight : false,
22917 keyboardNavigation: true,
22919 calendarWeeks: false,
22921 startDate: -Infinity,
22925 daysOfWeekDisabled: [],
22929 singleMode : false,
22931 UTCDate: function()
22933 return new Date(Date.UTC.apply(Date, arguments));
22936 UTCToday: function()
22938 var today = new Date();
22939 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22942 getDate: function() {
22943 var d = this.getUTCDate();
22944 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22947 getUTCDate: function() {
22951 setDate: function(d) {
22952 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22955 setUTCDate: function(d) {
22957 this.setValue(this.formatDate(this.date));
22960 onRender: function(ct, position)
22963 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22965 this.language = this.language || 'en';
22966 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22967 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22969 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22970 this.format = this.format || 'm/d/y';
22971 this.isInline = false;
22972 this.isInput = true;
22973 this.component = this.el.select('.add-on', true).first() || false;
22974 this.component = (this.component && this.component.length === 0) ? false : this.component;
22975 this.hasInput = this.component && this.inputEl().length;
22977 if (typeof(this.minViewMode === 'string')) {
22978 switch (this.minViewMode) {
22980 this.minViewMode = 1;
22983 this.minViewMode = 2;
22986 this.minViewMode = 0;
22991 if (typeof(this.viewMode === 'string')) {
22992 switch (this.viewMode) {
23005 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23007 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23009 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23011 this.picker().on('mousedown', this.onMousedown, this);
23012 this.picker().on('click', this.onClick, this);
23014 this.picker().addClass('datepicker-dropdown');
23016 this.startViewMode = this.viewMode;
23018 if(this.singleMode){
23019 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23020 v.setVisibilityMode(Roo.Element.DISPLAY);
23024 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23025 v.setStyle('width', '189px');
23029 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23030 if(!this.calendarWeeks){
23035 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23036 v.attr('colspan', function(i, val){
23037 return parseInt(val) + 1;
23042 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23044 this.setStartDate(this.startDate);
23045 this.setEndDate(this.endDate);
23047 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23054 if(this.isInline) {
23059 picker : function()
23061 return this.pickerEl;
23062 // return this.el.select('.datepicker', true).first();
23065 fillDow: function()
23067 var dowCnt = this.weekStart;
23076 if(this.calendarWeeks){
23084 while (dowCnt < this.weekStart + 7) {
23088 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23092 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23095 fillMonths: function()
23098 var months = this.picker().select('>.datepicker-months td', true).first();
23100 months.dom.innerHTML = '';
23106 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23109 months.createChild(month);
23116 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;
23118 if (this.date < this.startDate) {
23119 this.viewDate = new Date(this.startDate);
23120 } else if (this.date > this.endDate) {
23121 this.viewDate = new Date(this.endDate);
23123 this.viewDate = new Date(this.date);
23131 var d = new Date(this.viewDate),
23132 year = d.getUTCFullYear(),
23133 month = d.getUTCMonth(),
23134 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23135 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23136 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23137 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23138 currentDate = this.date && this.date.valueOf(),
23139 today = this.UTCToday();
23141 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23143 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23145 // this.picker.select('>tfoot th.today').
23146 // .text(dates[this.language].today)
23147 // .toggle(this.todayBtn !== false);
23149 this.updateNavArrows();
23152 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23154 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23156 prevMonth.setUTCDate(day);
23158 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23160 var nextMonth = new Date(prevMonth);
23162 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23164 nextMonth = nextMonth.valueOf();
23166 var fillMonths = false;
23168 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23170 while(prevMonth.valueOf() <= nextMonth) {
23173 if (prevMonth.getUTCDay() === this.weekStart) {
23175 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23183 if(this.calendarWeeks){
23184 // ISO 8601: First week contains first thursday.
23185 // ISO also states week starts on Monday, but we can be more abstract here.
23187 // Start of current week: based on weekstart/current date
23188 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23189 // Thursday of this week
23190 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23191 // First Thursday of year, year from thursday
23192 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23193 // Calendar week: ms between thursdays, div ms per day, div 7 days
23194 calWeek = (th - yth) / 864e5 / 7 + 1;
23196 fillMonths.cn.push({
23204 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23206 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23209 if (this.todayHighlight &&
23210 prevMonth.getUTCFullYear() == today.getFullYear() &&
23211 prevMonth.getUTCMonth() == today.getMonth() &&
23212 prevMonth.getUTCDate() == today.getDate()) {
23213 clsName += ' today';
23216 if (currentDate && prevMonth.valueOf() === currentDate) {
23217 clsName += ' active';
23220 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23221 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23222 clsName += ' disabled';
23225 fillMonths.cn.push({
23227 cls: 'day ' + clsName,
23228 html: prevMonth.getDate()
23231 prevMonth.setDate(prevMonth.getDate()+1);
23234 var currentYear = this.date && this.date.getUTCFullYear();
23235 var currentMonth = this.date && this.date.getUTCMonth();
23237 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23239 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23240 v.removeClass('active');
23242 if(currentYear === year && k === currentMonth){
23243 v.addClass('active');
23246 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23247 v.addClass('disabled');
23253 year = parseInt(year/10, 10) * 10;
23255 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23257 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23260 for (var i = -1; i < 11; i++) {
23261 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23263 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23271 showMode: function(dir)
23274 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23277 Roo.each(this.picker().select('>div',true).elements, function(v){
23278 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23281 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23286 if(this.isInline) {
23290 this.picker().removeClass(['bottom', 'top']);
23292 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23294 * place to the top of element!
23298 this.picker().addClass('top');
23299 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23304 this.picker().addClass('bottom');
23306 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23309 parseDate : function(value)
23311 if(!value || value instanceof Date){
23314 var v = Date.parseDate(value, this.format);
23315 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23316 v = Date.parseDate(value, 'Y-m-d');
23318 if(!v && this.altFormats){
23319 if(!this.altFormatsArray){
23320 this.altFormatsArray = this.altFormats.split("|");
23322 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23323 v = Date.parseDate(value, this.altFormatsArray[i]);
23329 formatDate : function(date, fmt)
23331 return (!date || !(date instanceof Date)) ?
23332 date : date.dateFormat(fmt || this.format);
23335 onFocus : function()
23337 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23341 onBlur : function()
23343 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23345 var d = this.inputEl().getValue();
23352 showPopup : function()
23354 this.picker().show();
23358 this.fireEvent('showpopup', this, this.date);
23361 hidePopup : function()
23363 if(this.isInline) {
23366 this.picker().hide();
23367 this.viewMode = this.startViewMode;
23370 this.fireEvent('hidepopup', this, this.date);
23374 onMousedown: function(e)
23376 e.stopPropagation();
23377 e.preventDefault();
23382 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23386 setValue: function(v)
23388 if(this.fireEvent('beforeselect', this, v) !== false){
23389 var d = new Date(this.parseDate(v) ).clearTime();
23391 if(isNaN(d.getTime())){
23392 this.date = this.viewDate = '';
23393 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23397 v = this.formatDate(d);
23399 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23401 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23405 this.fireEvent('select', this, this.date);
23409 getValue: function()
23411 return this.formatDate(this.date);
23414 fireKey: function(e)
23416 if (!this.picker().isVisible()){
23417 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23423 var dateChanged = false,
23425 newDate, newViewDate;
23430 e.preventDefault();
23434 if (!this.keyboardNavigation) {
23437 dir = e.keyCode == 37 ? -1 : 1;
23440 newDate = this.moveYear(this.date, dir);
23441 newViewDate = this.moveYear(this.viewDate, dir);
23442 } else if (e.shiftKey){
23443 newDate = this.moveMonth(this.date, dir);
23444 newViewDate = this.moveMonth(this.viewDate, dir);
23446 newDate = new Date(this.date);
23447 newDate.setUTCDate(this.date.getUTCDate() + dir);
23448 newViewDate = new Date(this.viewDate);
23449 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23451 if (this.dateWithinRange(newDate)){
23452 this.date = newDate;
23453 this.viewDate = newViewDate;
23454 this.setValue(this.formatDate(this.date));
23456 e.preventDefault();
23457 dateChanged = true;
23462 if (!this.keyboardNavigation) {
23465 dir = e.keyCode == 38 ? -1 : 1;
23467 newDate = this.moveYear(this.date, dir);
23468 newViewDate = this.moveYear(this.viewDate, dir);
23469 } else if (e.shiftKey){
23470 newDate = this.moveMonth(this.date, dir);
23471 newViewDate = this.moveMonth(this.viewDate, dir);
23473 newDate = new Date(this.date);
23474 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23475 newViewDate = new Date(this.viewDate);
23476 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23478 if (this.dateWithinRange(newDate)){
23479 this.date = newDate;
23480 this.viewDate = newViewDate;
23481 this.setValue(this.formatDate(this.date));
23483 e.preventDefault();
23484 dateChanged = true;
23488 this.setValue(this.formatDate(this.date));
23490 e.preventDefault();
23493 this.setValue(this.formatDate(this.date));
23507 onClick: function(e)
23509 e.stopPropagation();
23510 e.preventDefault();
23512 var target = e.getTarget();
23514 if(target.nodeName.toLowerCase() === 'i'){
23515 target = Roo.get(target).dom.parentNode;
23518 var nodeName = target.nodeName;
23519 var className = target.className;
23520 var html = target.innerHTML;
23521 //Roo.log(nodeName);
23523 switch(nodeName.toLowerCase()) {
23525 switch(className) {
23531 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23532 switch(this.viewMode){
23534 this.viewDate = this.moveMonth(this.viewDate, dir);
23538 this.viewDate = this.moveYear(this.viewDate, dir);
23544 var date = new Date();
23545 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23547 this.setValue(this.formatDate(this.date));
23554 if (className.indexOf('disabled') < 0) {
23555 if (!this.viewDate) {
23556 this.viewDate = new Date();
23558 this.viewDate.setUTCDate(1);
23559 if (className.indexOf('month') > -1) {
23560 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23562 var year = parseInt(html, 10) || 0;
23563 this.viewDate.setUTCFullYear(year);
23567 if(this.singleMode){
23568 this.setValue(this.formatDate(this.viewDate));
23579 //Roo.log(className);
23580 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23581 var day = parseInt(html, 10) || 1;
23582 var year = (this.viewDate || new Date()).getUTCFullYear(),
23583 month = (this.viewDate || new Date()).getUTCMonth();
23585 if (className.indexOf('old') > -1) {
23592 } else if (className.indexOf('new') > -1) {
23600 //Roo.log([year,month,day]);
23601 this.date = this.UTCDate(year, month, day,0,0,0,0);
23602 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23604 //Roo.log(this.formatDate(this.date));
23605 this.setValue(this.formatDate(this.date));
23612 setStartDate: function(startDate)
23614 this.startDate = startDate || -Infinity;
23615 if (this.startDate !== -Infinity) {
23616 this.startDate = this.parseDate(this.startDate);
23619 this.updateNavArrows();
23622 setEndDate: function(endDate)
23624 this.endDate = endDate || Infinity;
23625 if (this.endDate !== Infinity) {
23626 this.endDate = this.parseDate(this.endDate);
23629 this.updateNavArrows();
23632 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23634 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23635 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23636 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23638 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23639 return parseInt(d, 10);
23642 this.updateNavArrows();
23645 updateNavArrows: function()
23647 if(this.singleMode){
23651 var d = new Date(this.viewDate),
23652 year = d.getUTCFullYear(),
23653 month = d.getUTCMonth();
23655 Roo.each(this.picker().select('.prev', true).elements, function(v){
23657 switch (this.viewMode) {
23660 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23666 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23673 Roo.each(this.picker().select('.next', true).elements, function(v){
23675 switch (this.viewMode) {
23678 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23684 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23692 moveMonth: function(date, dir)
23697 var new_date = new Date(date.valueOf()),
23698 day = new_date.getUTCDate(),
23699 month = new_date.getUTCMonth(),
23700 mag = Math.abs(dir),
23702 dir = dir > 0 ? 1 : -1;
23705 // If going back one month, make sure month is not current month
23706 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23708 return new_date.getUTCMonth() == month;
23710 // If going forward one month, make sure month is as expected
23711 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23713 return new_date.getUTCMonth() != new_month;
23715 new_month = month + dir;
23716 new_date.setUTCMonth(new_month);
23717 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23718 if (new_month < 0 || new_month > 11) {
23719 new_month = (new_month + 12) % 12;
23722 // For magnitudes >1, move one month at a time...
23723 for (var i=0; i<mag; i++) {
23724 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23725 new_date = this.moveMonth(new_date, dir);
23727 // ...then reset the day, keeping it in the new month
23728 new_month = new_date.getUTCMonth();
23729 new_date.setUTCDate(day);
23731 return new_month != new_date.getUTCMonth();
23734 // Common date-resetting loop -- if date is beyond end of month, make it
23737 new_date.setUTCDate(--day);
23738 new_date.setUTCMonth(new_month);
23743 moveYear: function(date, dir)
23745 return this.moveMonth(date, dir*12);
23748 dateWithinRange: function(date)
23750 return date >= this.startDate && date <= this.endDate;
23756 this.picker().remove();
23759 validateValue : function(value)
23761 if(this.getVisibilityEl().hasClass('hidden')){
23765 if(value.length < 1) {
23766 if(this.allowBlank){
23772 if(value.length < this.minLength){
23775 if(value.length > this.maxLength){
23779 var vt = Roo.form.VTypes;
23780 if(!vt[this.vtype](value, this)){
23784 if(typeof this.validator == "function"){
23785 var msg = this.validator(value);
23791 if(this.regex && !this.regex.test(value)){
23795 if(typeof(this.parseDate(value)) == 'undefined'){
23799 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23803 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23813 this.date = this.viewDate = '';
23815 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23820 Roo.apply(Roo.bootstrap.form.DateField, {
23831 html: '<i class="fa fa-arrow-left"/>'
23841 html: '<i class="fa fa-arrow-right"/>'
23883 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23884 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23885 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23886 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23887 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23900 navFnc: 'FullYear',
23905 navFnc: 'FullYear',
23910 Roo.apply(Roo.bootstrap.form.DateField, {
23914 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23918 cls: 'datepicker-days',
23922 cls: 'table-condensed',
23924 Roo.bootstrap.form.DateField.head,
23928 Roo.bootstrap.form.DateField.footer
23935 cls: 'datepicker-months',
23939 cls: 'table-condensed',
23941 Roo.bootstrap.form.DateField.head,
23942 Roo.bootstrap.form.DateField.content,
23943 Roo.bootstrap.form.DateField.footer
23950 cls: 'datepicker-years',
23954 cls: 'table-condensed',
23956 Roo.bootstrap.form.DateField.head,
23957 Roo.bootstrap.form.DateField.content,
23958 Roo.bootstrap.form.DateField.footer
23977 * @class Roo.bootstrap.form.TimeField
23978 * @extends Roo.bootstrap.form.Input
23979 * Bootstrap DateField class
23983 * Create a new TimeField
23984 * @param {Object} config The config object
23987 Roo.bootstrap.form.TimeField = function(config){
23988 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23992 * Fires when this field show.
23993 * @param {Roo.bootstrap.form.DateField} thisthis
23994 * @param {Mixed} date The date value
23999 * Fires when this field hide.
24000 * @param {Roo.bootstrap.form.DateField} this
24001 * @param {Mixed} date The date value
24006 * Fires when select a date.
24007 * @param {Roo.bootstrap.form.DateField} this
24008 * @param {Mixed} date The date value
24014 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
24017 * @cfg {String} format
24018 * The default time format string which can be overriden for localization support. The format must be
24019 * valid according to {@link Date#parseDate} (defaults to 'H:i').
24023 getAutoCreate : function()
24025 this.after = '<i class="fa far fa-clock"></i>';
24026 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24030 onRender: function(ct, position)
24033 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24035 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24037 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24039 this.pop = this.picker().select('>.datepicker-time',true).first();
24040 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24042 this.picker().on('mousedown', this.onMousedown, this);
24043 this.picker().on('click', this.onClick, this);
24045 this.picker().addClass('datepicker-dropdown');
24050 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24051 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24052 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24053 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24054 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24055 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24059 fireKey: function(e){
24060 if (!this.picker().isVisible()){
24061 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24067 e.preventDefault();
24075 this.onTogglePeriod();
24078 this.onIncrementMinutes();
24081 this.onDecrementMinutes();
24090 onClick: function(e) {
24091 e.stopPropagation();
24092 e.preventDefault();
24095 picker : function()
24097 return this.pickerEl;
24100 fillTime: function()
24102 var time = this.pop.select('tbody', true).first();
24104 time.dom.innerHTML = '';
24119 cls: 'hours-up fa fas fa-chevron-up'
24139 cls: 'minutes-up fa fas fa-chevron-up'
24160 cls: 'timepicker-hour',
24175 cls: 'timepicker-minute',
24190 cls: 'btn btn-primary period',
24212 cls: 'hours-down fa fas fa-chevron-down'
24232 cls: 'minutes-down fa fas fa-chevron-down'
24250 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24257 var hours = this.time.getHours();
24258 var minutes = this.time.getMinutes();
24271 hours = hours - 12;
24275 hours = '0' + hours;
24279 minutes = '0' + minutes;
24282 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24283 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24284 this.pop.select('button', true).first().dom.innerHTML = period;
24290 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24292 var cls = ['bottom'];
24294 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24301 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24305 //this.picker().setXY(20000,20000);
24306 this.picker().addClass(cls.join('-'));
24310 Roo.each(cls, function(c){
24315 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24316 //_this.picker().setTop(_this.inputEl().getHeight());
24320 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24322 //_this.picker().setTop(0 - _this.picker().getHeight());
24327 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24331 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24339 onFocus : function()
24341 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24345 onBlur : function()
24347 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24353 this.picker().show();
24358 this.fireEvent('show', this, this.date);
24363 this.picker().hide();
24366 this.fireEvent('hide', this, this.date);
24369 setTime : function()
24372 this.setValue(this.time.format(this.format));
24374 this.fireEvent('select', this, this.date);
24379 onMousedown: function(e){
24380 e.stopPropagation();
24381 e.preventDefault();
24384 onIncrementHours: function()
24386 Roo.log('onIncrementHours');
24387 this.time = this.time.add(Date.HOUR, 1);
24392 onDecrementHours: function()
24394 Roo.log('onDecrementHours');
24395 this.time = this.time.add(Date.HOUR, -1);
24399 onIncrementMinutes: function()
24401 Roo.log('onIncrementMinutes');
24402 this.time = this.time.add(Date.MINUTE, 1);
24406 onDecrementMinutes: function()
24408 Roo.log('onDecrementMinutes');
24409 this.time = this.time.add(Date.MINUTE, -1);
24413 onTogglePeriod: function()
24415 Roo.log('onTogglePeriod');
24416 this.time = this.time.add(Date.HOUR, 12);
24424 Roo.apply(Roo.bootstrap.form.TimeField, {
24428 cls: 'datepicker dropdown-menu',
24432 cls: 'datepicker-time',
24436 cls: 'table-condensed',
24465 cls: 'btn btn-info ok',
24493 * @class Roo.bootstrap.form.MonthField
24494 * @extends Roo.bootstrap.form.Input
24495 * Bootstrap MonthField class
24497 * @cfg {String} language default en
24500 * Create a new MonthField
24501 * @param {Object} config The config object
24504 Roo.bootstrap.form.MonthField = function(config){
24505 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24510 * Fires when this field show.
24511 * @param {Roo.bootstrap.form.MonthField} this
24512 * @param {Mixed} date The date value
24517 * Fires when this field hide.
24518 * @param {Roo.bootstrap.form.MonthField} this
24519 * @param {Mixed} date The date value
24524 * Fires when select a date.
24525 * @param {Roo.bootstrap.form.MonthField} this
24526 * @param {String} oldvalue The old value
24527 * @param {String} newvalue The new value
24533 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24535 onRender: function(ct, position)
24538 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24540 this.language = this.language || 'en';
24541 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24542 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24544 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24545 this.isInline = false;
24546 this.isInput = true;
24547 this.component = this.el.select('.add-on', true).first() || false;
24548 this.component = (this.component && this.component.length === 0) ? false : this.component;
24549 this.hasInput = this.component && this.inputEL().length;
24551 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24553 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24555 this.picker().on('mousedown', this.onMousedown, this);
24556 this.picker().on('click', this.onClick, this);
24558 this.picker().addClass('datepicker-dropdown');
24560 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24561 v.setStyle('width', '189px');
24568 if(this.isInline) {
24574 setValue: function(v, suppressEvent)
24576 var o = this.getValue();
24578 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24582 if(suppressEvent !== true){
24583 this.fireEvent('select', this, o, v);
24588 getValue: function()
24593 onClick: function(e)
24595 e.stopPropagation();
24596 e.preventDefault();
24598 var target = e.getTarget();
24600 if(target.nodeName.toLowerCase() === 'i'){
24601 target = Roo.get(target).dom.parentNode;
24604 var nodeName = target.nodeName;
24605 var className = target.className;
24606 var html = target.innerHTML;
24608 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24612 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24614 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24620 picker : function()
24622 return this.pickerEl;
24625 fillMonths: function()
24628 var months = this.picker().select('>.datepicker-months td', true).first();
24630 months.dom.innerHTML = '';
24636 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24639 months.createChild(month);
24648 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24649 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24652 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24653 e.removeClass('active');
24655 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24656 e.addClass('active');
24663 if(this.isInline) {
24667 this.picker().removeClass(['bottom', 'top']);
24669 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24671 * place to the top of element!
24675 this.picker().addClass('top');
24676 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24681 this.picker().addClass('bottom');
24683 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24686 onFocus : function()
24688 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24692 onBlur : function()
24694 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24696 var d = this.inputEl().getValue();
24705 this.picker().show();
24706 this.picker().select('>.datepicker-months', true).first().show();
24710 this.fireEvent('show', this, this.date);
24715 if(this.isInline) {
24718 this.picker().hide();
24719 this.fireEvent('hide', this, this.date);
24723 onMousedown: function(e)
24725 e.stopPropagation();
24726 e.preventDefault();
24731 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24735 fireKey: function(e)
24737 if (!this.picker().isVisible()){
24738 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24749 e.preventDefault();
24753 dir = e.keyCode == 37 ? -1 : 1;
24755 this.vIndex = this.vIndex + dir;
24757 if(this.vIndex < 0){
24761 if(this.vIndex > 11){
24765 if(isNaN(this.vIndex)){
24769 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24775 dir = e.keyCode == 38 ? -1 : 1;
24777 this.vIndex = this.vIndex + dir * 4;
24779 if(this.vIndex < 0){
24783 if(this.vIndex > 11){
24787 if(isNaN(this.vIndex)){
24791 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24796 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24797 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24801 e.preventDefault();
24804 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24805 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24821 this.picker().remove();
24826 Roo.apply(Roo.bootstrap.form.MonthField, {
24845 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24846 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24851 Roo.apply(Roo.bootstrap.form.MonthField, {
24855 cls: 'datepicker dropdown-menu roo-dynamic',
24859 cls: 'datepicker-months',
24863 cls: 'table-condensed',
24865 Roo.bootstrap.form.DateField.content
24885 * @class Roo.bootstrap.form.CheckBox
24886 * @extends Roo.bootstrap.form.Input
24887 * Bootstrap CheckBox class
24889 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24890 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24891 * @cfg {String} boxLabel The text that appears beside the checkbox
24892 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24893 * @cfg {Boolean} checked initnal the element
24894 * @cfg {Boolean} inline inline the element (default false)
24895 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24896 * @cfg {String} tooltip label tooltip
24899 * Create a new CheckBox
24900 * @param {Object} config The config object
24903 Roo.bootstrap.form.CheckBox = function(config){
24904 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24909 * Fires when the element is checked or unchecked.
24910 * @param {Roo.bootstrap.form.CheckBox} this This input
24911 * @param {Boolean} checked The new checked value
24916 * Fires when the element is click.
24917 * @param {Roo.bootstrap.form.CheckBox} this This input
24924 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24926 inputType: 'checkbox',
24935 // checkbox success does not make any sense really..
24940 getAutoCreate : function()
24942 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24948 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24951 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24957 type : this.inputType,
24958 value : this.inputValue,
24959 cls : 'roo-' + this.inputType, //'form-box',
24960 placeholder : this.placeholder || ''
24964 if(this.inputType != 'radio'){
24968 cls : 'roo-hidden-value',
24969 value : this.checked ? this.inputValue : this.valueOff
24974 if (this.weight) { // Validity check?
24975 cfg.cls += " " + this.inputType + "-" + this.weight;
24978 if (this.disabled) {
24979 input.disabled=true;
24983 input.checked = this.checked;
24988 input.name = this.name;
24990 if(this.inputType != 'radio'){
24991 hidden.name = this.name;
24992 input.name = '_hidden_' + this.name;
24997 input.cls += ' input-' + this.size;
25002 ['xs','sm','md','lg'].map(function(size){
25003 if (settings[size]) {
25004 cfg.cls += ' col-' + size + '-' + settings[size];
25008 var inputblock = input;
25010 if (this.before || this.after) {
25013 cls : 'input-group',
25018 inputblock.cn.push({
25020 cls : 'input-group-addon',
25025 inputblock.cn.push(input);
25027 if(this.inputType != 'radio'){
25028 inputblock.cn.push(hidden);
25032 inputblock.cn.push({
25034 cls : 'input-group-addon',
25040 var boxLabelCfg = false;
25046 //'for': id, // box label is handled by onclick - so no for...
25048 html: this.boxLabel
25051 boxLabelCfg.tooltip = this.tooltip;
25057 if (align ==='left' && this.fieldLabel.length) {
25058 // Roo.log("left and has label");
25063 cls : 'control-label',
25064 html : this.fieldLabel
25075 cfg.cn[1].cn.push(boxLabelCfg);
25078 if(this.labelWidth > 12){
25079 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25082 if(this.labelWidth < 13 && this.labelmd == 0){
25083 this.labelmd = this.labelWidth;
25086 if(this.labellg > 0){
25087 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25088 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25091 if(this.labelmd > 0){
25092 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25093 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25096 if(this.labelsm > 0){
25097 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25098 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25101 if(this.labelxs > 0){
25102 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25103 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25106 } else if ( this.fieldLabel.length) {
25107 // Roo.log(" label");
25111 tag: this.boxLabel ? 'span' : 'label',
25113 cls: 'control-label box-input-label',
25114 //cls : 'input-group-addon',
25115 html : this.fieldLabel
25122 cfg.cn.push(boxLabelCfg);
25127 // Roo.log(" no label && no align");
25128 cfg.cn = [ inputblock ] ;
25130 cfg.cn.push(boxLabelCfg);
25138 if(this.inputType != 'radio'){
25139 cfg.cn.push(hidden);
25147 * return the real input element.
25149 inputEl: function ()
25151 return this.el.select('input.roo-' + this.inputType,true).first();
25153 hiddenEl: function ()
25155 return this.el.select('input.roo-hidden-value',true).first();
25158 labelEl: function()
25160 return this.el.select('label.control-label',true).first();
25162 /* depricated... */
25166 return this.labelEl();
25169 boxLabelEl: function()
25171 return this.el.select('label.box-label',true).first();
25174 initEvents : function()
25176 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25178 this.inputEl().on('click', this.onClick, this);
25180 if (this.boxLabel) {
25181 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25184 this.startValue = this.getValue();
25187 Roo.bootstrap.form.CheckBox.register(this);
25191 onClick : function(e)
25193 if(this.fireEvent('click', this, e) !== false){
25194 this.setChecked(!this.checked);
25199 setChecked : function(state,suppressEvent)
25201 this.startValue = this.getValue();
25203 if(this.inputType == 'radio'){
25205 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25206 e.dom.checked = false;
25209 this.inputEl().dom.checked = true;
25211 this.inputEl().dom.value = this.inputValue;
25213 if(suppressEvent !== true){
25214 this.fireEvent('check', this, true);
25222 this.checked = state;
25224 this.inputEl().dom.checked = state;
25227 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25229 if(suppressEvent !== true){
25230 this.fireEvent('check', this, state);
25236 getValue : function()
25238 if(this.inputType == 'radio'){
25239 return this.getGroupValue();
25242 return this.hiddenEl().dom.value;
25246 getGroupValue : function()
25248 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25252 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25255 setValue : function(v,suppressEvent)
25257 if(this.inputType == 'radio'){
25258 this.setGroupValue(v, suppressEvent);
25262 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25267 setGroupValue : function(v, suppressEvent)
25269 this.startValue = this.getValue();
25271 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25272 e.dom.checked = false;
25274 if(e.dom.value == v){
25275 e.dom.checked = true;
25279 if(suppressEvent !== true){
25280 this.fireEvent('check', this, true);
25288 validate : function()
25290 if(this.getVisibilityEl().hasClass('hidden')){
25296 (this.inputType == 'radio' && this.validateRadio()) ||
25297 (this.inputType == 'checkbox' && this.validateCheckbox())
25303 this.markInvalid();
25307 validateRadio : function()
25309 if(this.getVisibilityEl().hasClass('hidden')){
25313 if(this.allowBlank){
25319 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25320 if(!e.dom.checked){
25332 validateCheckbox : function()
25335 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25336 //return (this.getValue() == this.inputValue) ? true : false;
25339 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25347 for(var i in group){
25348 if(group[i].el.isVisible(true)){
25356 for(var i in group){
25361 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25368 * Mark this field as valid
25370 markValid : function()
25374 this.fireEvent('valid', this);
25376 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25379 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25386 if(this.inputType == 'radio'){
25387 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25388 var fg = e.findParent('.form-group', false, true);
25389 if (Roo.bootstrap.version == 3) {
25390 fg.removeClass([_this.invalidClass, _this.validClass]);
25391 fg.addClass(_this.validClass);
25393 fg.removeClass(['is-valid', 'is-invalid']);
25394 fg.addClass('is-valid');
25402 var fg = this.el.findParent('.form-group', false, true);
25403 if (Roo.bootstrap.version == 3) {
25404 fg.removeClass([this.invalidClass, this.validClass]);
25405 fg.addClass(this.validClass);
25407 fg.removeClass(['is-valid', 'is-invalid']);
25408 fg.addClass('is-valid');
25413 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25419 for(var i in group){
25420 var fg = group[i].el.findParent('.form-group', false, true);
25421 if (Roo.bootstrap.version == 3) {
25422 fg.removeClass([this.invalidClass, this.validClass]);
25423 fg.addClass(this.validClass);
25425 fg.removeClass(['is-valid', 'is-invalid']);
25426 fg.addClass('is-valid');
25432 * Mark this field as invalid
25433 * @param {String} msg The validation message
25435 markInvalid : function(msg)
25437 if(this.allowBlank){
25443 this.fireEvent('invalid', this, msg);
25445 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25448 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25452 label.markInvalid();
25455 if(this.inputType == 'radio'){
25457 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25458 var fg = e.findParent('.form-group', false, true);
25459 if (Roo.bootstrap.version == 3) {
25460 fg.removeClass([_this.invalidClass, _this.validClass]);
25461 fg.addClass(_this.invalidClass);
25463 fg.removeClass(['is-invalid', 'is-valid']);
25464 fg.addClass('is-invalid');
25472 var fg = this.el.findParent('.form-group', false, true);
25473 if (Roo.bootstrap.version == 3) {
25474 fg.removeClass([_this.invalidClass, _this.validClass]);
25475 fg.addClass(_this.invalidClass);
25477 fg.removeClass(['is-invalid', 'is-valid']);
25478 fg.addClass('is-invalid');
25483 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25489 for(var i in group){
25490 var fg = group[i].el.findParent('.form-group', false, true);
25491 if (Roo.bootstrap.version == 3) {
25492 fg.removeClass([_this.invalidClass, _this.validClass]);
25493 fg.addClass(_this.invalidClass);
25495 fg.removeClass(['is-invalid', 'is-valid']);
25496 fg.addClass('is-invalid');
25502 clearInvalid : function()
25504 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25506 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25508 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25510 if (label && label.iconEl) {
25511 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25512 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25516 disable : function()
25518 if(this.inputType != 'radio'){
25519 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25526 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25527 _this.getActionEl().addClass(this.disabledClass);
25528 e.dom.disabled = true;
25532 this.disabled = true;
25533 this.fireEvent("disable", this);
25537 enable : function()
25539 if(this.inputType != 'radio'){
25540 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25547 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25548 _this.getActionEl().removeClass(this.disabledClass);
25549 e.dom.disabled = false;
25553 this.disabled = false;
25554 this.fireEvent("enable", this);
25558 setBoxLabel : function(v)
25563 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25569 Roo.apply(Roo.bootstrap.form.CheckBox, {
25574 * register a CheckBox Group
25575 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25577 register : function(checkbox)
25579 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25580 this.groups[checkbox.groupId] = {};
25583 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25587 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25591 * fetch a CheckBox Group based on the group ID
25592 * @param {string} the group ID
25593 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25595 get: function(groupId) {
25596 if (typeof(this.groups[groupId]) == 'undefined') {
25600 return this.groups[groupId] ;
25613 * @class Roo.bootstrap.form.Radio
25614 * @extends Roo.bootstrap.Component
25615 * Bootstrap Radio class
25616 * @cfg {String} boxLabel - the label associated
25617 * @cfg {String} value - the value of radio
25620 * Create a new Radio
25621 * @param {Object} config The config object
25623 Roo.bootstrap.form.Radio = function(config){
25624 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25628 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25634 getAutoCreate : function()
25638 cls : 'form-group radio',
25643 html : this.boxLabel
25651 initEvents : function()
25653 this.parent().register(this);
25655 this.el.on('click', this.onClick, this);
25659 onClick : function(e)
25661 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25662 this.setChecked(true);
25666 setChecked : function(state, suppressEvent)
25668 this.parent().setValue(this.value, suppressEvent);
25672 setBoxLabel : function(v)
25677 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25692 * @class Roo.bootstrap.form.SecurePass
25693 * @extends Roo.bootstrap.form.Input
25694 * Bootstrap SecurePass class
25698 * Create a new SecurePass
25699 * @param {Object} config The config object
25702 Roo.bootstrap.form.SecurePass = function (config) {
25703 // these go here, so the translation tool can replace them..
25705 PwdEmpty: "Please type a password, and then retype it to confirm.",
25706 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25707 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25708 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25709 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25710 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25711 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25712 TooWeak: "Your password is Too Weak."
25714 this.meterLabel = "Password strength:";
25715 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25716 this.meterClass = [
25717 "roo-password-meter-tooweak",
25718 "roo-password-meter-weak",
25719 "roo-password-meter-medium",
25720 "roo-password-meter-strong",
25721 "roo-password-meter-grey"
25726 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25729 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25731 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25733 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25734 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25735 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25736 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25737 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25738 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25739 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25749 * @cfg {String/Object} Label for the strength meter (defaults to
25750 * 'Password strength:')
25755 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25756 * ['Weak', 'Medium', 'Strong'])
25759 pwdStrengths: false,
25772 initEvents: function ()
25774 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25776 if (this.el.is('input[type=password]') && Roo.isSafari) {
25777 this.el.on('keydown', this.SafariOnKeyDown, this);
25780 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25783 onRender: function (ct, position)
25785 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25786 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25787 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25789 this.trigger.createChild({
25794 cls: 'roo-password-meter-grey col-xs-12',
25797 //width: this.meterWidth + 'px'
25801 cls: 'roo-password-meter-text'
25807 if (this.hideTrigger) {
25808 this.trigger.setDisplayed(false);
25810 this.setSize(this.width || '', this.height || '');
25813 onDestroy: function ()
25815 if (this.trigger) {
25816 this.trigger.removeAllListeners();
25817 this.trigger.remove();
25820 this.wrap.remove();
25822 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25825 checkStrength: function ()
25827 var pwd = this.inputEl().getValue();
25828 if (pwd == this._lastPwd) {
25833 if (this.ClientSideStrongPassword(pwd)) {
25835 } else if (this.ClientSideMediumPassword(pwd)) {
25837 } else if (this.ClientSideWeakPassword(pwd)) {
25843 Roo.log('strength1: ' + strength);
25845 //var pm = this.trigger.child('div/div/div').dom;
25846 var pm = this.trigger.child('div/div');
25847 pm.removeClass(this.meterClass);
25848 pm.addClass(this.meterClass[strength]);
25851 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25853 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25855 this._lastPwd = pwd;
25859 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25861 this._lastPwd = '';
25863 var pm = this.trigger.child('div/div');
25864 pm.removeClass(this.meterClass);
25865 pm.addClass('roo-password-meter-grey');
25868 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25871 this.inputEl().dom.type='password';
25874 validateValue: function (value)
25876 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25879 if (value.length == 0) {
25880 if (this.allowBlank) {
25881 this.clearInvalid();
25885 this.markInvalid(this.errors.PwdEmpty);
25886 this.errorMsg = this.errors.PwdEmpty;
25894 if (!value.match(/[\x21-\x7e]+/)) {
25895 this.markInvalid(this.errors.PwdBadChar);
25896 this.errorMsg = this.errors.PwdBadChar;
25899 if (value.length < 6) {
25900 this.markInvalid(this.errors.PwdShort);
25901 this.errorMsg = this.errors.PwdShort;
25904 if (value.length > 16) {
25905 this.markInvalid(this.errors.PwdLong);
25906 this.errorMsg = this.errors.PwdLong;
25910 if (this.ClientSideStrongPassword(value)) {
25912 } else if (this.ClientSideMediumPassword(value)) {
25914 } else if (this.ClientSideWeakPassword(value)) {
25921 if (strength < 2) {
25922 //this.markInvalid(this.errors.TooWeak);
25923 this.errorMsg = this.errors.TooWeak;
25928 console.log('strength2: ' + strength);
25930 //var pm = this.trigger.child('div/div/div').dom;
25932 var pm = this.trigger.child('div/div');
25933 pm.removeClass(this.meterClass);
25934 pm.addClass(this.meterClass[strength]);
25936 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25938 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25940 this.errorMsg = '';
25944 CharacterSetChecks: function (type)
25947 this.fResult = false;
25950 isctype: function (character, type)
25953 case this.kCapitalLetter:
25954 if (character >= 'A' && character <= 'Z') {
25959 case this.kSmallLetter:
25960 if (character >= 'a' && character <= 'z') {
25966 if (character >= '0' && character <= '9') {
25971 case this.kPunctuation:
25972 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25983 IsLongEnough: function (pwd, size)
25985 return !(pwd == null || isNaN(size) || pwd.length < size);
25988 SpansEnoughCharacterSets: function (word, nb)
25990 if (!this.IsLongEnough(word, nb))
25995 var characterSetChecks = new Array(
25996 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25997 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26000 for (var index = 0; index < word.length; ++index) {
26001 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26002 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26003 characterSetChecks[nCharSet].fResult = true;
26010 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26011 if (characterSetChecks[nCharSet].fResult) {
26016 if (nCharSets < nb) {
26022 ClientSideStrongPassword: function (pwd)
26024 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26027 ClientSideMediumPassword: function (pwd)
26029 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26032 ClientSideWeakPassword: function (pwd)
26034 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26038 Roo.htmleditor = {};
26041 * @class Roo.htmleditor.Filter
26042 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26043 * @cfg {DomElement} node The node to iterate and filter
26044 * @cfg {boolean|String|Array} tag Tags to replace
26046 * Create a new Filter.
26047 * @param {Object} config Configuration options
26052 Roo.htmleditor.Filter = function(cfg) {
26053 Roo.apply(this.cfg);
26054 // this does not actually call walk as it's really just a abstract class
26058 Roo.htmleditor.Filter.prototype = {
26064 // overrride to do replace comments.
26065 replaceComment : false,
26067 // overrride to do replace or do stuff with tags..
26068 replaceTag : false,
26070 walk : function(dom)
26072 Roo.each( Array.from(dom.childNodes), function( e ) {
26075 case e.nodeType == 8 && this.replaceComment !== false: // comment
26076 this.replaceComment(e);
26079 case e.nodeType != 1: //not a node.
26082 case this.tag === true: // everything
26083 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26084 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26085 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26086 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26087 if (this.replaceTag && false === this.replaceTag(e)) {
26090 if (e.hasChildNodes()) {
26095 default: // tags .. that do not match.
26096 if (e.hasChildNodes()) {
26106 removeNodeKeepChildren : function( node)
26109 ar = Array.from(node.childNodes);
26110 for (var i = 0; i < ar.length; i++) {
26112 node.removeChild(ar[i]);
26113 // what if we need to walk these???
26114 node.parentNode.insertBefore(ar[i], node);
26117 node.parentNode.removeChild(node);
26122 * @class Roo.htmleditor.FilterAttributes
26123 * clean attributes and styles including http:// etc.. in attribute
26125 * Run a new Attribute Filter
26126 * @param {Object} config Configuration options
26128 Roo.htmleditor.FilterAttributes = function(cfg)
26130 Roo.apply(this, cfg);
26131 this.attrib_black = this.attrib_black || [];
26132 this.attrib_white = this.attrib_white || [];
26134 this.attrib_clean = this.attrib_clean || [];
26135 this.style_white = this.style_white || [];
26136 this.style_black = this.style_black || [];
26137 this.walk(cfg.node);
26140 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26142 tag: true, // all tags
26144 attrib_black : false, // array
26145 attrib_clean : false,
26146 attrib_white : false,
26148 style_white : false,
26149 style_black : false,
26152 replaceTag : function(node)
26154 if (!node.attributes || !node.attributes.length) {
26158 for (var i = node.attributes.length-1; i > -1 ; i--) {
26159 var a = node.attributes[i];
26161 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26162 node.removeAttribute(a.name);
26168 if (a.name.toLowerCase().substr(0,2)=='on') {
26169 node.removeAttribute(a.name);
26174 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26175 node.removeAttribute(a.name);
26178 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26179 this.cleanAttr(node,a.name,a.value); // fixme..
26182 if (a.name == 'style') {
26183 this.cleanStyle(node,a.name,a.value);
26186 /// clean up MS crap..
26187 // tecnically this should be a list of valid class'es..
26190 if (a.name == 'class') {
26191 if (a.value.match(/^Mso/)) {
26192 node.removeAttribute('class');
26195 if (a.value.match(/^body$/)) {
26196 node.removeAttribute('class');
26206 return true; // clean children
26209 cleanAttr: function(node, n,v)
26212 if (v.match(/^\./) || v.match(/^\//)) {
26215 if (v.match(/^(http|https):\/\//)
26216 || v.match(/^mailto:/)
26217 || v.match(/^ftp:/)
26218 || v.match(/^data:/)
26222 if (v.match(/^#/)) {
26225 if (v.match(/^\{/)) { // allow template editing.
26228 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26229 node.removeAttribute(n);
26232 cleanStyle : function(node, n,v)
26234 if (v.match(/expression/)) { //XSS?? should we even bother..
26235 node.removeAttribute(n);
26239 var parts = v.split(/;/);
26242 Roo.each(parts, function(p) {
26243 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26247 var l = p.split(':').shift().replace(/\s+/g,'');
26248 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26250 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26254 // only allow 'c whitelisted system attributes'
26255 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26263 if (clean.length) {
26264 node.setAttribute(n, clean.join(';'));
26266 node.removeAttribute(n);
26275 * @class Roo.htmleditor.FilterBlack
26276 * remove blacklisted elements.
26278 * Run a new Blacklisted Filter
26279 * @param {Object} config Configuration options
26282 Roo.htmleditor.FilterBlack = function(cfg)
26284 Roo.apply(this, cfg);
26285 this.walk(cfg.node);
26288 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26290 tag : true, // all elements.
26292 replaceTag : function(n)
26294 n.parentNode.removeChild(n);
26298 * @class Roo.htmleditor.FilterComment
26301 * Run a new Comments Filter
26302 * @param {Object} config Configuration options
26304 Roo.htmleditor.FilterComment = function(cfg)
26306 this.walk(cfg.node);
26309 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26312 replaceComment : function(n)
26314 n.parentNode.removeChild(n);
26317 * @class Roo.htmleditor.FilterKeepChildren
26318 * remove tags but keep children
26320 * Run a new Keep Children Filter
26321 * @param {Object} config Configuration options
26324 Roo.htmleditor.FilterKeepChildren = function(cfg)
26326 Roo.apply(this, cfg);
26327 if (this.tag === false) {
26328 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26331 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26332 this.cleanNamespace = true;
26335 this.walk(cfg.node);
26338 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26340 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26342 replaceTag : function(node)
26344 // walk children...
26345 //Roo.log(node.tagName);
26346 var ar = Array.from(node.childNodes);
26349 for (var i = 0; i < ar.length; i++) {
26351 if (e.nodeType == 1) {
26353 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26354 || // array and it matches
26355 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26357 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26359 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26361 this.replaceTag(ar[i]); // child is blacklisted as well...
26366 ar = Array.from(node.childNodes);
26367 for (var i = 0; i < ar.length; i++) {
26369 node.removeChild(ar[i]);
26370 // what if we need to walk these???
26371 node.parentNode.insertBefore(ar[i], node);
26372 if (this.tag !== false) {
26377 //Roo.log("REMOVE:" + node.tagName);
26378 node.parentNode.removeChild(node);
26379 return false; // don't walk children
26384 * @class Roo.htmleditor.FilterParagraph
26385 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26386 * like on 'push' to remove the <p> tags and replace them with line breaks.
26388 * Run a new Paragraph Filter
26389 * @param {Object} config Configuration options
26392 Roo.htmleditor.FilterParagraph = function(cfg)
26394 // no need to apply config.
26395 this.walk(cfg.node);
26398 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26405 replaceTag : function(node)
26408 if (node.childNodes.length == 1 &&
26409 node.childNodes[0].nodeType == 3 &&
26410 node.childNodes[0].textContent.trim().length < 1
26412 // remove and replace with '<BR>';
26413 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26414 return false; // no need to walk..
26416 var ar = Array.from(node.childNodes);
26417 for (var i = 0; i < ar.length; i++) {
26418 node.removeChild(ar[i]);
26419 // what if we need to walk these???
26420 node.parentNode.insertBefore(ar[i], node);
26422 // now what about this?
26426 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26427 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26428 node.parentNode.removeChild(node);
26435 * @class Roo.htmleditor.FilterSpan
26436 * filter span's with no attributes out..
26438 * Run a new Span Filter
26439 * @param {Object} config Configuration options
26442 Roo.htmleditor.FilterSpan = function(cfg)
26444 // no need to apply config.
26445 this.walk(cfg.node);
26448 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26454 replaceTag : function(node)
26456 if (node.attributes && node.attributes.length > 0) {
26457 return true; // walk if there are any.
26459 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26465 * @class Roo.htmleditor.FilterTableWidth
26466 try and remove table width data - as that frequently messes up other stuff.
26468 * was cleanTableWidths.
26470 * Quite often pasting from word etc.. results in tables with column and widths.
26471 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26474 * Run a new Table Filter
26475 * @param {Object} config Configuration options
26478 Roo.htmleditor.FilterTableWidth = function(cfg)
26480 // no need to apply config.
26481 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26482 this.walk(cfg.node);
26485 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26490 replaceTag: function(node) {
26494 if (node.hasAttribute('width')) {
26495 node.removeAttribute('width');
26499 if (node.hasAttribute("style")) {
26502 var styles = node.getAttribute("style").split(";");
26504 Roo.each(styles, function(s) {
26505 if (!s.match(/:/)) {
26508 var kv = s.split(":");
26509 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26512 // what ever is left... we allow.
26515 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26516 if (!nstyle.length) {
26517 node.removeAttribute('style');
26521 return true; // continue doing children..
26524 * @class Roo.htmleditor.FilterWord
26525 * try and clean up all the mess that Word generates.
26527 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26530 * Run a new Span Filter
26531 * @param {Object} config Configuration options
26534 Roo.htmleditor.FilterWord = function(cfg)
26536 // no need to apply config.
26537 this.replaceDocBullets(cfg.node);
26539 this.replaceAname(cfg.node);
26540 // this is disabled as the removal is done by other filters;
26541 // this.walk(cfg.node);
26546 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26552 * Clean up MS wordisms...
26554 replaceTag : function(node)
26557 // no idea what this does - span with text, replaceds with just text.
26559 node.nodeName == 'SPAN' &&
26560 !node.hasAttributes() &&
26561 node.childNodes.length == 1 &&
26562 node.firstChild.nodeName == "#text"
26564 var textNode = node.firstChild;
26565 node.removeChild(textNode);
26566 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26567 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26569 node.parentNode.insertBefore(textNode, node);
26570 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26571 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26574 node.parentNode.removeChild(node);
26575 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26580 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26581 node.parentNode.removeChild(node);
26582 return false; // dont do chidlren
26584 //Roo.log(node.tagName);
26585 // remove - but keep children..
26586 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26587 //Roo.log('-- removed');
26588 while (node.childNodes.length) {
26589 var cn = node.childNodes[0];
26590 node.removeChild(cn);
26591 node.parentNode.insertBefore(cn, node);
26592 // move node to parent - and clean it..
26593 if (cn.nodeType == 1) {
26594 this.replaceTag(cn);
26598 node.parentNode.removeChild(node);
26599 /// no need to iterate chidlren = it's got none..
26600 //this.iterateChildren(node, this.cleanWord);
26601 return false; // no need to iterate children.
26604 if (node.className.length) {
26606 var cn = node.className.split(/\W+/);
26608 Roo.each(cn, function(cls) {
26609 if (cls.match(/Mso[a-zA-Z]+/)) {
26614 node.className = cna.length ? cna.join(' ') : '';
26616 node.removeAttribute("class");
26620 if (node.hasAttribute("lang")) {
26621 node.removeAttribute("lang");
26624 if (node.hasAttribute("style")) {
26626 var styles = node.getAttribute("style").split(";");
26628 Roo.each(styles, function(s) {
26629 if (!s.match(/:/)) {
26632 var kv = s.split(":");
26633 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26636 // what ever is left... we allow.
26639 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26640 if (!nstyle.length) {
26641 node.removeAttribute('style');
26644 return true; // do children
26650 styleToObject: function(node)
26652 var styles = (node.getAttribute("style") || '').split(";");
26654 Roo.each(styles, function(s) {
26655 if (!s.match(/:/)) {
26658 var kv = s.split(":");
26660 // what ever is left... we allow.
26661 ret[kv[0].trim()] = kv[1];
26667 replaceAname : function (doc)
26669 // replace all the a/name without..
26670 var aa = Array.from(doc.getElementsByTagName('a'));
26671 for (var i = 0; i < aa.length; i++) {
26673 if (a.hasAttribute("name")) {
26674 a.removeAttribute("name");
26676 if (a.hasAttribute("href")) {
26679 // reparent children.
26680 this.removeNodeKeepChildren(a);
26690 replaceDocBullets : function(doc)
26692 // this is a bit odd - but it appears some indents use ql-indent-1
26693 //Roo.log(doc.innerHTML);
26695 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26696 for( var i = 0; i < listpara.length; i ++) {
26697 listpara[i].className = "MsoListParagraph";
26700 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26701 for( var i = 0; i < listpara.length; i ++) {
26702 listpara[i].className = "MsoListParagraph";
26704 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26705 for( var i = 0; i < listpara.length; i ++) {
26706 listpara[i].className = "MsoListParagraph";
26708 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
26709 for( var i = 0; i < listpara.length; i ++) {
26710 listpara[i].className = "MsoListParagraph";
26713 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26714 var htwo = Array.from(doc.getElementsByTagName('h2'));
26715 for( var i = 0; i < htwo.length; i ++) {
26716 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26717 htwo[i].className = "MsoListParagraph";
26720 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
26721 for( var i = 0; i < listpara.length; i ++) {
26722 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26723 listpara[i].className = "MsoListParagraph";
26725 listpara[i].className = "MsoNormalx";
26729 listpara = doc.getElementsByClassName('MsoListParagraph');
26730 // Roo.log(doc.innerHTML);
26734 while(listpara.length) {
26736 this.replaceDocBullet(listpara.item(0));
26743 replaceDocBullet : function(p)
26745 // gather all the siblings.
26747 parent = p.parentNode,
26748 doc = parent.ownerDocument,
26751 var listtype = 'ul';
26753 if (ns.nodeType != 1) {
26754 ns = ns.nextSibling;
26757 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26760 var spans = ns.getElementsByTagName('span');
26761 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26763 ns = ns.nextSibling;
26765 if (spans.length && spans[0].hasAttribute('style')) {
26766 var style = this.styleToObject(spans[0]);
26767 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26774 var spans = ns.getElementsByTagName('span');
26775 if (!spans.length) {
26778 var has_list = false;
26779 for(var i = 0; i < spans.length; i++) {
26780 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26789 ns = ns.nextSibling;
26793 if (!items.length) {
26798 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26799 parent.insertBefore(ul, p);
26801 var stack = [ ul ];
26802 var last_li = false;
26804 var margin_to_depth = {};
26807 items.forEach(function(n, ipos) {
26808 //Roo.log("got innertHMLT=" + n.innerHTML);
26810 var spans = n.getElementsByTagName('span');
26811 if (!spans.length) {
26812 //Roo.log("No spans found");
26814 parent.removeChild(n);
26817 return; // skip it...
26823 for(var i = 0; i < spans.length; i++) {
26825 style = this.styleToObject(spans[i]);
26826 if (typeof(style['mso-list']) == 'undefined') {
26829 if (listtype == 'ol') {
26830 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
26832 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26835 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26836 style = this.styleToObject(n); // mo-list is from the parent node.
26837 if (typeof(style['mso-list']) == 'undefined') {
26838 //Roo.log("parent is missing level");
26840 parent.removeChild(n);
26845 var margin = style['margin-left'];
26846 if (typeof(margin_to_depth[margin]) == 'undefined') {
26848 margin_to_depth[margin] = max_margins;
26850 nlvl = margin_to_depth[margin] ;
26854 var nul = doc.createElement(listtype); // what about number lists...
26856 last_li = doc.createElement('li');
26857 stack[lvl].appendChild(last_li);
26859 last_li.appendChild(nul);
26865 // not starting at 1..
26866 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26867 stack[nlvl].setAttribute("start", num);
26870 var nli = stack[nlvl].appendChild(doc.createElement('li'));
26872 nli.innerHTML = n.innerHTML;
26873 //Roo.log("innerHTML = " + n.innerHTML);
26874 parent.removeChild(n);
26890 * @class Roo.htmleditor.FilterStyleToTag
26891 * part of the word stuff... - certain 'styles' should be converted to tags.
26893 * font-weight: bold -> bold
26894 * ?? super / subscrit etc..
26897 * Run a new style to tag filter.
26898 * @param {Object} config Configuration options
26900 Roo.htmleditor.FilterStyleToTag = function(cfg)
26904 B : [ 'fontWeight' , 'bold'],
26905 I : [ 'fontStyle' , 'italic'],
26906 //pre : [ 'font-style' , 'italic'],
26907 // h1.. h6 ?? font-size?
26908 SUP : [ 'verticalAlign' , 'super' ],
26909 SUB : [ 'verticalAlign' , 'sub' ]
26914 Roo.apply(this, cfg);
26917 this.walk(cfg.node);
26924 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26926 tag: true, // all tags
26931 replaceTag : function(node)
26935 if (node.getAttribute("style") === null) {
26939 for (var k in this.tags) {
26940 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26942 node.style.removeProperty(this.tags[k][0]);
26945 if (!inject.length) {
26948 var cn = Array.from(node.childNodes);
26950 Roo.each(inject, function(t) {
26951 var nc = node.ownerDocument.createElement(t);
26952 nn.appendChild(nc);
26955 for(var i = 0;i < cn.length;cn++) {
26956 node.removeChild(cn[i]);
26957 nn.appendChild(cn[i]);
26959 return true /// iterate thru
26963 * @class Roo.htmleditor.FilterLongBr
26964 * BR/BR/BR - keep a maximum of 2...
26966 * Run a new Long BR Filter
26967 * @param {Object} config Configuration options
26970 Roo.htmleditor.FilterLongBr = function(cfg)
26972 // no need to apply config.
26973 this.walk(cfg.node);
26976 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26983 replaceTag : function(node)
26986 var ps = node.nextSibling;
26987 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26988 ps = ps.nextSibling;
26991 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
26992 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26996 if (!ps || ps.nodeType != 1) {
27000 if (!ps || ps.tagName != 'BR') {
27009 if (!node.previousSibling) {
27012 var ps = node.previousSibling;
27014 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27015 ps = ps.previousSibling;
27017 if (!ps || ps.nodeType != 1) {
27020 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27021 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27025 node.parentNode.removeChild(node); // remove me...
27027 return false; // no need to do children
27034 * @class Roo.htmleditor.FilterBlock
27035 * removes id / data-block and contenteditable that are associated with blocks
27036 * usage should be done on a cloned copy of the dom
27038 * Run a new Attribute Filter { node : xxxx }}
27039 * @param {Object} config Configuration options
27041 Roo.htmleditor.FilterBlock = function(cfg)
27043 Roo.apply(this, cfg);
27044 var qa = cfg.node.querySelectorAll;
27045 this.removeAttributes('data-block');
27046 this.removeAttributes('contenteditable');
27047 this.removeAttributes('id');
27051 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27053 node: true, // all tags
27056 removeAttributes : function(attr)
27058 var ar = this.node.querySelectorAll('*[' + attr + ']');
27059 for (var i =0;i<ar.length;i++) {
27060 ar[i].removeAttribute(attr);
27069 * This is based loosely on tinymce
27070 * @class Roo.htmleditor.TidySerializer
27071 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27073 * @method Serializer
27074 * @param {Object} settings Name/value settings object.
27078 Roo.htmleditor.TidySerializer = function(settings)
27080 Roo.apply(this, settings);
27082 this.writer = new Roo.htmleditor.TidyWriter(settings);
27087 Roo.htmleditor.TidySerializer.prototype = {
27090 * @param {boolean} inner do the inner of the node.
27097 * Serializes the specified node into a string.
27100 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27101 * @method serialize
27102 * @param {DomElement} node Node instance to serialize.
27103 * @return {String} String with HTML based on DOM tree.
27105 serialize : function(node) {
27107 // = settings.validate;
27108 var writer = this.writer;
27112 3: function(node) {
27114 writer.text(node.nodeValue, node);
27117 8: function(node) {
27118 writer.comment(node.nodeValue);
27120 // Processing instruction
27121 7: function(node) {
27122 writer.pi(node.name, node.nodeValue);
27125 10: function(node) {
27126 writer.doctype(node.nodeValue);
27129 4: function(node) {
27130 writer.cdata(node.nodeValue);
27132 // Document fragment
27133 11: function(node) {
27134 node = node.firstChild;
27140 node = node.nextSibling
27145 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27146 return writer.getContent();
27149 walk: function(node)
27151 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27152 handler = this.handlers[node.nodeType];
27159 var name = node.nodeName;
27160 var isEmpty = node.childNodes.length < 1;
27162 var writer = this.writer;
27163 var attrs = node.attributes;
27166 writer.start(node.nodeName, attrs, isEmpty, node);
27170 node = node.firstChild;
27177 node = node.nextSibling;
27183 // Serialize element and treat all non elements as fragments
27188 * This is based loosely on tinymce
27189 * @class Roo.htmleditor.TidyWriter
27190 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27193 * - not tested much with 'PRE' formated elements.
27199 Roo.htmleditor.TidyWriter = function(settings)
27202 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27203 Roo.apply(this, settings);
27207 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27210 Roo.htmleditor.TidyWriter.prototype = {
27217 // part of state...
27221 last_inline : false,
27226 * Writes the a start element such as <p id="a">.
27229 * @param {String} name Name of the element.
27230 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27231 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27233 start: function(name, attrs, empty, node)
27235 var i, l, attr, value;
27237 // there are some situations where adding line break && indentation will not work. will not work.
27238 // <span / b / i ... formating?
27240 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27241 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27243 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27245 var add_lb = name == 'BR' ? false : in_inline;
27247 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27251 var indentstr = this.indentstr;
27253 // e_inline = elements that can be inline, but still allow \n before and after?
27254 // only 'BR' ??? any others?
27256 // ADD LINE BEFORE tage
27257 if (!this.in_pre) {
27260 if (name == 'BR') {
27262 } else if (this.lastElementEndsWS()) {
27265 // otherwise - no new line. (and dont indent.)
27276 this.html.push(indentstr + '<', name.toLowerCase());
27279 for (i = 0, l = attrs.length; i < l; i++) {
27281 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27287 this.html[this.html.length] = '/>';
27289 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27291 var e_inline = name == 'BR' ? false : this.in_inline;
27293 if (!e_inline && !this.in_pre) {
27300 this.html[this.html.length] = '>';
27302 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27304 if (!in_inline && !in_pre) {
27305 var cn = node.firstChild;
27307 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27311 cn = cn.nextSibling;
27319 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27321 in_inline : in_inline
27323 // add a line after if we are not in a
27325 if (!in_inline && !in_pre) {
27334 lastElementEndsWS : function()
27336 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27337 if (value === false) {
27340 return value.match(/\s+$/);
27345 * Writes the a end element such as </p>.
27348 * @param {String} name Name of the element.
27350 end: function(name) {
27353 var indentstr = '';
27354 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27356 if (!this.in_pre && !in_inline) {
27358 indentstr = this.indentstr;
27360 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27361 this.last_inline = in_inline;
27363 // pop the indent state..
27366 * Writes a text node.
27368 * In pre - we should not mess with the contents.
27372 * @param {String} text String to write out.
27373 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27375 text: function(in_text, node)
27377 // if not in whitespace critical
27378 if (in_text.length < 1) {
27381 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27384 this.html[this.html.length] = text;
27388 if (this.in_inline) {
27389 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27391 text = text.replace(/\s+/,' '); // all white space to single white space
27394 // if next tag is '<BR>', then we can trim right..
27395 if (node.nextSibling &&
27396 node.nextSibling.nodeType == 1 &&
27397 node.nextSibling.nodeName == 'BR' )
27399 text = text.replace(/\s+$/g,'');
27401 // if previous tag was a BR, we can also trim..
27402 if (node.previousSibling &&
27403 node.previousSibling.nodeType == 1 &&
27404 node.previousSibling.nodeName == 'BR' )
27406 text = this.indentstr + text.replace(/^\s+/g,'');
27408 if (text.match(/\n/)) {
27409 text = text.replace(
27410 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27412 // remoeve the last whitespace / line break.
27413 text = text.replace(/\n\s+$/,'');
27415 // repace long lines
27419 this.html[this.html.length] = text;
27422 // see if previous element was a inline element.
27423 var indentstr = this.indentstr;
27425 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27427 // should trim left?
27428 if (node.previousSibling &&
27429 node.previousSibling.nodeType == 1 &&
27430 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27436 text = text.replace(/^\s+/,''); // trim left
27439 // should trim right?
27440 if (node.nextSibling &&
27441 node.nextSibling.nodeType == 1 &&
27442 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27447 text = text.replace(/\s+$/,''); // trim right
27454 if (text.length < 1) {
27457 if (!text.match(/\n/)) {
27458 this.html.push(indentstr + text);
27462 text = this.indentstr + text.replace(
27463 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27465 // remoeve the last whitespace / line break.
27466 text = text.replace(/\s+$/,'');
27468 this.html.push(text);
27470 // split and indent..
27475 * Writes a cdata node such as <![CDATA[data]]>.
27478 * @param {String} text String to write out inside the cdata.
27480 cdata: function(text) {
27481 this.html.push('<![CDATA[', text, ']]>');
27484 * Writes a comment node such as <!-- Comment -->.
27487 * @param {String} text String to write out inside the comment.
27489 comment: function(text) {
27490 this.html.push('<!--', text, '-->');
27493 * Writes a PI node such as <?xml attr="value" ?>.
27496 * @param {String} name Name of the pi.
27497 * @param {String} text String to write out inside the pi.
27499 pi: function(name, text) {
27500 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27501 this.indent != '' && this.html.push('\n');
27504 * Writes a doctype node such as <!DOCTYPE data>.
27507 * @param {String} text String to write out inside the doctype.
27509 doctype: function(text) {
27510 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27513 * Resets the internal buffer if one wants to reuse the writer.
27517 reset: function() {
27518 this.html.length = 0;
27527 * Returns the contents that got serialized.
27529 * @method getContent
27530 * @return {String} HTML contents that got written down.
27532 getContent: function() {
27533 return this.html.join('').replace(/\n$/, '');
27536 pushState : function(cfg)
27538 this.state.push(cfg);
27539 Roo.apply(this, cfg);
27542 popState : function()
27544 if (this.state.length < 1) {
27545 return; // nothing to push
27552 if (this.state.length > 0) {
27553 cfg = this.state[this.state.length-1];
27555 Roo.apply(this, cfg);
27558 addLine: function()
27560 if (this.html.length < 1) {
27565 var value = this.html[this.html.length - 1];
27566 if (value.length > 0 && '\n' !== value) {
27567 this.html.push('\n');
27572 //'pre script noscript style textarea video audio iframe object code'
27573 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
27577 Roo.htmleditor.TidyWriter.inline_elements = [
27578 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27579 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27581 Roo.htmleditor.TidyWriter.shortend_elements = [
27582 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27583 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27586 Roo.htmleditor.TidyWriter.whitespace_elements = [
27587 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27589 * This is based loosely on tinymce
27590 * @class Roo.htmleditor.TidyEntities
27592 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27594 * Not 100% sure this is actually used or needed.
27597 Roo.htmleditor.TidyEntities = {
27600 * initialize data..
27602 init : function (){
27604 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27609 buildEntitiesLookup: function(items, radix) {
27610 var i, chr, entity, lookup = {};
27614 items = typeof(items) == 'string' ? items.split(',') : items;
27615 radix = radix || 10;
27616 // Build entities lookup table
27617 for (i = 0; i < items.length; i += 2) {
27618 chr = String.fromCharCode(parseInt(items[i], radix));
27619 // Only add non base entities
27620 if (!this.baseEntities[chr]) {
27621 entity = '&' + items[i + 1] + ';';
27622 lookup[chr] = entity;
27623 lookup[entity] = chr;
27662 // Needs to be escaped since the YUI compressor would otherwise break the code
27669 // Reverse lookup table for raw entities
27670 reverseEntities : {
27678 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27679 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27680 rawCharsRegExp : /[<>&\"\']/g,
27681 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27682 namedEntities : false,
27683 namedEntitiesData : [
28184 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28186 * @method encodeRaw
28187 * @param {String} text Text to encode.
28188 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28189 * @return {String} Entity encoded text.
28191 encodeRaw: function(text, attr)
28194 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28195 return t.baseEntities[chr] || chr;
28199 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28200 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28201 * and is exposed as the DOMUtils.encode function.
28203 * @method encodeAllRaw
28204 * @param {String} text Text to encode.
28205 * @return {String} Entity encoded text.
28207 encodeAllRaw: function(text) {
28209 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28210 return t.baseEntities[chr] || chr;
28214 * Encodes the specified string using numeric entities. The core entities will be
28215 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28217 * @method encodeNumeric
28218 * @param {String} text Text to encode.
28219 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28220 * @return {String} Entity encoded text.
28222 encodeNumeric: function(text, attr) {
28224 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28225 // Multi byte sequence convert it to a single entity
28226 if (chr.length > 1) {
28227 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28229 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28233 * Encodes the specified string using named entities. The core entities will be encoded
28234 * as named ones but all non lower ascii characters will be encoded into named entities.
28236 * @method encodeNamed
28237 * @param {String} text Text to encode.
28238 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28239 * @param {Object} entities Optional parameter with entities to use.
28240 * @return {String} Entity encoded text.
28242 encodeNamed: function(text, attr, entities) {
28244 entities = entities || this.namedEntities;
28245 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28246 return t.baseEntities[chr] || entities[chr] || chr;
28250 * Returns an encode function based on the name(s) and it's optional entities.
28252 * @method getEncodeFunc
28253 * @param {String} name Comma separated list of encoders for example named,numeric.
28254 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28255 * @return {function} Encode function to be used.
28257 getEncodeFunc: function(name, entities) {
28258 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28260 function encodeNamedAndNumeric(text, attr) {
28261 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28262 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28266 function encodeCustomNamed(text, attr) {
28267 return t.encodeNamed(text, attr, entities);
28269 // Replace + with , to be compatible with previous TinyMCE versions
28270 name = this.makeMap(name.replace(/\+/g, ','));
28271 // Named and numeric encoder
28272 if (name.named && name.numeric) {
28273 return this.encodeNamedAndNumeric;
28279 return encodeCustomNamed;
28281 return this.encodeNamed;
28284 if (name.numeric) {
28285 return this.encodeNumeric;
28288 return this.encodeRaw;
28291 * Decodes the specified string, this will replace entities with raw UTF characters.
28294 * @param {String} text Text to entity decode.
28295 * @return {String} Entity decoded string.
28297 decode: function(text)
28300 return text.replace(this.entityRegExp, function(all, numeric) {
28302 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28303 // Support upper UTF
28304 if (numeric > 65535) {
28306 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28308 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28310 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28313 nativeDecode : function (text) {
28316 makeMap : function (items, delim, map) {
28318 items = items || [];
28319 delim = delim || ',';
28320 if (typeof items == "string") {
28321 items = items.split(delim);
28326 map[items[i]] = {};
28334 Roo.htmleditor.TidyEntities.init();
28336 * @class Roo.htmleditor.KeyEnter
28337 * Handle Enter press..
28338 * @cfg {Roo.HtmlEditorCore} core the editor.
28340 * Create a new Filter.
28341 * @param {Object} config Configuration options
28348 Roo.htmleditor.KeyEnter = function(cfg) {
28349 Roo.apply(this, cfg);
28350 // this does not actually call walk as it's really just a abstract class
28352 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28355 //Roo.htmleditor.KeyEnter.i = 0;
28358 Roo.htmleditor.KeyEnter.prototype = {
28362 keypress : function(e)
28364 if (e.charCode != 13 && e.charCode != 10) {
28365 Roo.log([e.charCode,e]);
28368 e.preventDefault();
28369 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28370 var doc = this.core.doc;
28374 var sel = this.core.getSelection();
28375 var range = sel.getRangeAt(0);
28376 var n = range.commonAncestorContainer;
28377 var pc = range.closest([ 'ol', 'ul']);
28378 var pli = range.closest('li');
28379 if (!pc || e.ctrlKey) {
28380 // on it list, or ctrl pressed.
28382 sel.insertNode('br', 'after');
28384 // only do this if we have ctrl key..
28385 var br = doc.createElement('br');
28386 br.className = 'clear';
28387 br.setAttribute('style', 'clear: both');
28388 sel.insertNode(br, 'after');
28392 this.core.undoManager.addEvent();
28393 this.core.fireEditorEvent(e);
28397 // deal with <li> insetion
28398 if (pli.innerText.trim() == '' &&
28399 pli.previousSibling &&
28400 pli.previousSibling.nodeName == 'LI' &&
28401 pli.previousSibling.innerText.trim() == '') {
28402 pli.parentNode.removeChild(pli.previousSibling);
28403 sel.cursorAfter(pc);
28404 this.core.undoManager.addEvent();
28405 this.core.fireEditorEvent(e);
28409 var li = doc.createElement('LI');
28410 li.innerHTML = ' ';
28411 if (!pli || !pli.firstSibling) {
28412 pc.appendChild(li);
28414 pli.parentNode.insertBefore(li, pli.firstSibling);
28416 sel.cursorText (li.firstChild);
28418 this.core.undoManager.addEvent();
28419 this.core.fireEditorEvent(e);
28431 * @class Roo.htmleditor.Block
28432 * Base class for html editor blocks - do not use it directly .. extend it..
28433 * @cfg {DomElement} node The node to apply stuff to.
28434 * @cfg {String} friendly_name the name that appears in the context bar about this block
28435 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28438 * Create a new Filter.
28439 * @param {Object} config Configuration options
28442 Roo.htmleditor.Block = function(cfg)
28444 // do nothing .. should not be called really.
28447 * factory method to get the block from an element (using cache if necessary)
28449 * @param {HtmlElement} the dom element
28451 Roo.htmleditor.Block.factory = function(node)
28453 var cc = Roo.htmleditor.Block.cache;
28454 var id = Roo.get(node).id;
28455 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28456 Roo.htmleditor.Block.cache[id].readElement(node);
28457 return Roo.htmleditor.Block.cache[id];
28459 var db = node.getAttribute('data-block');
28461 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28463 var cls = Roo.htmleditor['Block' + db];
28464 if (typeof(cls) == 'undefined') {
28465 //Roo.log(node.getAttribute('data-block'));
28466 Roo.log("OOps missing block : " + 'Block' + db);
28469 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28470 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
28474 * initalize all Elements from content that are 'blockable'
28476 * @param the body element
28478 Roo.htmleditor.Block.initAll = function(body, type)
28480 if (typeof(type) == 'undefined') {
28481 var ia = Roo.htmleditor.Block.initAll;
28487 Roo.each(Roo.get(body).query(type), function(e) {
28488 Roo.htmleditor.Block.factory(e);
28491 // question goes here... do we need to clear out this cache sometimes?
28492 // or show we make it relivant to the htmleditor.
28493 Roo.htmleditor.Block.cache = {};
28495 Roo.htmleditor.Block.prototype = {
28499 // used by context menu
28500 friendly_name : 'Based Block',
28502 // text for button to delete this element
28503 deleteTitle : false,
28507 * Update a node with values from this object
28508 * @param {DomElement} node
28510 updateElement : function(node)
28512 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28515 * convert to plain HTML for calling insertAtCursor..
28517 toHTML : function()
28519 return Roo.DomHelper.markup(this.toObject());
28522 * used by readEleemnt to extract data from a node
28523 * may need improving as it's pretty basic
28525 * @param {DomElement} node
28526 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28527 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28528 * @param {String} style the style property - eg. text-align
28530 getVal : function(node, tag, attr, style)
28533 if (tag !== true && n.tagName != tag.toUpperCase()) {
28534 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28535 // but kiss for now.
28536 n = node.getElementsByTagName(tag).item(0);
28541 if (attr === false) {
28544 if (attr == 'html') {
28545 return n.innerHTML;
28547 if (attr == 'style') {
28548 return n.style[style];
28551 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28555 * create a DomHelper friendly object - for use with
28556 * Roo.DomHelper.markup / overwrite / etc..
28559 toObject : function()
28564 * Read a node that has a 'data-block' property - and extract the values from it.
28565 * @param {DomElement} node - the node
28567 readElement : function(node)
28578 * @class Roo.htmleditor.BlockFigure
28579 * Block that has an image and a figcaption
28580 * @cfg {String} image_src the url for the image
28581 * @cfg {String} align (left|right) alignment for the block default left
28582 * @cfg {String} caption the text to appear below (and in the alt tag)
28583 * @cfg {String} caption_display (block|none) display or not the caption
28584 * @cfg {String|number} image_width the width of the image number or %?
28585 * @cfg {String|number} image_height the height of the image number or %?
28588 * Create a new Filter.
28589 * @param {Object} config Configuration options
28592 Roo.htmleditor.BlockFigure = function(cfg)
28595 this.readElement(cfg.node);
28596 this.updateElement(cfg.node);
28598 Roo.apply(this, cfg);
28600 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28607 caption_display : 'block',
28613 // margin: '2%', not used
28615 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
28618 // used by context menu
28619 friendly_name : 'Image with caption',
28620 deleteTitle : "Delete Image and Caption",
28622 contextMenu : function(toolbar)
28625 var block = function() {
28626 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28630 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28632 var syncValue = toolbar.editorcore.syncValue;
28638 xtype : 'TextItem',
28640 xns : rooui.Toolbar //Boostrap?
28644 text: 'Change Image URL',
28647 click: function (btn, state)
28651 Roo.MessageBox.show({
28652 title : "Image Source URL",
28653 msg : "Enter the url for the image",
28654 buttons: Roo.MessageBox.OKCANCEL,
28655 fn: function(btn, val){
28662 toolbar.editorcore.onEditorEvent();
28666 //multiline: multiline,
28668 value : b.image_src
28672 xns : rooui.Toolbar
28677 text: 'Change Link URL',
28680 click: function (btn, state)
28684 Roo.MessageBox.show({
28685 title : "Link URL",
28686 msg : "Enter the url for the link - leave blank to have no link",
28687 buttons: Roo.MessageBox.OKCANCEL,
28688 fn: function(btn, val){
28695 toolbar.editorcore.onEditorEvent();
28699 //multiline: multiline,
28705 xns : rooui.Toolbar
28709 text: 'Show Video URL',
28712 click: function (btn, state)
28714 Roo.MessageBox.alert("Video URL",
28715 block().video_url == '' ? 'This image is not linked ot a video' :
28716 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28719 xns : rooui.Toolbar
28724 xtype : 'TextItem',
28726 xns : rooui.Toolbar //Boostrap?
28729 xtype : 'ComboBox',
28730 allowBlank : false,
28731 displayField : 'val',
28734 triggerAction : 'all',
28736 valueField : 'val',
28740 select : function (combo, r, index)
28742 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28744 b.width = r.get('val');
28747 toolbar.editorcore.onEditorEvent();
28752 xtype : 'SimpleStore',
28765 xtype : 'TextItem',
28767 xns : rooui.Toolbar //Boostrap?
28770 xtype : 'ComboBox',
28771 allowBlank : false,
28772 displayField : 'val',
28775 triggerAction : 'all',
28777 valueField : 'val',
28781 select : function (combo, r, index)
28783 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28785 b.align = r.get('val');
28788 toolbar.editorcore.onEditorEvent();
28793 xtype : 'SimpleStore',
28807 text: 'Hide Caption',
28808 name : 'caption_display',
28810 enableToggle : true,
28811 setValue : function(v) {
28812 // this trigger toggle.
28814 this.setText(v ? "Hide Caption" : "Show Caption");
28815 this.setPressed(v != 'block');
28818 toggle: function (btn, state)
28821 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28822 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28825 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28826 toolbar.editorcore.onEditorEvent();
28829 xns : rooui.Toolbar
28835 * create a DomHelper friendly object - for use with
28836 * Roo.DomHelper.markup / overwrite / etc..
28838 toObject : function()
28840 var d = document.createElement('div');
28841 d.innerHTML = this.caption;
28843 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
28845 var iw = this.align == 'center' ? this.width : '100%';
28848 contenteditable : 'false',
28849 src : this.image_src,
28850 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28853 maxWidth : iw + ' !important', // this is not getting rendered?
28859 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28861 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
28866 if (this.href.length > 0) {
28870 contenteditable : 'true',
28878 if (this.video_url.length > 0) {
28883 allowfullscreen : true,
28884 width : 420, // these are for video tricks - that we replace the outer
28886 src : this.video_url,
28892 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28893 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28898 'data-block' : 'Figure',
28899 'data-width' : this.width,
28900 contenteditable : 'false',
28904 float : this.align ,
28905 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28906 width : this.align == 'center' ? '100%' : this.width,
28908 padding: this.align == 'center' ? '0' : '0 10px' ,
28909 textAlign : this.align // seems to work for email..
28914 align : this.align,
28920 'data-display' : this.caption_display,
28922 textAlign : 'left',
28924 lineHeight : '24px',
28925 display : this.caption_display,
28926 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
28928 width: this.align == 'center' ? this.width : '100%'
28932 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
28937 marginTop : '16px',
28943 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
28945 contenteditable : true,
28961 readElement : function(node)
28963 // this should not really come from the link...
28964 this.video_url = this.getVal(node, 'div', 'src');
28965 this.cls = this.getVal(node, 'div', 'class');
28966 this.href = this.getVal(node, 'a', 'href');
28969 this.image_src = this.getVal(node, 'img', 'src');
28971 this.align = this.getVal(node, 'figure', 'align');
28972 var figcaption = this.getVal(node, 'figcaption', false);
28973 if (figcaption !== '') {
28974 this.caption = this.getVal(figcaption, 'i', 'html');
28978 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28979 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28980 this.width = this.getVal(node, true, 'data-width');
28981 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28984 removeNode : function()
29001 * @class Roo.htmleditor.BlockTable
29002 * Block that manages a table
29005 * Create a new Filter.
29006 * @param {Object} config Configuration options
29009 Roo.htmleditor.BlockTable = function(cfg)
29012 this.readElement(cfg.node);
29013 this.updateElement(cfg.node);
29015 Roo.apply(this, cfg);
29018 for(var r = 0; r < this.no_row; r++) {
29020 for(var c = 0; c < this.no_col; c++) {
29021 this.rows[r][c] = this.emptyCell();
29028 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29037 // used by context menu
29038 friendly_name : 'Table',
29039 deleteTitle : 'Delete Table',
29040 // context menu is drawn once..
29042 contextMenu : function(toolbar)
29045 var block = function() {
29046 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29050 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29052 var syncValue = toolbar.editorcore.syncValue;
29058 xtype : 'TextItem',
29060 xns : rooui.Toolbar //Boostrap?
29063 xtype : 'ComboBox',
29064 allowBlank : false,
29065 displayField : 'val',
29068 triggerAction : 'all',
29070 valueField : 'val',
29074 select : function (combo, r, index)
29076 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29078 b.width = r.get('val');
29081 toolbar.editorcore.onEditorEvent();
29086 xtype : 'SimpleStore',
29098 xtype : 'TextItem',
29099 text : "Columns: ",
29100 xns : rooui.Toolbar //Boostrap?
29107 click : function (_self, e)
29109 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29110 block().removeColumn();
29112 toolbar.editorcore.onEditorEvent();
29115 xns : rooui.Toolbar
29121 click : function (_self, e)
29123 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29124 block().addColumn();
29126 toolbar.editorcore.onEditorEvent();
29129 xns : rooui.Toolbar
29133 xtype : 'TextItem',
29135 xns : rooui.Toolbar //Boostrap?
29142 click : function (_self, e)
29144 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29145 block().removeRow();
29147 toolbar.editorcore.onEditorEvent();
29150 xns : rooui.Toolbar
29156 click : function (_self, e)
29160 toolbar.editorcore.onEditorEvent();
29163 xns : rooui.Toolbar
29168 text: 'Reset Column Widths',
29171 click : function (_self, e)
29173 block().resetWidths();
29175 toolbar.editorcore.onEditorEvent();
29178 xns : rooui.Toolbar
29189 * create a DomHelper friendly object - for use with
29190 * Roo.DomHelper.markup / overwrite / etc..
29191 * ?? should it be called with option to hide all editing features?
29193 toObject : function()
29198 contenteditable : 'false', // this stops cell selection from picking the table.
29199 'data-block' : 'Table',
29202 border : 'solid 1px #000', // ??? hard coded?
29203 'border-collapse' : 'collapse'
29206 { tag : 'tbody' , cn : [] }
29210 // do we have a head = not really
29212 Roo.each(this.rows, function( row ) {
29217 border : 'solid 1px #000',
29223 ret.cn[0].cn.push(tr);
29224 // does the row have any properties? ?? height?
29226 Roo.each(row, function( cell ) {
29230 contenteditable : 'true',
29231 'data-block' : 'Td',
29235 if (cell.colspan > 1) {
29236 td.colspan = cell.colspan ;
29237 nc += cell.colspan;
29241 if (cell.rowspan > 1) {
29242 td.rowspan = cell.rowspan ;
29251 ncols = Math.max(nc, ncols);
29255 // add the header row..
29264 readElement : function(node)
29266 node = node ? node : this.node ;
29267 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29271 var trs = Array.from(node.rows);
29272 trs.forEach(function(tr) {
29274 this.rows.push(row);
29278 Array.from(tr.cells).forEach(function(td) {
29281 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29282 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29283 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29284 html : td.innerHTML
29286 no_column += add.colspan;
29293 this.no_col = Math.max(this.no_col, no_column);
29300 normalizeRows: function()
29304 this.rows.forEach(function(row) {
29307 row = this.normalizeRow(row);
29309 row.forEach(function(c) {
29310 while (typeof(ret[rid][cid]) != 'undefined') {
29313 if (typeof(ret[rid]) == 'undefined') {
29319 if (c.rowspan < 2) {
29323 for(var i = 1 ;i < c.rowspan; i++) {
29324 if (typeof(ret[rid+i]) == 'undefined') {
29327 ret[rid+i][cid] = c;
29335 normalizeRow: function(row)
29338 row.forEach(function(c) {
29339 if (c.colspan < 2) {
29343 for(var i =0 ;i < c.colspan; i++) {
29351 deleteColumn : function(sel)
29353 if (!sel || sel.type != 'col') {
29356 if (this.no_col < 2) {
29360 this.rows.forEach(function(row) {
29361 var cols = this.normalizeRow(row);
29362 var col = cols[sel.col];
29363 if (col.colspan > 1) {
29373 removeColumn : function()
29375 this.deleteColumn({
29377 col : this.no_col-1
29379 this.updateElement();
29383 addColumn : function()
29386 this.rows.forEach(function(row) {
29387 row.push(this.emptyCell());
29390 this.updateElement();
29393 deleteRow : function(sel)
29395 if (!sel || sel.type != 'row') {
29399 if (this.no_row < 2) {
29403 var rows = this.normalizeRows();
29406 rows[sel.row].forEach(function(col) {
29407 if (col.rowspan > 1) {
29410 col.remove = 1; // flage it as removed.
29415 this.rows.forEach(function(row) {
29417 row.forEach(function(c) {
29418 if (typeof(c.remove) == 'undefined') {
29423 if (newrow.length > 0) {
29427 this.rows = newrows;
29432 this.updateElement();
29435 removeRow : function()
29439 row : this.no_row-1
29445 addRow : function()
29449 for (var i = 0; i < this.no_col; i++ ) {
29451 row.push(this.emptyCell());
29454 this.rows.push(row);
29455 this.updateElement();
29459 // the default cell object... at present...
29460 emptyCell : function() {
29461 return (new Roo.htmleditor.BlockTd({})).toObject();
29466 removeNode : function()
29473 resetWidths : function()
29475 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29476 var nn = Roo.htmleditor.Block.factory(n);
29478 nn.updateElement(n);
29491 * since selections really work on the table cell, then editing really should work from there
29493 * The original plan was to support merging etc... - but that may not be needed yet..
29495 * So this simple version will support:
29497 * adjust the width +/-
29498 * reset the width...
29507 * @class Roo.htmleditor.BlockTable
29508 * Block that manages a table
29511 * Create a new Filter.
29512 * @param {Object} config Configuration options
29515 Roo.htmleditor.BlockTd = function(cfg)
29518 this.readElement(cfg.node);
29519 this.updateElement(cfg.node);
29521 Roo.apply(this, cfg);
29526 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29531 textAlign : 'left',
29538 // used by context menu
29539 friendly_name : 'Table Cell',
29540 deleteTitle : false, // use our customer delete
29542 // context menu is drawn once..
29544 contextMenu : function(toolbar)
29547 var cell = function() {
29548 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29551 var table = function() {
29552 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29556 var saveSel = function()
29558 lr = toolbar.editorcore.getSelection().getRangeAt(0);
29560 var restoreSel = function()
29564 toolbar.editorcore.focus();
29565 var cr = toolbar.editorcore.getSelection();
29566 cr.removeAllRanges();
29568 toolbar.editorcore.onEditorEvent();
29569 }).defer(10, this);
29575 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29577 var syncValue = toolbar.editorcore.syncValue;
29584 text : 'Edit Table',
29586 click : function() {
29587 var t = toolbar.tb.selectedNode.closest('table');
29588 toolbar.editorcore.selectNode(t);
29589 toolbar.editorcore.onEditorEvent();
29598 xtype : 'TextItem',
29599 text : "Column Width: ",
29600 xns : rooui.Toolbar
29607 click : function (_self, e)
29609 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29610 cell().shrinkColumn();
29612 toolbar.editorcore.onEditorEvent();
29615 xns : rooui.Toolbar
29621 click : function (_self, e)
29623 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29624 cell().growColumn();
29626 toolbar.editorcore.onEditorEvent();
29629 xns : rooui.Toolbar
29633 xtype : 'TextItem',
29634 text : "Vertical Align: ",
29635 xns : rooui.Toolbar //Boostrap?
29638 xtype : 'ComboBox',
29639 allowBlank : false,
29640 displayField : 'val',
29643 triggerAction : 'all',
29645 valueField : 'val',
29649 select : function (combo, r, index)
29651 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29653 b.valign = r.get('val');
29656 toolbar.editorcore.onEditorEvent();
29661 xtype : 'SimpleStore',
29665 ['bottom'] // there are afew more...
29673 xtype : 'TextItem',
29674 text : "Merge Cells: ",
29675 xns : rooui.Toolbar
29684 click : function (_self, e)
29686 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29687 cell().mergeRight();
29688 //block().growColumn();
29690 toolbar.editorcore.onEditorEvent();
29693 xns : rooui.Toolbar
29700 click : function (_self, e)
29702 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29703 cell().mergeBelow();
29704 //block().growColumn();
29706 toolbar.editorcore.onEditorEvent();
29709 xns : rooui.Toolbar
29712 xtype : 'TextItem',
29714 xns : rooui.Toolbar
29722 click : function (_self, e)
29724 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29727 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29728 toolbar.editorcore.onEditorEvent();
29732 xns : rooui.Toolbar
29736 xns : rooui.Toolbar
29745 xns : rooui.Toolbar,
29754 click : function (_self, e)
29758 cell().deleteColumn();
29760 toolbar.editorcore.selectNode(t.node);
29761 toolbar.editorcore.onEditorEvent();
29770 click : function (_self, e)
29773 cell().deleteRow();
29776 toolbar.editorcore.selectNode(t.node);
29777 toolbar.editorcore.onEditorEvent();
29784 xtype : 'Separator',
29791 click : function (_self, e)
29794 var nn = t.node.nextSibling || t.node.previousSibling;
29795 t.node.parentNode.removeChild(t.node);
29797 toolbar.editorcore.selectNode(nn, true);
29799 toolbar.editorcore.onEditorEvent();
29809 // align... << fixme
29817 * create a DomHelper friendly object - for use with
29818 * Roo.DomHelper.markup / overwrite / etc..
29819 * ?? should it be called with option to hide all editing features?
29822 * create a DomHelper friendly object - for use with
29823 * Roo.DomHelper.markup / overwrite / etc..
29824 * ?? should it be called with option to hide all editing features?
29826 toObject : function()
29830 contenteditable : 'true', // this stops cell selection from picking the table.
29831 'data-block' : 'Td',
29832 valign : this.valign,
29834 'text-align' : this.textAlign,
29835 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29836 'border-collapse' : 'collapse',
29837 padding : '6px', // 8 for desktop / 4 for mobile
29838 'vertical-align': this.valign
29842 if (this.width != '') {
29843 ret.width = this.width;
29844 ret.style.width = this.width;
29848 if (this.colspan > 1) {
29849 ret.colspan = this.colspan ;
29851 if (this.rowspan > 1) {
29852 ret.rowspan = this.rowspan ;
29861 readElement : function(node)
29863 node = node ? node : this.node ;
29864 this.width = node.style.width;
29865 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29866 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29867 this.html = node.innerHTML;
29868 if (node.style.textAlign != '') {
29869 this.textAlign = node.style.textAlign;
29875 // the default cell object... at present...
29876 emptyCell : function() {
29880 textAlign : 'left',
29881 html : " " // is this going to be editable now?
29886 removeNode : function()
29888 return this.node.closest('table');
29896 toTableArray : function()
29899 var tab = this.node.closest('tr').closest('table');
29900 Array.from(tab.rows).forEach(function(r, ri){
29904 this.colWidths = [];
29905 var all_auto = true;
29906 Array.from(tab.rows).forEach(function(r, ri){
29909 Array.from(r.cells).forEach(function(ce, ci){
29914 colspan : ce.colSpan,
29915 rowspan : ce.rowSpan
29917 if (ce.isEqualNode(this.node)) {
29920 // if we have been filled up by a row?
29921 if (typeof(ret[rn][cn]) != 'undefined') {
29922 while(typeof(ret[rn][cn]) != 'undefined') {
29928 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29929 this.colWidths[cn] = ce.style.width;
29930 if (this.colWidths[cn] != '') {
29936 if (c.colspan < 2 && c.rowspan < 2 ) {
29941 for(var j = 0; j < c.rowspan; j++) {
29942 if (typeof(ret[rn+j]) == 'undefined') {
29943 continue; // we have a problem..
29946 for(var i = 0; i < c.colspan; i++) {
29947 ret[rn+j][cn+i] = c;
29956 // initalize widths.?
29957 // either all widths or no widths..
29959 this.colWidths[0] = false; // no widths flag.
29970 mergeRight: function()
29973 // get the contents of the next cell along..
29974 var tr = this.node.closest('tr');
29975 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29976 if (i >= tr.childNodes.length - 1) {
29977 return; // no cells on right to merge with.
29979 var table = this.toTableArray();
29981 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29982 return; // nothing right?
29984 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29985 // right cell - must be same rowspan and on the same row.
29986 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29987 return; // right hand side is not same rowspan.
29992 this.node.innerHTML += ' ' + rc.cell.innerHTML;
29993 tr.removeChild(rc.cell);
29994 this.colspan += rc.colspan;
29995 this.node.setAttribute('colspan', this.colspan);
29997 var table = this.toTableArray();
29998 this.normalizeWidths(table);
29999 this.updateWidths(table);
30003 mergeBelow : function()
30005 var table = this.toTableArray();
30006 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30007 return; // no row below
30009 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30010 return; // nothing right?
30012 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30014 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30015 return; // right hand side is not same rowspan.
30017 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30018 rc.cell.parentNode.removeChild(rc.cell);
30019 this.rowspan += rc.rowspan;
30020 this.node.setAttribute('rowspan', this.rowspan);
30025 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30028 var table = this.toTableArray();
30029 var cd = this.cellData;
30033 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30036 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30037 if (r == cd.row && c == cd.col) {
30038 this.node.removeAttribute('rowspan');
30039 this.node.removeAttribute('colspan');
30042 var ntd = this.node.cloneNode(); // which col/row should be 0..
30043 ntd.removeAttribute('id');
30044 ntd.style.width = this.colWidths[c];
30045 ntd.innerHTML = '';
30046 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30050 this.redrawAllCells(table);
30056 redrawAllCells: function(table)
30060 var tab = this.node.closest('tr').closest('table');
30061 var ctr = tab.rows[0].parentNode;
30062 Array.from(tab.rows).forEach(function(r, ri){
30064 Array.from(r.cells).forEach(function(ce, ci){
30065 ce.parentNode.removeChild(ce);
30067 r.parentNode.removeChild(r);
30069 for(var r = 0 ; r < table.length; r++) {
30070 var re = tab.rows[r];
30072 var re = tab.ownerDocument.createElement('tr');
30073 ctr.appendChild(re);
30074 for(var c = 0 ; c < table[r].length; c++) {
30075 if (table[r][c].cell === false) {
30079 re.appendChild(table[r][c].cell);
30081 table[r][c].cell = false;
30086 updateWidths : function(table)
30088 for(var r = 0 ; r < table.length; r++) {
30090 for(var c = 0 ; c < table[r].length; c++) {
30091 if (table[r][c].cell === false) {
30095 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30096 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30097 el.width = Math.floor(this.colWidths[c]) +'%';
30098 el.updateElement(el.node);
30100 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30101 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30103 for(var i = 0; i < table[r][c].colspan; i ++) {
30104 width += Math.floor(this.colWidths[c + i]);
30106 el.width = width +'%';
30107 el.updateElement(el.node);
30109 table[r][c].cell = false; // done
30113 normalizeWidths : function(table)
30115 if (this.colWidths[0] === false) {
30116 var nw = 100.0 / this.colWidths.length;
30117 this.colWidths.forEach(function(w,i) {
30118 this.colWidths[i] = nw;
30123 var t = 0, missing = [];
30125 this.colWidths.forEach(function(w,i) {
30127 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30128 var add = this.colWidths[i];
30137 var nc = this.colWidths.length;
30138 if (missing.length) {
30139 var mult = (nc - missing.length) / (1.0 * nc);
30141 var ew = (100 -t) / (1.0 * missing.length);
30142 this.colWidths.forEach(function(w,i) {
30144 this.colWidths[i] = w * mult;
30148 this.colWidths[i] = ew;
30150 // have to make up numbers..
30153 // now we should have all the widths..
30158 shrinkColumn : function()
30160 var table = this.toTableArray();
30161 this.normalizeWidths(table);
30162 var col = this.cellData.col;
30163 var nw = this.colWidths[col] * 0.8;
30167 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30168 this.colWidths.forEach(function(w,i) {
30170 this.colWidths[i] = nw;
30173 this.colWidths[i] += otherAdd
30175 this.updateWidths(table);
30178 growColumn : function()
30180 var table = this.toTableArray();
30181 this.normalizeWidths(table);
30182 var col = this.cellData.col;
30183 var nw = this.colWidths[col] * 1.2;
30187 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30188 this.colWidths.forEach(function(w,i) {
30190 this.colWidths[i] = nw;
30193 this.colWidths[i] -= otherSub
30195 this.updateWidths(table);
30198 deleteRow : function()
30200 // delete this rows 'tr'
30201 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30202 // then reduce the rowspan.
30203 var table = this.toTableArray();
30204 // this.cellData.row;
30205 for (var i =0;i< table[this.cellData.row].length ; i++) {
30206 var c = table[this.cellData.row][i];
30207 if (c.row != this.cellData.row) {
30210 c.cell.setAttribute('rowspan', c.rowspan);
30213 if (c.rowspan > 1) {
30215 c.cell.setAttribute('rowspan', c.rowspan);
30218 table.splice(this.cellData.row,1);
30219 this.redrawAllCells(table);
30222 deleteColumn : function()
30224 var table = this.toTableArray();
30226 for (var i =0;i< table.length ; i++) {
30227 var c = table[i][this.cellData.col];
30228 if (c.col != this.cellData.col) {
30229 table[i][this.cellData.col].colspan--;
30230 } else if (c.colspan > 1) {
30232 c.cell.setAttribute('colspan', c.colspan);
30234 table[i].splice(this.cellData.col,1);
30237 this.redrawAllCells(table);
30245 //<script type="text/javascript">
30248 * Based Ext JS Library 1.1.1
30249 * Copyright(c) 2006-2007, Ext JS, LLC.
30255 * @class Roo.HtmlEditorCore
30256 * @extends Roo.Component
30257 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30259 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30262 Roo.HtmlEditorCore = function(config){
30265 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30270 * @event initialize
30271 * Fires when the editor is fully initialized (including the iframe)
30272 * @param {Roo.HtmlEditorCore} this
30277 * Fires when the editor is first receives the focus. Any insertion must wait
30278 * until after this event.
30279 * @param {Roo.HtmlEditorCore} this
30283 * @event beforesync
30284 * Fires before the textarea is updated with content from the editor iframe. Return false
30285 * to cancel the sync.
30286 * @param {Roo.HtmlEditorCore} this
30287 * @param {String} html
30291 * @event beforepush
30292 * Fires before the iframe editor is updated with content from the textarea. Return false
30293 * to cancel the push.
30294 * @param {Roo.HtmlEditorCore} this
30295 * @param {String} html
30300 * Fires when the textarea is updated with content from the editor iframe.
30301 * @param {Roo.HtmlEditorCore} this
30302 * @param {String} html
30307 * Fires when the iframe editor is updated with content from the textarea.
30308 * @param {Roo.HtmlEditorCore} this
30309 * @param {String} html
30314 * @event editorevent
30315 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30316 * @param {Roo.HtmlEditorCore} this
30323 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30325 // defaults : white / black...
30326 this.applyBlacklists();
30333 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30337 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30343 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
30348 * @cfg {Number} height (in pixels)
30352 * @cfg {Number} width (in pixels)
30356 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30357 * if you are doing an email editor, this probably needs disabling, it's designed
30362 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30364 enableBlocks : true,
30366 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30369 stylesheets: false,
30371 * @cfg {String} language default en - language of text (usefull for rtl languages)
30377 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30378 * - by default they are stripped - if you are editing email you may need this.
30380 allowComments: false,
30384 // private properties
30385 validationEvent : false,
30387 initialized : false,
30389 sourceEditMode : false,
30390 onFocus : Roo.emptyFn,
30392 hideMode:'offsets',
30396 // blacklist + whitelisted elements..
30403 undoManager : false,
30405 * Protected method that will not generally be called directly. It
30406 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30407 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30409 getDocMarkup : function(){
30413 // inherit styels from page...??
30414 if (this.stylesheets === false) {
30416 Roo.get(document.head).select('style').each(function(node) {
30417 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30420 Roo.get(document.head).select('link').each(function(node) {
30421 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30424 } else if (!this.stylesheets.length) {
30426 st = '<style type="text/css">' +
30427 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30430 for (var i in this.stylesheets) {
30431 if (typeof(this.stylesheets[i]) != 'string') {
30434 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30439 st += '<style type="text/css">' +
30440 'IMG { cursor: pointer } ' +
30443 st += '<meta name="google" content="notranslate">';
30445 var cls = 'notranslate roo-htmleditor-body';
30447 if(this.bodyCls.length){
30448 cls += ' ' + this.bodyCls;
30451 return '<html class="notranslate" translate="no"><head>' + st +
30452 //<style type="text/css">' +
30453 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30455 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30459 onRender : function(ct, position)
30462 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30463 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30466 this.el.dom.style.border = '0 none';
30467 this.el.dom.setAttribute('tabIndex', -1);
30468 this.el.addClass('x-hidden hide');
30472 if(Roo.isIE){ // fix IE 1px bogus margin
30473 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30477 this.frameId = Roo.id();
30481 var iframe = this.owner.wrap.createChild({
30483 cls: 'form-control', // bootstrap..
30485 name: this.frameId,
30486 frameBorder : 'no',
30487 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
30492 this.iframe = iframe.dom;
30494 this.assignDocWin();
30496 this.doc.designMode = 'on';
30499 this.doc.write(this.getDocMarkup());
30503 var task = { // must defer to wait for browser to be ready
30505 //console.log("run task?" + this.doc.readyState);
30506 this.assignDocWin();
30507 if(this.doc.body || this.doc.readyState == 'complete'){
30509 this.doc.designMode="on";
30514 Roo.TaskMgr.stop(task);
30515 this.initEditor.defer(10, this);
30522 Roo.TaskMgr.start(task);
30527 onResize : function(w, h)
30529 Roo.log('resize: ' +w + ',' + h );
30530 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30534 if(typeof w == 'number'){
30536 this.iframe.style.width = w + 'px';
30538 if(typeof h == 'number'){
30540 this.iframe.style.height = h + 'px';
30542 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30549 * Toggles the editor between standard and source edit mode.
30550 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30552 toggleSourceEdit : function(sourceEditMode){
30554 this.sourceEditMode = sourceEditMode === true;
30556 if(this.sourceEditMode){
30558 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
30561 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30562 //this.iframe.className = '';
30565 //this.setSize(this.owner.wrap.getSize());
30566 //this.fireEvent('editmodechange', this, this.sourceEditMode);
30573 * Protected method that will not generally be called directly. If you need/want
30574 * custom HTML cleanup, this is the method you should override.
30575 * @param {String} html The HTML to be cleaned
30576 * return {String} The cleaned HTML
30578 cleanHtml : function(html)
30580 html = String(html);
30581 if(html.length > 5){
30582 if(Roo.isSafari){ // strip safari nonsense
30583 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30586 if(html == ' '){
30593 * HTML Editor -> Textarea
30594 * Protected method that will not generally be called directly. Syncs the contents
30595 * of the editor iframe with the textarea.
30597 syncValue : function()
30599 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30600 if(this.initialized){
30602 if (this.undoManager) {
30603 this.undoManager.addEvent();
30607 var bd = (this.doc.body || this.doc.documentElement);
30610 var sel = this.win.getSelection();
30612 var div = document.createElement('div');
30613 div.innerHTML = bd.innerHTML;
30614 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30615 if (gtx.length > 0) {
30616 var rm = gtx.item(0).parentNode;
30617 rm.parentNode.removeChild(rm);
30621 if (this.enableBlocks) {
30622 new Roo.htmleditor.FilterBlock({ node : div });
30625 var html = div.innerHTML;
30628 if (this.autoClean) {
30630 new Roo.htmleditor.FilterAttributes({
30651 attrib_clean : ['href', 'src' ]
30654 var tidy = new Roo.htmleditor.TidySerializer({
30657 html = tidy.serialize(div);
30663 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30664 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30666 html = '<div style="'+m[0]+'">' + html + '</div>';
30669 html = this.cleanHtml(html);
30670 // fix up the special chars.. normaly like back quotes in word...
30671 // however we do not want to do this with chinese..
30672 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30674 var cc = match.charCodeAt();
30676 // Get the character value, handling surrogate pairs
30677 if (match.length == 2) {
30678 // It's a surrogate pair, calculate the Unicode code point
30679 var high = match.charCodeAt(0) - 0xD800;
30680 var low = match.charCodeAt(1) - 0xDC00;
30681 cc = (high * 0x400) + low + 0x10000;
30683 (cc >= 0x4E00 && cc < 0xA000 ) ||
30684 (cc >= 0x3400 && cc < 0x4E00 ) ||
30685 (cc >= 0xf900 && cc < 0xfb00 )
30690 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30691 return "&#" + cc + ";";
30698 if(this.owner.fireEvent('beforesync', this, html) !== false){
30699 this.el.dom.value = html;
30700 this.owner.fireEvent('sync', this, html);
30706 * TEXTAREA -> EDITABLE
30707 * Protected method that will not generally be called directly. Pushes the value of the textarea
30708 * into the iframe editor.
30710 pushValue : function()
30712 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30713 if(this.initialized){
30714 var v = this.el.dom.value.trim();
30717 if(this.owner.fireEvent('beforepush', this, v) !== false){
30718 var d = (this.doc.body || this.doc.documentElement);
30721 this.el.dom.value = d.innerHTML;
30722 this.owner.fireEvent('push', this, v);
30724 if (this.autoClean) {
30725 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30726 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30728 if (this.enableBlocks) {
30729 Roo.htmleditor.Block.initAll(this.doc.body);
30732 this.updateLanguage();
30734 var lc = this.doc.body.lastChild;
30735 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30736 // add an extra line at the end.
30737 this.doc.body.appendChild(this.doc.createElement('br'));
30745 deferFocus : function(){
30746 this.focus.defer(10, this);
30750 focus : function(){
30751 if(this.win && !this.sourceEditMode){
30758 assignDocWin: function()
30760 var iframe = this.iframe;
30763 this.doc = iframe.contentWindow.document;
30764 this.win = iframe.contentWindow;
30766 // if (!Roo.get(this.frameId)) {
30769 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30770 // this.win = Roo.get(this.frameId).dom.contentWindow;
30772 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30776 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30777 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30782 initEditor : function(){
30783 //console.log("INIT EDITOR");
30784 this.assignDocWin();
30788 this.doc.designMode="on";
30790 this.doc.write(this.getDocMarkup());
30793 var dbody = (this.doc.body || this.doc.documentElement);
30794 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30795 // this copies styles from the containing element into thsi one..
30796 // not sure why we need all of this..
30797 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30799 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30800 //ss['background-attachment'] = 'fixed'; // w3c
30801 dbody.bgProperties = 'fixed'; // ie
30802 dbody.setAttribute("translate", "no");
30804 //Roo.DomHelper.applyStyles(dbody, ss);
30805 Roo.EventManager.on(this.doc, {
30807 'mouseup': this.onEditorEvent,
30808 'dblclick': this.onEditorEvent,
30809 'click': this.onEditorEvent,
30810 'keyup': this.onEditorEvent,
30815 Roo.EventManager.on(this.doc, {
30816 'paste': this.onPasteEvent,
30820 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30823 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30824 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30826 this.initialized = true;
30829 // initialize special key events - enter
30830 new Roo.htmleditor.KeyEnter({core : this});
30834 this.owner.fireEvent('initialize', this);
30837 // this is to prevent a href clicks resulting in a redirect?
30839 onPasteEvent : function(e,v)
30841 // I think we better assume paste is going to be a dirty load of rubish from word..
30843 // even pasting into a 'email version' of this widget will have to clean up that mess.
30844 var cd = (e.browserEvent.clipboardData || window.clipboardData);
30846 // check what type of paste - if it's an image, then handle it differently.
30847 if (cd.files && cd.files.length > 0) {
30849 var urlAPI = (window.createObjectURL && window) ||
30850 (window.URL && URL.revokeObjectURL && URL) ||
30851 (window.webkitURL && webkitURL);
30853 var url = urlAPI.createObjectURL( cd.files[0]);
30854 this.insertAtCursor('<img src=" + url + ">');
30857 if (cd.types.indexOf('text/html') < 0 ) {
30861 var html = cd.getData('text/html'); // clipboard event
30862 if (cd.types.indexOf('text/rtf') > -1) {
30863 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30864 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30869 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30870 .map(function(g) { return g.toDataURL(); })
30871 .filter(function(g) { return g != 'about:blank'; });
30874 html = this.cleanWordChars(html);
30876 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30879 var sn = this.getParentElement();
30880 // check if d contains a table, and prevent nesting??
30881 //Roo.log(d.getElementsByTagName('table'));
30883 //Roo.log(sn.closest('table'));
30884 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30885 e.preventDefault();
30886 this.insertAtCursor("You can not nest tables");
30887 //Roo.log("prevent?"); // fixme -
30893 if (images.length > 0) {
30894 // replace all v:imagedata - with img.
30895 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30896 Roo.each(ar, function(node) {
30897 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30898 node.parentNode.removeChild(node);
30902 Roo.each(d.getElementsByTagName('img'), function(img, i) {
30903 img.setAttribute('src', images[i]);
30906 if (this.autoClean) {
30907 new Roo.htmleditor.FilterWord({ node : d });
30909 new Roo.htmleditor.FilterStyleToTag({ node : d });
30910 new Roo.htmleditor.FilterAttributes({
30912 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30913 attrib_clean : ['href', 'src' ]
30915 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30916 // should be fonts..
30917 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30918 new Roo.htmleditor.FilterParagraph({ node : d });
30919 new Roo.htmleditor.FilterSpan({ node : d });
30920 new Roo.htmleditor.FilterLongBr({ node : d });
30921 new Roo.htmleditor.FilterComment({ node : d });
30925 if (this.enableBlocks) {
30927 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30928 if (img.closest('figure')) { // assume!! that it's aready
30931 var fig = new Roo.htmleditor.BlockFigure({
30932 image_src : img.src
30934 fig.updateElement(img); // replace it..
30940 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
30941 if (this.enableBlocks) {
30942 Roo.htmleditor.Block.initAll(this.doc.body);
30946 e.preventDefault();
30948 // default behaveiour should be our local cleanup paste? (optional?)
30949 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30950 //this.owner.fireEvent('paste', e, v);
30953 onDestroy : function(){
30959 //for (var i =0; i < this.toolbars.length;i++) {
30960 // // fixme - ask toolbars for heights?
30961 // this.toolbars[i].onDestroy();
30964 //this.wrap.dom.innerHTML = '';
30965 //this.wrap.remove();
30970 onFirstFocus : function(){
30972 this.assignDocWin();
30973 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30975 this.activated = true;
30978 if(Roo.isGecko){ // prevent silly gecko errors
30980 var s = this.win.getSelection();
30981 if(!s.focusNode || s.focusNode.nodeType != 3){
30982 var r = s.getRangeAt(0);
30983 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30988 this.execCmd('useCSS', true);
30989 this.execCmd('styleWithCSS', false);
30992 this.owner.fireEvent('activate', this);
30996 adjustFont: function(btn){
30997 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30998 //if(Roo.isSafari){ // safari
31001 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31002 if(Roo.isSafari){ // safari
31003 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31004 v = (v < 10) ? 10 : v;
31005 v = (v > 48) ? 48 : v;
31006 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31011 v = Math.max(1, v+adjust);
31013 this.execCmd('FontSize', v );
31016 onEditorEvent : function(e)
31020 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31021 return; // we do not handle this.. (undo manager does..)
31023 // in theory this detects if the last element is not a br, then we try and do that.
31024 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31026 e.target.nodeName == 'BODY' &&
31027 e.type == "mouseup" &&
31028 this.doc.body.lastChild
31030 var lc = this.doc.body.lastChild;
31031 // gtx-trans is google translate plugin adding crap.
31032 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31033 lc = lc.previousSibling;
31035 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31036 // if last element is <BR> - then dont do anything.
31038 var ns = this.doc.createElement('br');
31039 this.doc.body.appendChild(ns);
31040 range = this.doc.createRange();
31041 range.setStartAfter(ns);
31042 range.collapse(true);
31043 var sel = this.win.getSelection();
31044 sel.removeAllRanges();
31045 sel.addRange(range);
31051 this.fireEditorEvent(e);
31052 // this.updateToolbar();
31053 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31056 fireEditorEvent: function(e)
31058 this.owner.fireEvent('editorevent', this, e);
31061 insertTag : function(tg)
31063 // could be a bit smarter... -> wrap the current selected tRoo..
31064 if (tg.toLowerCase() == 'span' ||
31065 tg.toLowerCase() == 'code' ||
31066 tg.toLowerCase() == 'sup' ||
31067 tg.toLowerCase() == 'sub'
31070 range = this.createRange(this.getSelection());
31071 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31072 wrappingNode.appendChild(range.extractContents());
31073 range.insertNode(wrappingNode);
31080 this.execCmd("formatblock", tg);
31081 this.undoManager.addEvent();
31084 insertText : function(txt)
31088 var range = this.createRange();
31089 range.deleteContents();
31090 //alert(Sender.getAttribute('label'));
31092 range.insertNode(this.doc.createTextNode(txt));
31093 this.undoManager.addEvent();
31099 * Executes a Midas editor command on the editor document and performs necessary focus and
31100 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31101 * @param {String} cmd The Midas command
31102 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31104 relayCmd : function(cmd, value)
31108 case 'justifyleft':
31109 case 'justifyright':
31110 case 'justifycenter':
31111 // if we are in a cell, then we will adjust the
31112 var n = this.getParentElement();
31113 var td = n.closest('td');
31115 var bl = Roo.htmleditor.Block.factory(td);
31116 bl.textAlign = cmd.replace('justify','');
31117 bl.updateElement();
31118 this.owner.fireEvent('editorevent', this);
31121 this.execCmd('styleWithCSS', true); //
31125 // if there is no selection, then we insert, and set the curson inside it..
31126 this.execCmd('styleWithCSS', false);
31136 this.execCmd(cmd, value);
31137 this.owner.fireEvent('editorevent', this);
31138 //this.updateToolbar();
31139 this.owner.deferFocus();
31143 * Executes a Midas editor command directly on the editor document.
31144 * For visual commands, you should use {@link #relayCmd} instead.
31145 * <b>This should only be called after the editor is initialized.</b>
31146 * @param {String} cmd The Midas command
31147 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31149 execCmd : function(cmd, value){
31150 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31157 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31159 * @param {String} text | dom node..
31161 insertAtCursor : function(text)
31164 if(!this.activated){
31168 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31172 // from jquery ui (MIT licenced)
31174 var win = this.win;
31176 if (win.getSelection && win.getSelection().getRangeAt) {
31178 // delete the existing?
31180 this.createRange(this.getSelection()).deleteContents();
31181 range = win.getSelection().getRangeAt(0);
31182 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31183 range.insertNode(node);
31184 range = range.cloneRange();
31185 range.collapse(false);
31187 win.getSelection().removeAllRanges();
31188 win.getSelection().addRange(range);
31192 } else if (win.document.selection && win.document.selection.createRange) {
31193 // no firefox support
31194 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31195 win.document.selection.createRange().pasteHTML(txt);
31198 // no firefox support
31199 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31200 this.execCmd('InsertHTML', txt);
31208 mozKeyPress : function(e){
31210 var c = e.getCharCode(), cmd;
31213 c = String.fromCharCode(c).toLowerCase();
31227 // this.cleanUpPaste.defer(100, this);
31233 this.relayCmd(cmd);
31234 //this.win.focus();
31235 //this.execCmd(cmd);
31236 //this.deferFocus();
31237 e.preventDefault();
31245 fixKeys : function(){ // load time branching for fastest keydown performance
31249 return function(e){
31250 var k = e.getKey(), r;
31253 r = this.doc.selection.createRange();
31256 r.pasteHTML('    ');
31261 /// this is handled by Roo.htmleditor.KeyEnter
31264 r = this.doc.selection.createRange();
31266 var target = r.parentElement();
31267 if(!target || target.tagName.toLowerCase() != 'li'){
31269 r.pasteHTML('<br/>');
31276 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31277 // this.cleanUpPaste.defer(100, this);
31283 }else if(Roo.isOpera){
31284 return function(e){
31285 var k = e.getKey();
31289 this.execCmd('InsertHTML','    ');
31293 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31294 // this.cleanUpPaste.defer(100, this);
31299 }else if(Roo.isSafari){
31300 return function(e){
31301 var k = e.getKey();
31305 this.execCmd('InsertText','\t');
31309 this.mozKeyPress(e);
31311 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31312 // this.cleanUpPaste.defer(100, this);
31320 getAllAncestors: function()
31322 var p = this.getSelectedNode();
31325 a.push(p); // push blank onto stack..
31326 p = this.getParentElement();
31330 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31334 a.push(this.doc.body);
31338 lastSelNode : false,
31341 getSelection : function()
31343 this.assignDocWin();
31344 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31347 * Select a dom node
31348 * @param {DomElement} node the node to select
31350 selectNode : function(node, collapse)
31352 var nodeRange = node.ownerDocument.createRange();
31354 nodeRange.selectNode(node);
31356 nodeRange.selectNodeContents(node);
31358 if (collapse === true) {
31359 nodeRange.collapse(true);
31362 var s = this.win.getSelection();
31363 s.removeAllRanges();
31364 s.addRange(nodeRange);
31367 getSelectedNode: function()
31369 // this may only work on Gecko!!!
31371 // should we cache this!!!!
31375 var range = this.createRange(this.getSelection()).cloneRange();
31378 var parent = range.parentElement();
31380 var testRange = range.duplicate();
31381 testRange.moveToElementText(parent);
31382 if (testRange.inRange(range)) {
31385 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31388 parent = parent.parentElement;
31393 // is ancestor a text element.
31394 var ac = range.commonAncestorContainer;
31395 if (ac.nodeType == 3) {
31396 ac = ac.parentNode;
31399 var ar = ac.childNodes;
31402 var other_nodes = [];
31403 var has_other_nodes = false;
31404 for (var i=0;i<ar.length;i++) {
31405 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31408 // fullly contained node.
31410 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31415 // probably selected..
31416 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31417 other_nodes.push(ar[i]);
31421 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31426 has_other_nodes = true;
31428 if (!nodes.length && other_nodes.length) {
31429 nodes= other_nodes;
31431 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31439 createRange: function(sel)
31441 // this has strange effects when using with
31442 // top toolbar - not sure if it's a great idea.
31443 //this.editor.contentWindow.focus();
31444 if (typeof sel != "undefined") {
31446 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31448 return this.doc.createRange();
31451 return this.doc.createRange();
31454 getParentElement: function()
31457 this.assignDocWin();
31458 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31460 var range = this.createRange(sel);
31463 var p = range.commonAncestorContainer;
31464 while (p.nodeType == 3) { // text node
31475 * Range intersection.. the hard stuff...
31479 * [ -- selected range --- ]
31483 * if end is before start or hits it. fail.
31484 * if start is after end or hits it fail.
31486 * if either hits (but other is outside. - then it's not
31492 // @see http://www.thismuchiknow.co.uk/?p=64.
31493 rangeIntersectsNode : function(range, node)
31495 var nodeRange = node.ownerDocument.createRange();
31497 nodeRange.selectNode(node);
31499 nodeRange.selectNodeContents(node);
31502 var rangeStartRange = range.cloneRange();
31503 rangeStartRange.collapse(true);
31505 var rangeEndRange = range.cloneRange();
31506 rangeEndRange.collapse(false);
31508 var nodeStartRange = nodeRange.cloneRange();
31509 nodeStartRange.collapse(true);
31511 var nodeEndRange = nodeRange.cloneRange();
31512 nodeEndRange.collapse(false);
31514 return rangeStartRange.compareBoundaryPoints(
31515 Range.START_TO_START, nodeEndRange) == -1 &&
31516 rangeEndRange.compareBoundaryPoints(
31517 Range.START_TO_START, nodeStartRange) == 1;
31521 rangeCompareNode : function(range, node)
31523 var nodeRange = node.ownerDocument.createRange();
31525 nodeRange.selectNode(node);
31527 nodeRange.selectNodeContents(node);
31531 range.collapse(true);
31533 nodeRange.collapse(true);
31535 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31536 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
31538 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31540 var nodeIsBefore = ss == 1;
31541 var nodeIsAfter = ee == -1;
31543 if (nodeIsBefore && nodeIsAfter) {
31546 if (!nodeIsBefore && nodeIsAfter) {
31547 return 1; //right trailed.
31550 if (nodeIsBefore && !nodeIsAfter) {
31551 return 2; // left trailed.
31557 cleanWordChars : function(input) {// change the chars to hex code
31560 [ 8211, "–" ],
31561 [ 8212, "—" ],
31569 var output = input;
31570 Roo.each(swapCodes, function(sw) {
31571 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31573 output = output.replace(swapper, sw[1]);
31583 cleanUpChild : function (node)
31586 new Roo.htmleditor.FilterComment({node : node});
31587 new Roo.htmleditor.FilterAttributes({
31589 attrib_black : this.ablack,
31590 attrib_clean : this.aclean,
31591 style_white : this.cwhite,
31592 style_black : this.cblack
31594 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31595 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31601 * Clean up MS wordisms...
31602 * @deprecated - use filter directly
31604 cleanWord : function(node)
31606 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31607 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31614 * @deprecated - use filters
31616 cleanTableWidths : function(node)
31618 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31625 applyBlacklists : function()
31627 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
31628 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
31630 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
31631 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
31632 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
31636 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31637 if (b.indexOf(tag) > -1) {
31640 this.white.push(tag);
31644 Roo.each(w, function(tag) {
31645 if (b.indexOf(tag) > -1) {
31648 if (this.white.indexOf(tag) > -1) {
31651 this.white.push(tag);
31656 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31657 if (w.indexOf(tag) > -1) {
31660 this.black.push(tag);
31664 Roo.each(b, function(tag) {
31665 if (w.indexOf(tag) > -1) {
31668 if (this.black.indexOf(tag) > -1) {
31671 this.black.push(tag);
31676 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
31677 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
31681 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31682 if (b.indexOf(tag) > -1) {
31685 this.cwhite.push(tag);
31689 Roo.each(w, function(tag) {
31690 if (b.indexOf(tag) > -1) {
31693 if (this.cwhite.indexOf(tag) > -1) {
31696 this.cwhite.push(tag);
31701 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31702 if (w.indexOf(tag) > -1) {
31705 this.cblack.push(tag);
31709 Roo.each(b, function(tag) {
31710 if (w.indexOf(tag) > -1) {
31713 if (this.cblack.indexOf(tag) > -1) {
31716 this.cblack.push(tag);
31721 setStylesheets : function(stylesheets)
31723 if(typeof(stylesheets) == 'string'){
31724 Roo.get(this.iframe.contentDocument.head).createChild({
31726 rel : 'stylesheet',
31735 Roo.each(stylesheets, function(s) {
31740 Roo.get(_this.iframe.contentDocument.head).createChild({
31742 rel : 'stylesheet',
31752 updateLanguage : function()
31754 if (!this.iframe || !this.iframe.contentDocument) {
31757 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31761 removeStylesheets : function()
31765 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31770 setStyle : function(style)
31772 Roo.get(this.iframe.contentDocument.head).createChild({
31781 // hide stuff that is not compatible
31795 * @event specialkey
31799 * @cfg {String} fieldClass @hide
31802 * @cfg {String} focusClass @hide
31805 * @cfg {String} autoCreate @hide
31808 * @cfg {String} inputType @hide
31811 * @cfg {String} invalidClass @hide
31814 * @cfg {String} invalidText @hide
31817 * @cfg {String} msgFx @hide
31820 * @cfg {String} validateOnBlur @hide
31824 Roo.HtmlEditorCore.white = [
31825 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31827 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
31828 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
31829 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
31830 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
31831 'TABLE', 'UL', 'XMP',
31833 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
31836 'DIR', 'MENU', 'OL', 'UL', 'DL',
31842 Roo.HtmlEditorCore.black = [
31843 // 'embed', 'object', // enable - backend responsiblity to clean thiese
31845 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
31846 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
31847 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
31848 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
31849 //'FONT' // CLEAN LATER..
31850 'COLGROUP', 'COL' // messy tables.
31854 Roo.HtmlEditorCore.clean = [ // ?? needed???
31855 'SCRIPT', 'STYLE', 'TITLE', 'XML'
31857 Roo.HtmlEditorCore.tag_remove = [
31862 Roo.HtmlEditorCore.ablack = [
31866 Roo.HtmlEditorCore.aclean = [
31867 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
31871 Roo.HtmlEditorCore.pwhite= [
31872 'http', 'https', 'mailto'
31875 // white listed style attributes.
31876 Roo.HtmlEditorCore.cwhite= [
31877 // 'text-align', /// default is to allow most things..
31883 // black listed style attributes.
31884 Roo.HtmlEditorCore.cblack= [
31885 // 'font-size' -- this can be set by the project
31899 * @class Roo.bootstrap.form.HtmlEditor
31900 * @extends Roo.bootstrap.form.TextArea
31901 * Bootstrap HtmlEditor class
31904 * Create a new HtmlEditor
31905 * @param {Object} config The config object
31908 Roo.bootstrap.form.HtmlEditor = function(config){
31909 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31910 if (!this.toolbars) {
31911 this.toolbars = [];
31914 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31917 * @event initialize
31918 * Fires when the editor is fully initialized (including the iframe)
31919 * @param {HtmlEditor} this
31924 * Fires when the editor is first receives the focus. Any insertion must wait
31925 * until after this event.
31926 * @param {HtmlEditor} this
31930 * @event beforesync
31931 * Fires before the textarea is updated with content from the editor iframe. Return false
31932 * to cancel the sync.
31933 * @param {HtmlEditor} this
31934 * @param {String} html
31938 * @event beforepush
31939 * Fires before the iframe editor is updated with content from the textarea. Return false
31940 * to cancel the push.
31941 * @param {HtmlEditor} this
31942 * @param {String} html
31947 * Fires when the textarea is updated with content from the editor iframe.
31948 * @param {HtmlEditor} this
31949 * @param {String} html
31954 * Fires when the iframe editor is updated with content from the textarea.
31955 * @param {HtmlEditor} this
31956 * @param {String} html
31960 * @event editmodechange
31961 * Fires when the editor switches edit modes
31962 * @param {HtmlEditor} this
31963 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31965 editmodechange: true,
31967 * @event editorevent
31968 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31969 * @param {HtmlEditor} this
31973 * @event firstfocus
31974 * Fires when on first focus - needed by toolbars..
31975 * @param {HtmlEditor} this
31980 * Auto save the htmlEditor value as a file into Events
31981 * @param {HtmlEditor} this
31985 * @event savedpreview
31986 * preview the saved version of htmlEditor
31987 * @param {HtmlEditor} this
31994 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
31998 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32003 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32008 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
32013 * @cfg {Number} height (in pixels)
32017 * @cfg {Number} width (in pixels)
32022 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32025 stylesheets: false,
32030 // private properties
32031 validationEvent : false,
32033 initialized : false,
32036 onFocus : Roo.emptyFn,
32038 hideMode:'offsets',
32040 tbContainer : false,
32044 toolbarContainer :function() {
32045 return this.wrap.select('.x-html-editor-tb',true).first();
32049 * Protected method that will not generally be called directly. It
32050 * is called when the editor creates its toolbar. Override this method if you need to
32051 * add custom toolbar buttons.
32052 * @param {HtmlEditor} editor
32054 createToolbar : function(){
32055 Roo.log('renewing');
32056 Roo.log("create toolbars");
32058 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32059 this.toolbars[0].render(this.toolbarContainer());
32063 // if (!editor.toolbars || !editor.toolbars.length) {
32064 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32067 // for (var i =0 ; i < editor.toolbars.length;i++) {
32068 // editor.toolbars[i] = Roo.factory(
32069 // typeof(editor.toolbars[i]) == 'string' ?
32070 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
32071 // Roo.bootstrap.form.HtmlEditor);
32072 // editor.toolbars[i].init(editor);
32078 onRender : function(ct, position)
32080 // Roo.log("Call onRender: " + this.xtype);
32082 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32084 this.wrap = this.inputEl().wrap({
32085 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32088 this.editorcore.onRender(ct, position);
32090 if (this.resizable) {
32091 this.resizeEl = new Roo.Resizable(this.wrap, {
32095 minHeight : this.height,
32096 height: this.height,
32097 handles : this.resizable,
32100 resize : function(r, w, h) {
32101 _t.onResize(w,h); // -something
32107 this.createToolbar(this);
32110 if(!this.width && this.resizable){
32111 this.setSize(this.wrap.getSize());
32113 if (this.resizeEl) {
32114 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32115 // should trigger onReize..
32121 onResize : function(w, h)
32123 Roo.log('resize: ' +w + ',' + h );
32124 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32128 if(this.inputEl() ){
32129 if(typeof w == 'number'){
32130 var aw = w - this.wrap.getFrameWidth('lr');
32131 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32134 if(typeof h == 'number'){
32135 var tbh = -11; // fixme it needs to tool bar size!
32136 for (var i =0; i < this.toolbars.length;i++) {
32137 // fixme - ask toolbars for heights?
32138 tbh += this.toolbars[i].el.getHeight();
32139 //if (this.toolbars[i].footer) {
32140 // tbh += this.toolbars[i].footer.el.getHeight();
32148 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32149 ah -= 5; // knock a few pixes off for look..
32150 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32154 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32155 this.editorcore.onResize(ew,eh);
32160 * Toggles the editor between standard and source edit mode.
32161 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32163 toggleSourceEdit : function(sourceEditMode)
32165 this.editorcore.toggleSourceEdit(sourceEditMode);
32167 if(this.editorcore.sourceEditMode){
32168 Roo.log('editor - showing textarea');
32171 // Roo.log(this.syncValue());
32173 this.inputEl().removeClass(['hide', 'x-hidden']);
32174 this.inputEl().dom.removeAttribute('tabIndex');
32175 this.inputEl().focus();
32177 Roo.log('editor - hiding textarea');
32179 // Roo.log(this.pushValue());
32182 this.inputEl().addClass(['hide', 'x-hidden']);
32183 this.inputEl().dom.setAttribute('tabIndex', -1);
32184 //this.deferFocus();
32187 if(this.resizable){
32188 this.setSize(this.wrap.getSize());
32191 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32194 // private (for BoxComponent)
32195 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32197 // private (for BoxComponent)
32198 getResizeEl : function(){
32202 // private (for BoxComponent)
32203 getPositionEl : function(){
32208 initEvents : function(){
32209 this.originalValue = this.getValue();
32213 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32216 // markInvalid : Roo.emptyFn,
32218 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32221 // clearInvalid : Roo.emptyFn,
32223 setValue : function(v){
32224 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32225 this.editorcore.pushValue();
32230 deferFocus : function(){
32231 this.focus.defer(10, this);
32235 focus : function(){
32236 this.editorcore.focus();
32242 onDestroy : function(){
32248 for (var i =0; i < this.toolbars.length;i++) {
32249 // fixme - ask toolbars for heights?
32250 this.toolbars[i].onDestroy();
32253 this.wrap.dom.innerHTML = '';
32254 this.wrap.remove();
32259 onFirstFocus : function(){
32260 //Roo.log("onFirstFocus");
32261 this.editorcore.onFirstFocus();
32262 for (var i =0; i < this.toolbars.length;i++) {
32263 this.toolbars[i].onFirstFocus();
32269 syncValue : function()
32271 this.editorcore.syncValue();
32274 pushValue : function()
32276 this.editorcore.pushValue();
32280 // hide stuff that is not compatible
32294 * @event specialkey
32298 * @cfg {String} fieldClass @hide
32301 * @cfg {String} focusClass @hide
32304 * @cfg {String} autoCreate @hide
32307 * @cfg {String} inputType @hide
32311 * @cfg {String} invalidText @hide
32314 * @cfg {String} msgFx @hide
32317 * @cfg {String} validateOnBlur @hide
32326 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32328 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32329 * @parent Roo.bootstrap.form.HtmlEditor
32330 * @extends Roo.bootstrap.nav.Simplebar
32336 new Roo.bootstrap.form.HtmlEditor({
32339 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32340 disable : { fonts: 1 , format: 1, ..., ... , ...],
32346 * @cfg {Object} disable List of elements to disable..
32347 * @cfg {Array} btns List of additional buttons.
32351 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32354 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32357 Roo.apply(this, config);
32359 // default disabled, based on 'good practice'..
32360 this.disable = this.disable || {};
32361 Roo.applyIf(this.disable, {
32364 specialElements : true
32366 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32368 this.editor = config.editor;
32369 this.editorcore = config.editor.editorcore;
32371 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32373 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32374 // dont call parent... till later.
32376 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
32381 editorcore : false,
32386 "h1","h2","h3","h4","h5","h6",
32388 "abbr", "acronym", "address", "cite", "samp", "var",
32392 onRender : function(ct, position)
32394 // Roo.log("Call onRender: " + this.xtype);
32396 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32398 this.el.dom.style.marginBottom = '0';
32400 var editorcore = this.editorcore;
32401 var editor= this.editor;
32404 var btn = function(id,cmd , toggle, handler, html){
32406 var event = toggle ? 'toggle' : 'click';
32411 xns: Roo.bootstrap,
32415 enableToggle:toggle !== false,
32417 pressed : toggle ? false : null,
32420 a.listeners[toggle ? 'toggle' : 'click'] = function() {
32421 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
32427 // var cb_box = function...
32432 xns: Roo.bootstrap,
32437 xns: Roo.bootstrap,
32441 Roo.each(this.formats, function(f) {
32442 style.menu.items.push({
32444 xns: Roo.bootstrap,
32445 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32450 editorcore.insertTag(this.tagname);
32457 children.push(style);
32459 btn('bold',false,true);
32460 btn('italic',false,true);
32461 btn('align-left', 'justifyleft',true);
32462 btn('align-center', 'justifycenter',true);
32463 btn('align-right' , 'justifyright',true);
32464 btn('link', false, false, function(btn) {
32465 //Roo.log("create link?");
32466 var url = prompt(this.createLinkText, this.defaultLinkValue);
32467 if(url && url != 'http:/'+'/'){
32468 this.editorcore.relayCmd('createlink', url);
32471 btn('list','insertunorderedlist',true);
32472 btn('pencil', false,true, function(btn){
32474 this.toggleSourceEdit(btn.pressed);
32477 if (this.editor.btns.length > 0) {
32478 for (var i = 0; i<this.editor.btns.length; i++) {
32479 children.push(this.editor.btns[i]);
32487 xns: Roo.bootstrap,
32492 xns: Roo.bootstrap,
32497 cog.menu.items.push({
32499 xns: Roo.bootstrap,
32500 html : Clean styles,
32505 editorcore.insertTag(this.tagname);
32514 this.xtype = 'NavSimplebar';
32516 for(var i=0;i< children.length;i++) {
32518 this.buttons.add(this.addxtypeChild(children[i]));
32522 editor.on('editorevent', this.updateToolbar, this);
32524 onBtnClick : function(id)
32526 this.editorcore.relayCmd(id);
32527 this.editorcore.focus();
32531 * Protected method that will not generally be called directly. It triggers
32532 * a toolbar update by reading the markup state of the current selection in the editor.
32534 updateToolbar: function(){
32536 if(!this.editorcore.activated){
32537 this.editor.onFirstFocus(); // is this neeed?
32541 var btns = this.buttons;
32542 var doc = this.editorcore.doc;
32543 btns.get('bold').setActive(doc.queryCommandState('bold'));
32544 btns.get('italic').setActive(doc.queryCommandState('italic'));
32545 //btns.get('underline').setActive(doc.queryCommandState('underline'));
32547 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32548 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32549 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32551 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32552 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32555 var ans = this.editorcore.getAllAncestors();
32556 if (this.formatCombo) {
32559 var store = this.formatCombo.store;
32560 this.formatCombo.setValue("");
32561 for (var i =0; i < ans.length;i++) {
32562 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32564 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32572 // hides menus... - so this cant be on a menu...
32573 Roo.bootstrap.MenuMgr.hideAll();
32575 Roo.bootstrap.menu.Manager.hideAll();
32576 //this.editorsyncValue();
32578 onFirstFocus: function() {
32579 this.buttons.each(function(item){
32583 toggleSourceEdit : function(sourceEditMode){
32586 if(sourceEditMode){
32587 Roo.log("disabling buttons");
32588 this.buttons.each( function(item){
32589 if(item.cmd != 'pencil'){
32595 Roo.log("enabling buttons");
32596 if(this.editorcore.initialized){
32597 this.buttons.each( function(item){
32603 Roo.log("calling toggole on editor");
32604 // tell the editor that it's been pressed..
32605 this.editor.toggleSourceEdit(sourceEditMode);
32619 * @class Roo.bootstrap.form.Markdown
32620 * @extends Roo.bootstrap.form.TextArea
32621 * Bootstrap Showdown editable area
32622 * @cfg {string} content
32625 * Create a new Showdown
32628 Roo.bootstrap.form.Markdown = function(config){
32629 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32633 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
32637 initEvents : function()
32640 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32641 this.markdownEl = this.el.createChild({
32642 cls : 'roo-markdown-area'
32644 this.inputEl().addClass('d-none');
32645 if (this.getValue() == '') {
32646 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32649 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32651 this.markdownEl.on('click', this.toggleTextEdit, this);
32652 this.on('blur', this.toggleTextEdit, this);
32653 this.on('specialkey', this.resizeTextArea, this);
32656 toggleTextEdit : function()
32658 var sh = this.markdownEl.getHeight();
32659 this.inputEl().addClass('d-none');
32660 this.markdownEl.addClass('d-none');
32661 if (!this.editing) {
32663 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32664 this.inputEl().removeClass('d-none');
32665 this.inputEl().focus();
32666 this.editing = true;
32669 // show showdown...
32670 this.updateMarkdown();
32671 this.markdownEl.removeClass('d-none');
32672 this.editing = false;
32675 updateMarkdown : function()
32677 if (this.getValue() == '') {
32678 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32682 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32685 resizeTextArea: function () {
32688 Roo.log([sh, this.getValue().split("\n").length * 30]);
32689 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32691 setValue : function(val)
32693 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32694 if (!this.editing) {
32695 this.updateMarkdown();
32701 if (!this.editing) {
32702 this.toggleTextEdit();
32710 * Ext JS Library 1.1.1
32711 * Copyright(c) 2006-2007, Ext JS, LLC.
32713 * Originally Released Under LGPL - original licence link has changed is not relivant.
32716 * <script type="text/javascript">
32720 * @class Roo.bootstrap.PagingToolbar
32721 * @extends Roo.bootstrap.nav.Simplebar
32722 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32724 * Create a new PagingToolbar
32725 * @param {Object} config The config object
32726 * @param {Roo.data.Store} store
32728 Roo.bootstrap.PagingToolbar = function(config)
32730 // old args format still supported... - xtype is prefered..
32731 // created from xtype...
32733 this.ds = config.dataSource;
32735 if (config.store && !this.ds) {
32736 this.store= Roo.factory(config.store, Roo.data);
32737 this.ds = this.store;
32738 this.ds.xmodule = this.xmodule || false;
32741 this.toolbarItems = [];
32742 if (config.items) {
32743 this.toolbarItems = config.items;
32746 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32751 this.bind(this.ds);
32754 if (Roo.bootstrap.version == 4) {
32755 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32757 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32762 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32764 * @cfg {Roo.bootstrap.Button} buttons[]
32765 * Buttons for the toolbar
32768 * @cfg {Roo.data.Store} store
32769 * The underlying data store providing the paged data
32772 * @cfg {String/HTMLElement/Element} container
32773 * container The id or element that will contain the toolbar
32776 * @cfg {Boolean} displayInfo
32777 * True to display the displayMsg (defaults to false)
32780 * @cfg {Number} pageSize
32781 * The number of records to display per page (defaults to 20)
32785 * @cfg {String} displayMsg
32786 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32788 displayMsg : 'Displaying {0} - {1} of {2}',
32790 * @cfg {String} emptyMsg
32791 * The message to display when no records are found (defaults to "No data to display")
32793 emptyMsg : 'No data to display',
32795 * Customizable piece of the default paging text (defaults to "Page")
32798 beforePageText : "Page",
32800 * Customizable piece of the default paging text (defaults to "of %0")
32803 afterPageText : "of {0}",
32805 * Customizable piece of the default paging text (defaults to "First Page")
32808 firstText : "First Page",
32810 * Customizable piece of the default paging text (defaults to "Previous Page")
32813 prevText : "Previous Page",
32815 * Customizable piece of the default paging text (defaults to "Next Page")
32818 nextText : "Next Page",
32820 * Customizable piece of the default paging text (defaults to "Last Page")
32823 lastText : "Last Page",
32825 * Customizable piece of the default paging text (defaults to "Refresh")
32828 refreshText : "Refresh",
32832 onRender : function(ct, position)
32834 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32835 this.navgroup.parentId = this.id;
32836 this.navgroup.onRender(this.el, null);
32837 // add the buttons to the navgroup
32839 if(this.displayInfo){
32840 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32841 this.displayEl = this.el.select('.x-paging-info', true).first();
32842 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32843 // this.displayEl = navel.el.select('span',true).first();
32849 Roo.each(_this.buttons, function(e){ // this might need to use render????
32850 Roo.factory(e).render(_this.el);
32854 Roo.each(_this.toolbarItems, function(e) {
32855 _this.navgroup.addItem(e);
32859 this.first = this.navgroup.addItem({
32860 tooltip: this.firstText,
32861 cls: "prev btn-outline-secondary",
32862 html : ' <i class="fa fa-step-backward"></i>',
32864 preventDefault: true,
32865 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32868 this.prev = this.navgroup.addItem({
32869 tooltip: this.prevText,
32870 cls: "prev btn-outline-secondary",
32871 html : ' <i class="fa fa-backward"></i>',
32873 preventDefault: true,
32874 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
32876 //this.addSeparator();
32879 var field = this.navgroup.addItem( {
32881 cls : 'x-paging-position btn-outline-secondary',
32883 html : this.beforePageText +
32884 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32885 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
32888 this.field = field.el.select('input', true).first();
32889 this.field.on("keydown", this.onPagingKeydown, this);
32890 this.field.on("focus", function(){this.dom.select();});
32893 this.afterTextEl = field.el.select('.x-paging-after',true).first();
32894 //this.field.setHeight(18);
32895 //this.addSeparator();
32896 this.next = this.navgroup.addItem({
32897 tooltip: this.nextText,
32898 cls: "next btn-outline-secondary",
32899 html : ' <i class="fa fa-forward"></i>',
32901 preventDefault: true,
32902 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
32904 this.last = this.navgroup.addItem({
32905 tooltip: this.lastText,
32906 html : ' <i class="fa fa-step-forward"></i>',
32907 cls: "next btn-outline-secondary",
32909 preventDefault: true,
32910 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
32912 //this.addSeparator();
32913 this.loading = this.navgroup.addItem({
32914 tooltip: this.refreshText,
32915 cls: "btn-outline-secondary",
32916 html : ' <i class="fa fa-refresh"></i>',
32917 preventDefault: true,
32918 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32924 updateInfo : function(){
32925 if(this.displayEl){
32926 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32927 var msg = count == 0 ?
32931 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32933 this.displayEl.update(msg);
32938 onLoad : function(ds, r, o)
32940 this.cursor = o.params && o.params.start ? o.params.start : 0;
32942 var d = this.getPageData(),
32947 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32948 this.field.dom.value = ap;
32949 this.first.setDisabled(ap == 1);
32950 this.prev.setDisabled(ap == 1);
32951 this.next.setDisabled(ap == ps);
32952 this.last.setDisabled(ap == ps);
32953 this.loading.enable();
32958 getPageData : function(){
32959 var total = this.ds.getTotalCount();
32962 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32963 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32968 onLoadError : function(proxy, o){
32969 this.loading.enable();
32970 if (this.ds.events.loadexception.listeners.length < 2) {
32971 // nothing has been assigned to loadexception except this...
32973 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32979 onPagingKeydown : function(e){
32980 var k = e.getKey();
32981 var d = this.getPageData();
32983 var v = this.field.dom.value, pageNum;
32984 if(!v || isNaN(pageNum = parseInt(v, 10))){
32985 this.field.dom.value = d.activePage;
32988 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32989 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32992 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))
32994 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32995 this.field.dom.value = pageNum;
32996 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32999 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33001 var v = this.field.dom.value, pageNum;
33002 var increment = (e.shiftKey) ? 10 : 1;
33003 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33006 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33007 this.field.dom.value = d.activePage;
33010 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33012 this.field.dom.value = parseInt(v, 10) + increment;
33013 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33014 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33021 beforeLoad : function(){
33023 this.loading.disable();
33028 onClick : function(which){
33037 ds.load({params:{start: 0, limit: this.pageSize}});
33040 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33043 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33046 var total = ds.getTotalCount();
33047 var extra = total % this.pageSize;
33048 var lastStart = extra ? (total - extra) : total-this.pageSize;
33049 ds.load({params:{start: lastStart, limit: this.pageSize}});
33052 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33058 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33059 * @param {Roo.data.Store} store The data store to unbind
33061 unbind : function(ds){
33062 ds.un("beforeload", this.beforeLoad, this);
33063 ds.un("load", this.onLoad, this);
33064 ds.un("loadexception", this.onLoadError, this);
33065 ds.un("remove", this.updateInfo, this);
33066 ds.un("add", this.updateInfo, this);
33067 this.ds = undefined;
33071 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33072 * @param {Roo.data.Store} store The data store to bind
33074 bind : function(ds){
33075 ds.on("beforeload", this.beforeLoad, this);
33076 ds.on("load", this.onLoad, this);
33077 ds.on("loadexception", this.onLoadError, this);
33078 ds.on("remove", this.updateInfo, this);
33079 ds.on("add", this.updateInfo, this);
33090 * @class Roo.bootstrap.MessageBar
33091 * @extends Roo.bootstrap.Component
33092 * Bootstrap MessageBar class
33093 * @cfg {String} html contents of the MessageBar
33094 * @cfg {String} weight (info | success | warning | danger) default info
33095 * @cfg {String} beforeClass insert the bar before the given class
33096 * @cfg {Boolean} closable (true | false) default false
33097 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33100 * Create a new Element
33101 * @param {Object} config The config object
33104 Roo.bootstrap.MessageBar = function(config){
33105 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33108 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33114 beforeClass: 'bootstrap-sticky-wrap',
33116 getAutoCreate : function(){
33120 cls: 'alert alert-dismissable alert-' + this.weight,
33125 html: this.html || ''
33131 cfg.cls += ' alert-messages-fixed';
33145 onRender : function(ct, position)
33147 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33150 var cfg = Roo.apply({}, this.getAutoCreate());
33154 cfg.cls += ' ' + this.cls;
33157 cfg.style = this.style;
33159 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33161 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33164 this.el.select('>button.close').on('click', this.hide, this);
33170 if (!this.rendered) {
33176 this.fireEvent('show', this);
33182 if (!this.rendered) {
33188 this.fireEvent('hide', this);
33191 update : function()
33193 // var e = this.el.dom.firstChild;
33195 // if(this.closable){
33196 // e = e.nextSibling;
33199 // e.data = this.html || '';
33201 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33217 * @class Roo.bootstrap.Graph
33218 * @extends Roo.bootstrap.Component
33219 * Bootstrap Graph class
33223 @cfg {String} graphtype bar | vbar | pie
33224 @cfg {number} g_x coodinator | centre x (pie)
33225 @cfg {number} g_y coodinator | centre y (pie)
33226 @cfg {number} g_r radius (pie)
33227 @cfg {number} g_height height of the chart (respected by all elements in the set)
33228 @cfg {number} g_width width of the chart (respected by all elements in the set)
33229 @cfg {Object} title The title of the chart
33232 -opts (object) options for the chart
33234 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33235 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33237 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.
33238 o stacked (boolean) whether or not to tread values as in a stacked bar chart
33240 o stretch (boolean)
33242 -opts (object) options for the pie
33245 o startAngle (number)
33246 o endAngle (number)
33250 * Create a new Input
33251 * @param {Object} config The config object
33254 Roo.bootstrap.Graph = function(config){
33255 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33261 * The img click event for the img.
33262 * @param {Roo.EventObject} e
33268 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
33279 //g_colors: this.colors,
33286 getAutoCreate : function(){
33297 onRender : function(ct,position){
33300 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33302 if (typeof(Raphael) == 'undefined') {
33303 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33307 this.raphael = Raphael(this.el.dom);
33309 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33310 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33311 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33312 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33314 r.text(160, 10, "Single Series Chart").attr(txtattr);
33315 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33316 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33317 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33319 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33320 r.barchart(330, 10, 300, 220, data1);
33321 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33322 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33325 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33326 // r.barchart(30, 30, 560, 250, xdata, {
33327 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33328 // axis : "0 0 1 1",
33329 // axisxlabels : xdata
33330 // //yvalues : cols,
33333 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33335 // this.load(null,xdata,{
33336 // axis : "0 0 1 1",
33337 // axisxlabels : xdata
33342 load : function(graphtype,xdata,opts)
33344 this.raphael.clear();
33346 graphtype = this.graphtype;
33351 var r = this.raphael,
33352 fin = function () {
33353 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33355 fout = function () {
33356 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33358 pfin = function() {
33359 this.sector.stop();
33360 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33363 this.label[0].stop();
33364 this.label[0].attr({ r: 7.5 });
33365 this.label[1].attr({ "font-weight": 800 });
33368 pfout = function() {
33369 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33372 this.label[0].animate({ r: 5 }, 500, "bounce");
33373 this.label[1].attr({ "font-weight": 400 });
33379 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33382 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33385 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
33386 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33388 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33395 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33400 setTitle: function(o)
33405 initEvents: function() {
33408 this.el.on('click', this.onClick, this);
33412 onClick : function(e)
33414 Roo.log('img onclick');
33415 this.fireEvent('click', this, e);
33421 Roo.bootstrap.dash = {};/*
33427 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33430 * @class Roo.bootstrap.dash.NumberBox
33431 * @extends Roo.bootstrap.Component
33432 * Bootstrap NumberBox class
33433 * @cfg {String} headline Box headline
33434 * @cfg {String} content Box content
33435 * @cfg {String} icon Box icon
33436 * @cfg {String} footer Footer text
33437 * @cfg {String} fhref Footer href
33440 * Create a new NumberBox
33441 * @param {Object} config The config object
33445 Roo.bootstrap.dash.NumberBox = function(config){
33446 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33450 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
33459 getAutoCreate : function(){
33463 cls : 'small-box ',
33471 cls : 'roo-headline',
33472 html : this.headline
33476 cls : 'roo-content',
33477 html : this.content
33491 cls : 'ion ' + this.icon
33500 cls : 'small-box-footer',
33501 href : this.fhref || '#',
33505 cfg.cn.push(footer);
33512 onRender : function(ct,position){
33513 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33520 setHeadline: function (value)
33522 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33525 setFooter: function (value, href)
33527 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33530 this.el.select('a.small-box-footer',true).first().attr('href', href);
33535 setContent: function (value)
33537 this.el.select('.roo-content',true).first().dom.innerHTML = value;
33540 initEvents: function()
33554 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33557 * @class Roo.bootstrap.dash.TabBox
33558 * @extends Roo.bootstrap.Component
33559 * @children Roo.bootstrap.dash.TabPane
33560 * Bootstrap TabBox class
33561 * @cfg {String} title Title of the TabBox
33562 * @cfg {String} icon Icon of the TabBox
33563 * @cfg {Boolean} showtabs (true|false) show the tabs default true
33564 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33567 * Create a new TabBox
33568 * @param {Object} config The config object
33572 Roo.bootstrap.dash.TabBox = function(config){
33573 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33578 * When a pane is added
33579 * @param {Roo.bootstrap.dash.TabPane} pane
33583 * @event activatepane
33584 * When a pane is activated
33585 * @param {Roo.bootstrap.dash.TabPane} pane
33587 "activatepane" : true
33595 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
33600 tabScrollable : false,
33602 getChildContainer : function()
33604 return this.el.select('.tab-content', true).first();
33607 getAutoCreate : function(){
33611 cls: 'pull-left header',
33619 cls: 'fa ' + this.icon
33625 cls: 'nav nav-tabs pull-right',
33631 if(this.tabScrollable){
33638 cls: 'nav nav-tabs pull-right',
33649 cls: 'nav-tabs-custom',
33654 cls: 'tab-content no-padding',
33662 initEvents : function()
33664 //Roo.log('add add pane handler');
33665 this.on('addpane', this.onAddPane, this);
33668 * Updates the box title
33669 * @param {String} html to set the title to.
33671 setTitle : function(value)
33673 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33675 onAddPane : function(pane)
33677 this.panes.push(pane);
33678 //Roo.log('addpane');
33680 // tabs are rendere left to right..
33681 if(!this.showtabs){
33685 var ctr = this.el.select('.nav-tabs', true).first();
33688 var existing = ctr.select('.nav-tab',true);
33689 var qty = existing.getCount();;
33692 var tab = ctr.createChild({
33694 cls : 'nav-tab' + (qty ? '' : ' active'),
33702 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33705 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33707 pane.el.addClass('active');
33712 onTabClick : function(ev,un,ob,pane)
33714 //Roo.log('tab - prev default');
33715 ev.preventDefault();
33718 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33719 pane.tab.addClass('active');
33720 //Roo.log(pane.title);
33721 this.getChildContainer().select('.tab-pane',true).removeClass('active');
33722 // technically we should have a deactivate event.. but maybe add later.
33723 // and it should not de-activate the selected tab...
33724 this.fireEvent('activatepane', pane);
33725 pane.el.addClass('active');
33726 pane.fireEvent('activate');
33731 getActivePane : function()
33734 Roo.each(this.panes, function(p) {
33735 if(p.el.hasClass('active')){
33756 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33758 * @class Roo.bootstrap.TabPane
33759 * @extends Roo.bootstrap.Component
33760 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
33761 * Bootstrap TabPane class
33762 * @cfg {Boolean} active (false | true) Default false
33763 * @cfg {String} title title of panel
33767 * Create a new TabPane
33768 * @param {Object} config The config object
33771 Roo.bootstrap.dash.TabPane = function(config){
33772 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33778 * When a pane is activated
33779 * @param {Roo.bootstrap.dash.TabPane} pane
33786 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
33791 // the tabBox that this is attached to.
33794 getAutoCreate : function()
33802 cfg.cls += ' active';
33807 initEvents : function()
33809 //Roo.log('trigger add pane handler');
33810 this.parent().fireEvent('addpane', this)
33814 * Updates the tab title
33815 * @param {String} html to set the title to.
33817 setTitle: function(str)
33823 this.tab.select('a', true).first().dom.innerHTML = str;
33842 * @class Roo.bootstrap.Tooltip
33843 * Bootstrap Tooltip class
33844 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33845 * to determine which dom element triggers the tooltip.
33847 * It needs to add support for additional attributes like tooltip-position
33850 * Create a new Toolti
33851 * @param {Object} config The config object
33854 Roo.bootstrap.Tooltip = function(config){
33855 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33857 this.alignment = Roo.bootstrap.Tooltip.alignment;
33859 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33860 this.alignment = config.alignment;
33865 Roo.apply(Roo.bootstrap.Tooltip, {
33867 * @function init initialize tooltip monitoring.
33871 currentTip : false,
33872 currentRegion : false,
33878 Roo.get(document).on('mouseover', this.enter ,this);
33879 Roo.get(document).on('mouseout', this.leave, this);
33882 this.currentTip = new Roo.bootstrap.Tooltip();
33885 enter : function(ev)
33887 var dom = ev.getTarget();
33889 //Roo.log(['enter',dom]);
33890 var el = Roo.fly(dom);
33891 if (this.currentEl) {
33893 //Roo.log(this.currentEl);
33894 //Roo.log(this.currentEl.contains(dom));
33895 if (this.currentEl == el) {
33898 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33904 if (this.currentTip.el) {
33905 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33909 if(!el || el.dom == document){
33915 if (!el.attr('tooltip')) {
33916 pel = el.findParent("[tooltip]");
33918 bindEl = Roo.get(pel);
33924 // you can not look for children, as if el is the body.. then everythign is the child..
33925 if (!pel && !el.attr('tooltip')) { //
33926 if (!el.select("[tooltip]").elements.length) {
33929 // is the mouse over this child...?
33930 bindEl = el.select("[tooltip]").first();
33931 var xy = ev.getXY();
33932 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33933 //Roo.log("not in region.");
33936 //Roo.log("child element over..");
33939 this.currentEl = el;
33940 this.currentTip.bind(bindEl);
33941 this.currentRegion = Roo.lib.Region.getRegion(dom);
33942 this.currentTip.enter();
33945 leave : function(ev)
33947 var dom = ev.getTarget();
33948 //Roo.log(['leave',dom]);
33949 if (!this.currentEl) {
33954 if (dom != this.currentEl.dom) {
33957 var xy = ev.getXY();
33958 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
33961 // only activate leave if mouse cursor is outside... bounding box..
33966 if (this.currentTip) {
33967 this.currentTip.leave();
33969 //Roo.log('clear currentEl');
33970 this.currentEl = false;
33975 'left' : ['r-l', [-2,0], 'right'],
33976 'right' : ['l-r', [2,0], 'left'],
33977 'bottom' : ['t-b', [0,2], 'top'],
33978 'top' : [ 'b-t', [0,-2], 'bottom']
33984 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
33989 delay : null, // can be { show : 300 , hide: 500}
33993 hoverState : null, //???
33995 placement : 'bottom',
33999 getAutoCreate : function(){
34006 cls : 'tooltip-arrow arrow'
34009 cls : 'tooltip-inner'
34016 bind : function(el)
34021 initEvents : function()
34023 this.arrowEl = this.el.select('.arrow', true).first();
34024 this.innerEl = this.el.select('.tooltip-inner', true).first();
34027 enter : function () {
34029 if (this.timeout != null) {
34030 clearTimeout(this.timeout);
34033 this.hoverState = 'in';
34034 //Roo.log("enter - show");
34035 if (!this.delay || !this.delay.show) {
34040 this.timeout = setTimeout(function () {
34041 if (_t.hoverState == 'in') {
34044 }, this.delay.show);
34048 clearTimeout(this.timeout);
34050 this.hoverState = 'out';
34051 if (!this.delay || !this.delay.hide) {
34057 this.timeout = setTimeout(function () {
34058 //Roo.log("leave - timeout");
34060 if (_t.hoverState == 'out') {
34062 Roo.bootstrap.Tooltip.currentEl = false;
34067 show : function (msg)
34070 this.render(document.body);
34073 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34075 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34077 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34079 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34080 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34082 if(this.bindEl.attr('tooltip-class')) {
34083 this.el.addClass(this.bindEl.attr('tooltip-class'));
34086 var placement = typeof this.placement == 'function' ?
34087 this.placement.call(this, this.el, on_el) :
34090 if(this.bindEl.attr('tooltip-placement')) {
34091 placement = this.bindEl.attr('tooltip-placement');
34094 var autoToken = /\s?auto?\s?/i;
34095 var autoPlace = autoToken.test(placement);
34097 placement = placement.replace(autoToken, '') || 'top';
34101 //this.el.setXY([0,0]);
34103 //this.el.dom.style.display='block';
34105 //this.el.appendTo(on_el);
34107 var p = this.getPosition();
34108 var box = this.el.getBox();
34114 var align = this.alignment[placement];
34116 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34118 if(placement == 'top' || placement == 'bottom'){
34120 placement = 'right';
34123 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34124 placement = 'left';
34127 var scroll = Roo.select('body', true).first().getScroll();
34129 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34133 align = this.alignment[placement];
34135 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34139 var elems = document.getElementsByTagName('div');
34140 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34141 for (var i = 0; i < elems.length; i++) {
34142 var zindex = Number.parseInt(
34143 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34146 if (zindex > highest) {
34153 this.el.dom.style.zIndex = highest;
34155 this.el.alignTo(this.bindEl, align[0],align[1]);
34156 //var arrow = this.el.select('.arrow',true).first();
34157 //arrow.set(align[2],
34159 this.el.addClass(placement);
34160 this.el.addClass("bs-tooltip-"+ placement);
34162 this.el.addClass('in fade show');
34164 this.hoverState = null;
34166 if (this.el.hasClass('fade')) {
34181 //this.el.setXY([0,0]);
34182 if(this.bindEl.attr('tooltip-class')) {
34183 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34185 this.el.removeClass(['show', 'in']);
34201 * @class Roo.bootstrap.LocationPicker
34202 * @extends Roo.bootstrap.Component
34203 * Bootstrap LocationPicker class
34204 * @cfg {Number} latitude Position when init default 0
34205 * @cfg {Number} longitude Position when init default 0
34206 * @cfg {Number} zoom default 15
34207 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34208 * @cfg {Boolean} mapTypeControl default false
34209 * @cfg {Boolean} disableDoubleClickZoom default false
34210 * @cfg {Boolean} scrollwheel default true
34211 * @cfg {Boolean} streetViewControl default false
34212 * @cfg {Number} radius default 0
34213 * @cfg {String} locationName
34214 * @cfg {Boolean} draggable default true
34215 * @cfg {Boolean} enableAutocomplete default false
34216 * @cfg {Boolean} enableReverseGeocode default true
34217 * @cfg {String} markerTitle
34220 * Create a new LocationPicker
34221 * @param {Object} config The config object
34225 Roo.bootstrap.LocationPicker = function(config){
34227 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34232 * Fires when the picker initialized.
34233 * @param {Roo.bootstrap.LocationPicker} this
34234 * @param {Google Location} location
34238 * @event positionchanged
34239 * Fires when the picker position changed.
34240 * @param {Roo.bootstrap.LocationPicker} this
34241 * @param {Google Location} location
34243 positionchanged : true,
34246 * Fires when the map resize.
34247 * @param {Roo.bootstrap.LocationPicker} this
34252 * Fires when the map show.
34253 * @param {Roo.bootstrap.LocationPicker} this
34258 * Fires when the map hide.
34259 * @param {Roo.bootstrap.LocationPicker} this
34264 * Fires when click the map.
34265 * @param {Roo.bootstrap.LocationPicker} this
34266 * @param {Map event} e
34270 * @event mapRightClick
34271 * Fires when right click the map.
34272 * @param {Roo.bootstrap.LocationPicker} this
34273 * @param {Map event} e
34275 mapRightClick : true,
34277 * @event markerClick
34278 * Fires when click the marker.
34279 * @param {Roo.bootstrap.LocationPicker} this
34280 * @param {Map event} e
34282 markerClick : true,
34284 * @event markerRightClick
34285 * Fires when right click the marker.
34286 * @param {Roo.bootstrap.LocationPicker} this
34287 * @param {Map event} e
34289 markerRightClick : true,
34291 * @event OverlayViewDraw
34292 * Fires when OverlayView Draw
34293 * @param {Roo.bootstrap.LocationPicker} this
34295 OverlayViewDraw : true,
34297 * @event OverlayViewOnAdd
34298 * Fires when OverlayView Draw
34299 * @param {Roo.bootstrap.LocationPicker} this
34301 OverlayViewOnAdd : true,
34303 * @event OverlayViewOnRemove
34304 * Fires when OverlayView Draw
34305 * @param {Roo.bootstrap.LocationPicker} this
34307 OverlayViewOnRemove : true,
34309 * @event OverlayViewShow
34310 * Fires when OverlayView Draw
34311 * @param {Roo.bootstrap.LocationPicker} this
34312 * @param {Pixel} cpx
34314 OverlayViewShow : true,
34316 * @event OverlayViewHide
34317 * Fires when OverlayView Draw
34318 * @param {Roo.bootstrap.LocationPicker} this
34320 OverlayViewHide : true,
34322 * @event loadexception
34323 * Fires when load google lib failed.
34324 * @param {Roo.bootstrap.LocationPicker} this
34326 loadexception : true
34331 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
34333 gMapContext: false,
34339 mapTypeControl: false,
34340 disableDoubleClickZoom: false,
34342 streetViewControl: false,
34346 enableAutocomplete: false,
34347 enableReverseGeocode: true,
34350 getAutoCreate: function()
34355 cls: 'roo-location-picker'
34361 initEvents: function(ct, position)
34363 if(!this.el.getWidth() || this.isApplied()){
34367 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34372 initial: function()
34374 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34375 this.fireEvent('loadexception', this);
34379 if(!this.mapTypeId){
34380 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34383 this.gMapContext = this.GMapContext();
34385 this.initOverlayView();
34387 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34391 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34392 _this.setPosition(_this.gMapContext.marker.position);
34395 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34396 _this.fireEvent('mapClick', this, event);
34400 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34401 _this.fireEvent('mapRightClick', this, event);
34405 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34406 _this.fireEvent('markerClick', this, event);
34410 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34411 _this.fireEvent('markerRightClick', this, event);
34415 this.setPosition(this.gMapContext.location);
34417 this.fireEvent('initial', this, this.gMapContext.location);
34420 initOverlayView: function()
34424 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34428 _this.fireEvent('OverlayViewDraw', _this);
34433 _this.fireEvent('OverlayViewOnAdd', _this);
34436 onRemove: function()
34438 _this.fireEvent('OverlayViewOnRemove', _this);
34441 show: function(cpx)
34443 _this.fireEvent('OverlayViewShow', _this, cpx);
34448 _this.fireEvent('OverlayViewHide', _this);
34454 fromLatLngToContainerPixel: function(event)
34456 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34459 isApplied: function()
34461 return this.getGmapContext() == false ? false : true;
34464 getGmapContext: function()
34466 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34469 GMapContext: function()
34471 var position = new google.maps.LatLng(this.latitude, this.longitude);
34473 var _map = new google.maps.Map(this.el.dom, {
34476 mapTypeId: this.mapTypeId,
34477 mapTypeControl: this.mapTypeControl,
34478 disableDoubleClickZoom: this.disableDoubleClickZoom,
34479 scrollwheel: this.scrollwheel,
34480 streetViewControl: this.streetViewControl,
34481 locationName: this.locationName,
34482 draggable: this.draggable,
34483 enableAutocomplete: this.enableAutocomplete,
34484 enableReverseGeocode: this.enableReverseGeocode
34487 var _marker = new google.maps.Marker({
34488 position: position,
34490 title: this.markerTitle,
34491 draggable: this.draggable
34498 location: position,
34499 radius: this.radius,
34500 locationName: this.locationName,
34501 addressComponents: {
34502 formatted_address: null,
34503 addressLine1: null,
34504 addressLine2: null,
34506 streetNumber: null,
34510 stateOrProvince: null
34513 domContainer: this.el.dom,
34514 geodecoder: new google.maps.Geocoder()
34518 drawCircle: function(center, radius, options)
34520 if (this.gMapContext.circle != null) {
34521 this.gMapContext.circle.setMap(null);
34525 options = Roo.apply({}, options, {
34526 strokeColor: "#0000FF",
34527 strokeOpacity: .35,
34529 fillColor: "#0000FF",
34533 options.map = this.gMapContext.map;
34534 options.radius = radius;
34535 options.center = center;
34536 this.gMapContext.circle = new google.maps.Circle(options);
34537 return this.gMapContext.circle;
34543 setPosition: function(location)
34545 this.gMapContext.location = location;
34546 this.gMapContext.marker.setPosition(location);
34547 this.gMapContext.map.panTo(location);
34548 this.drawCircle(location, this.gMapContext.radius, {});
34552 if (this.gMapContext.settings.enableReverseGeocode) {
34553 this.gMapContext.geodecoder.geocode({
34554 latLng: this.gMapContext.location
34555 }, function(results, status) {
34557 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34558 _this.gMapContext.locationName = results[0].formatted_address;
34559 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34561 _this.fireEvent('positionchanged', this, location);
34568 this.fireEvent('positionchanged', this, location);
34573 google.maps.event.trigger(this.gMapContext.map, "resize");
34575 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34577 this.fireEvent('resize', this);
34580 setPositionByLatLng: function(latitude, longitude)
34582 this.setPosition(new google.maps.LatLng(latitude, longitude));
34585 getCurrentPosition: function()
34588 latitude: this.gMapContext.location.lat(),
34589 longitude: this.gMapContext.location.lng()
34593 getAddressName: function()
34595 return this.gMapContext.locationName;
34598 getAddressComponents: function()
34600 return this.gMapContext.addressComponents;
34603 address_component_from_google_geocode: function(address_components)
34607 for (var i = 0; i < address_components.length; i++) {
34608 var component = address_components[i];
34609 if (component.types.indexOf("postal_code") >= 0) {
34610 result.postalCode = component.short_name;
34611 } else if (component.types.indexOf("street_number") >= 0) {
34612 result.streetNumber = component.short_name;
34613 } else if (component.types.indexOf("route") >= 0) {
34614 result.streetName = component.short_name;
34615 } else if (component.types.indexOf("neighborhood") >= 0) {
34616 result.city = component.short_name;
34617 } else if (component.types.indexOf("locality") >= 0) {
34618 result.city = component.short_name;
34619 } else if (component.types.indexOf("sublocality") >= 0) {
34620 result.district = component.short_name;
34621 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34622 result.stateOrProvince = component.short_name;
34623 } else if (component.types.indexOf("country") >= 0) {
34624 result.country = component.short_name;
34628 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34629 result.addressLine2 = "";
34633 setZoomLevel: function(zoom)
34635 this.gMapContext.map.setZoom(zoom);
34648 this.fireEvent('show', this);
34659 this.fireEvent('hide', this);
34664 Roo.apply(Roo.bootstrap.LocationPicker, {
34666 OverlayView : function(map, options)
34668 options = options || {};
34675 * @class Roo.bootstrap.Alert
34676 * @extends Roo.bootstrap.Component
34677 * Bootstrap Alert class - shows an alert area box
34679 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34680 Enter a valid email address
34683 * @cfg {String} title The title of alert
34684 * @cfg {String} html The content of alert
34685 * @cfg {String} weight (success|info|warning|danger) Weight of the message
34686 * @cfg {String} fa font-awesomeicon
34687 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34688 * @cfg {Boolean} close true to show a x closer
34692 * Create a new alert
34693 * @param {Object} config The config object
34697 Roo.bootstrap.Alert = function(config){
34698 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34702 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
34708 faicon: false, // BC
34712 getAutoCreate : function()
34724 style : this.close ? '' : 'display:none'
34728 cls : 'roo-alert-icon'
34733 cls : 'roo-alert-title',
34738 cls : 'roo-alert-text',
34745 cfg.cn[0].cls += ' fa ' + this.faicon;
34748 cfg.cn[0].cls += ' fa ' + this.fa;
34752 cfg.cls += ' alert-' + this.weight;
34758 initEvents: function()
34760 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34761 this.titleEl = this.el.select('.roo-alert-title',true).first();
34762 this.iconEl = this.el.select('.roo-alert-icon',true).first();
34763 this.htmlEl = this.el.select('.roo-alert-text',true).first();
34764 if (this.seconds > 0) {
34765 this.hide.defer(this.seconds, this);
34769 * Set the Title Message HTML
34770 * @param {String} html
34772 setTitle : function(str)
34774 this.titleEl.dom.innerHTML = str;
34778 * Set the Body Message HTML
34779 * @param {String} html
34781 setHtml : function(str)
34783 this.htmlEl.dom.innerHTML = str;
34786 * Set the Weight of the alert
34787 * @param {String} (success|info|warning|danger) weight
34790 setWeight : function(weight)
34793 this.el.removeClass('alert-' + this.weight);
34796 this.weight = weight;
34798 this.el.addClass('alert-' + this.weight);
34801 * Set the Icon of the alert
34802 * @param {String} see fontawsome names (name without the 'fa-' bit)
34804 setIcon : function(icon)
34807 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34810 this.faicon = icon;
34812 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34837 * @class Roo.bootstrap.UploadCropbox
34838 * @extends Roo.bootstrap.Component
34839 * Bootstrap UploadCropbox class
34840 * @cfg {String} emptyText show when image has been loaded
34841 * @cfg {String} rotateNotify show when image too small to rotate
34842 * @cfg {Number} errorTimeout default 3000
34843 * @cfg {Number} minWidth default 300
34844 * @cfg {Number} minHeight default 300
34845 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34846 * @cfg {Boolean} isDocument (true|false) default false
34847 * @cfg {String} url action url
34848 * @cfg {String} paramName default 'imageUpload'
34849 * @cfg {String} method default POST
34850 * @cfg {Boolean} loadMask (true|false) default true
34851 * @cfg {Boolean} loadingText default 'Loading...'
34854 * Create a new UploadCropbox
34855 * @param {Object} config The config object
34858 Roo.bootstrap.UploadCropbox = function(config){
34859 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34863 * @event beforeselectfile
34864 * Fire before select file
34865 * @param {Roo.bootstrap.UploadCropbox} this
34867 "beforeselectfile" : true,
34870 * Fire after initEvent
34871 * @param {Roo.bootstrap.UploadCropbox} this
34876 * Fire after initEvent
34877 * @param {Roo.bootstrap.UploadCropbox} this
34878 * @param {String} data
34883 * Fire when preparing the file data
34884 * @param {Roo.bootstrap.UploadCropbox} this
34885 * @param {Object} file
34890 * Fire when get exception
34891 * @param {Roo.bootstrap.UploadCropbox} this
34892 * @param {XMLHttpRequest} xhr
34894 "exception" : true,
34896 * @event beforeloadcanvas
34897 * Fire before load the canvas
34898 * @param {Roo.bootstrap.UploadCropbox} this
34899 * @param {String} src
34901 "beforeloadcanvas" : true,
34904 * Fire when trash image
34905 * @param {Roo.bootstrap.UploadCropbox} this
34910 * Fire when download the image
34911 * @param {Roo.bootstrap.UploadCropbox} this
34915 * @event footerbuttonclick
34916 * Fire when footerbuttonclick
34917 * @param {Roo.bootstrap.UploadCropbox} this
34918 * @param {String} type
34920 "footerbuttonclick" : true,
34924 * @param {Roo.bootstrap.UploadCropbox} this
34929 * Fire when rotate the image
34930 * @param {Roo.bootstrap.UploadCropbox} this
34931 * @param {String} pos
34936 * Fire when inspect the file
34937 * @param {Roo.bootstrap.UploadCropbox} this
34938 * @param {Object} file
34943 * Fire when xhr upload the file
34944 * @param {Roo.bootstrap.UploadCropbox} this
34945 * @param {Object} data
34950 * Fire when arrange the file data
34951 * @param {Roo.bootstrap.UploadCropbox} this
34952 * @param {Object} formData
34957 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34960 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
34962 emptyText : 'Click to upload image',
34963 rotateNotify : 'Image is too small to rotate',
34964 errorTimeout : 3000,
34978 cropType : 'image/jpeg',
34980 canvasLoaded : false,
34981 isDocument : false,
34983 paramName : 'imageUpload',
34985 loadingText : 'Loading...',
34988 getAutoCreate : function()
34992 cls : 'roo-upload-cropbox',
34996 cls : 'roo-upload-cropbox-selector',
35001 cls : 'roo-upload-cropbox-body',
35002 style : 'cursor:pointer',
35006 cls : 'roo-upload-cropbox-preview'
35010 cls : 'roo-upload-cropbox-thumb'
35014 cls : 'roo-upload-cropbox-empty-notify',
35015 html : this.emptyText
35019 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35020 html : this.rotateNotify
35026 cls : 'roo-upload-cropbox-footer',
35029 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35039 onRender : function(ct, position)
35041 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35043 if (this.buttons.length) {
35045 Roo.each(this.buttons, function(bb) {
35047 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35049 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35055 this.maskEl = this.el;
35059 initEvents : function()
35061 this.urlAPI = (window.createObjectURL && window) ||
35062 (window.URL && URL.revokeObjectURL && URL) ||
35063 (window.webkitURL && webkitURL);
35065 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35066 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35068 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35069 this.selectorEl.hide();
35071 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35072 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35074 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35075 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35076 this.thumbEl.hide();
35078 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35079 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35081 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35082 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35083 this.errorEl.hide();
35085 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35086 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35087 this.footerEl.hide();
35089 this.setThumbBoxSize();
35095 this.fireEvent('initial', this);
35102 window.addEventListener("resize", function() { _this.resize(); } );
35104 this.bodyEl.on('click', this.beforeSelectFile, this);
35107 this.bodyEl.on('touchstart', this.onTouchStart, this);
35108 this.bodyEl.on('touchmove', this.onTouchMove, this);
35109 this.bodyEl.on('touchend', this.onTouchEnd, this);
35113 this.bodyEl.on('mousedown', this.onMouseDown, this);
35114 this.bodyEl.on('mousemove', this.onMouseMove, this);
35115 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35116 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35117 Roo.get(document).on('mouseup', this.onMouseUp, this);
35120 this.selectorEl.on('change', this.onFileSelected, this);
35126 this.baseScale = 1;
35128 this.baseRotate = 1;
35129 this.dragable = false;
35130 this.pinching = false;
35133 this.cropData = false;
35134 this.notifyEl.dom.innerHTML = this.emptyText;
35136 this.selectorEl.dom.value = '';
35140 resize : function()
35142 if(this.fireEvent('resize', this) != false){
35143 this.setThumbBoxPosition();
35144 this.setCanvasPosition();
35148 onFooterButtonClick : function(e, el, o, type)
35151 case 'rotate-left' :
35152 this.onRotateLeft(e);
35154 case 'rotate-right' :
35155 this.onRotateRight(e);
35158 this.beforeSelectFile(e);
35173 this.fireEvent('footerbuttonclick', this, type);
35176 beforeSelectFile : function(e)
35178 e.preventDefault();
35180 if(this.fireEvent('beforeselectfile', this) != false){
35181 this.selectorEl.dom.click();
35185 onFileSelected : function(e)
35187 e.preventDefault();
35189 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35193 var file = this.selectorEl.dom.files[0];
35195 if(this.fireEvent('inspect', this, file) != false){
35196 this.prepare(file);
35201 trash : function(e)
35203 this.fireEvent('trash', this);
35206 download : function(e)
35208 this.fireEvent('download', this);
35211 loadCanvas : function(src)
35213 if(this.fireEvent('beforeloadcanvas', this, src) != false){
35217 this.imageEl = document.createElement('img');
35221 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35223 this.imageEl.src = src;
35227 onLoadCanvas : function()
35229 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35230 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35232 this.bodyEl.un('click', this.beforeSelectFile, this);
35234 this.notifyEl.hide();
35235 this.thumbEl.show();
35236 this.footerEl.show();
35238 this.baseRotateLevel();
35240 if(this.isDocument){
35241 this.setThumbBoxSize();
35244 this.setThumbBoxPosition();
35246 this.baseScaleLevel();
35252 this.canvasLoaded = true;
35255 this.maskEl.unmask();
35260 setCanvasPosition : function()
35262 if(!this.canvasEl){
35266 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35267 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35269 this.previewEl.setLeft(pw);
35270 this.previewEl.setTop(ph);
35274 onMouseDown : function(e)
35278 this.dragable = true;
35279 this.pinching = false;
35281 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35282 this.dragable = false;
35286 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35287 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35291 onMouseMove : function(e)
35295 if(!this.canvasLoaded){
35299 if (!this.dragable){
35303 var minX = Math.ceil(this.thumbEl.getLeft(true));
35304 var minY = Math.ceil(this.thumbEl.getTop(true));
35306 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35307 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35309 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35310 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35312 x = x - this.mouseX;
35313 y = y - this.mouseY;
35315 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35316 var bgY = Math.ceil(y + this.previewEl.getTop(true));
35318 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35319 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35321 this.previewEl.setLeft(bgX);
35322 this.previewEl.setTop(bgY);
35324 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35325 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35328 onMouseUp : function(e)
35332 this.dragable = false;
35335 onMouseWheel : function(e)
35339 this.startScale = this.scale;
35341 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35343 if(!this.zoomable()){
35344 this.scale = this.startScale;
35353 zoomable : function()
35355 var minScale = this.thumbEl.getWidth() / this.minWidth;
35357 if(this.minWidth < this.minHeight){
35358 minScale = this.thumbEl.getHeight() / this.minHeight;
35361 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35362 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35366 (this.rotate == 0 || this.rotate == 180) &&
35368 width > this.imageEl.OriginWidth ||
35369 height > this.imageEl.OriginHeight ||
35370 (width < this.minWidth && height < this.minHeight)
35378 (this.rotate == 90 || this.rotate == 270) &&
35380 width > this.imageEl.OriginWidth ||
35381 height > this.imageEl.OriginHeight ||
35382 (width < this.minHeight && height < this.minWidth)
35389 !this.isDocument &&
35390 (this.rotate == 0 || this.rotate == 180) &&
35392 width < this.minWidth ||
35393 width > this.imageEl.OriginWidth ||
35394 height < this.minHeight ||
35395 height > this.imageEl.OriginHeight
35402 !this.isDocument &&
35403 (this.rotate == 90 || this.rotate == 270) &&
35405 width < this.minHeight ||
35406 width > this.imageEl.OriginWidth ||
35407 height < this.minWidth ||
35408 height > this.imageEl.OriginHeight
35418 onRotateLeft : function(e)
35420 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35422 var minScale = this.thumbEl.getWidth() / this.minWidth;
35424 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35425 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35427 this.startScale = this.scale;
35429 while (this.getScaleLevel() < minScale){
35431 this.scale = this.scale + 1;
35433 if(!this.zoomable()){
35438 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35439 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35444 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35451 this.scale = this.startScale;
35453 this.onRotateFail();
35458 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35460 if(this.isDocument){
35461 this.setThumbBoxSize();
35462 this.setThumbBoxPosition();
35463 this.setCanvasPosition();
35468 this.fireEvent('rotate', this, 'left');
35472 onRotateRight : function(e)
35474 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35476 var minScale = this.thumbEl.getWidth() / this.minWidth;
35478 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35479 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35481 this.startScale = this.scale;
35483 while (this.getScaleLevel() < minScale){
35485 this.scale = this.scale + 1;
35487 if(!this.zoomable()){
35492 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35493 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35498 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35505 this.scale = this.startScale;
35507 this.onRotateFail();
35512 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35514 if(this.isDocument){
35515 this.setThumbBoxSize();
35516 this.setThumbBoxPosition();
35517 this.setCanvasPosition();
35522 this.fireEvent('rotate', this, 'right');
35525 onRotateFail : function()
35527 this.errorEl.show(true);
35531 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35536 this.previewEl.dom.innerHTML = '';
35538 var canvasEl = document.createElement("canvas");
35540 var contextEl = canvasEl.getContext("2d");
35542 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35543 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35544 var center = this.imageEl.OriginWidth / 2;
35546 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35547 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35548 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35549 center = this.imageEl.OriginHeight / 2;
35552 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35554 contextEl.translate(center, center);
35555 contextEl.rotate(this.rotate * Math.PI / 180);
35557 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35559 this.canvasEl = document.createElement("canvas");
35561 this.contextEl = this.canvasEl.getContext("2d");
35563 switch (this.rotate) {
35566 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35567 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35569 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35574 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35575 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35577 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35578 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);
35582 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35587 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35588 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35590 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35591 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);
35595 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);
35600 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35601 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35603 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35604 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35608 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);
35615 this.previewEl.appendChild(this.canvasEl);
35617 this.setCanvasPosition();
35622 if(!this.canvasLoaded){
35626 var imageCanvas = document.createElement("canvas");
35628 var imageContext = imageCanvas.getContext("2d");
35630 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35631 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35633 var center = imageCanvas.width / 2;
35635 imageContext.translate(center, center);
35637 imageContext.rotate(this.rotate * Math.PI / 180);
35639 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35641 var canvas = document.createElement("canvas");
35643 var context = canvas.getContext("2d");
35645 canvas.width = this.minWidth;
35646 canvas.height = this.minHeight;
35648 switch (this.rotate) {
35651 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35652 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35654 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35655 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35657 var targetWidth = this.minWidth - 2 * x;
35658 var targetHeight = this.minHeight - 2 * y;
35662 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35663 scale = targetWidth / width;
35666 if(x > 0 && y == 0){
35667 scale = targetHeight / height;
35670 if(x > 0 && y > 0){
35671 scale = targetWidth / width;
35673 if(width < height){
35674 scale = targetHeight / height;
35678 context.scale(scale, scale);
35680 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35681 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35683 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35684 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35686 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35691 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35692 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35694 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35695 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35697 var targetWidth = this.minWidth - 2 * x;
35698 var targetHeight = this.minHeight - 2 * y;
35702 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35703 scale = targetWidth / width;
35706 if(x > 0 && y == 0){
35707 scale = targetHeight / height;
35710 if(x > 0 && y > 0){
35711 scale = targetWidth / width;
35713 if(width < height){
35714 scale = targetHeight / height;
35718 context.scale(scale, scale);
35720 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35721 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35723 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35724 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35726 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35728 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35733 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35734 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35736 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35737 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35739 var targetWidth = this.minWidth - 2 * x;
35740 var targetHeight = this.minHeight - 2 * y;
35744 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35745 scale = targetWidth / width;
35748 if(x > 0 && y == 0){
35749 scale = targetHeight / height;
35752 if(x > 0 && y > 0){
35753 scale = targetWidth / width;
35755 if(width < height){
35756 scale = targetHeight / height;
35760 context.scale(scale, scale);
35762 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35763 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35765 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35766 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35768 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35769 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35771 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35776 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35777 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35779 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35780 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35782 var targetWidth = this.minWidth - 2 * x;
35783 var targetHeight = this.minHeight - 2 * y;
35787 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35788 scale = targetWidth / width;
35791 if(x > 0 && y == 0){
35792 scale = targetHeight / height;
35795 if(x > 0 && y > 0){
35796 scale = targetWidth / width;
35798 if(width < height){
35799 scale = targetHeight / height;
35803 context.scale(scale, scale);
35805 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35806 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35808 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35809 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35811 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35813 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35820 this.cropData = canvas.toDataURL(this.cropType);
35822 if(this.fireEvent('crop', this, this.cropData) !== false){
35823 this.process(this.file, this.cropData);
35830 setThumbBoxSize : function()
35834 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35835 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35836 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35838 this.minWidth = width;
35839 this.minHeight = height;
35841 if(this.rotate == 90 || this.rotate == 270){
35842 this.minWidth = height;
35843 this.minHeight = width;
35848 width = Math.ceil(this.minWidth * height / this.minHeight);
35850 if(this.minWidth > this.minHeight){
35852 height = Math.ceil(this.minHeight * width / this.minWidth);
35855 this.thumbEl.setStyle({
35856 width : width + 'px',
35857 height : height + 'px'
35864 setThumbBoxPosition : function()
35866 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35867 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35869 this.thumbEl.setLeft(x);
35870 this.thumbEl.setTop(y);
35874 baseRotateLevel : function()
35876 this.baseRotate = 1;
35879 typeof(this.exif) != 'undefined' &&
35880 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35881 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35883 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35886 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35890 baseScaleLevel : function()
35894 if(this.isDocument){
35896 if(this.baseRotate == 6 || this.baseRotate == 8){
35898 height = this.thumbEl.getHeight();
35899 this.baseScale = height / this.imageEl.OriginWidth;
35901 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35902 width = this.thumbEl.getWidth();
35903 this.baseScale = width / this.imageEl.OriginHeight;
35909 height = this.thumbEl.getHeight();
35910 this.baseScale = height / this.imageEl.OriginHeight;
35912 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35913 width = this.thumbEl.getWidth();
35914 this.baseScale = width / this.imageEl.OriginWidth;
35920 if(this.baseRotate == 6 || this.baseRotate == 8){
35922 width = this.thumbEl.getHeight();
35923 this.baseScale = width / this.imageEl.OriginHeight;
35925 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35926 height = this.thumbEl.getWidth();
35927 this.baseScale = height / this.imageEl.OriginHeight;
35930 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35931 height = this.thumbEl.getWidth();
35932 this.baseScale = height / this.imageEl.OriginHeight;
35934 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35935 width = this.thumbEl.getHeight();
35936 this.baseScale = width / this.imageEl.OriginWidth;
35943 width = this.thumbEl.getWidth();
35944 this.baseScale = width / this.imageEl.OriginWidth;
35946 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35947 height = this.thumbEl.getHeight();
35948 this.baseScale = height / this.imageEl.OriginHeight;
35951 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35953 height = this.thumbEl.getHeight();
35954 this.baseScale = height / this.imageEl.OriginHeight;
35956 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35957 width = this.thumbEl.getWidth();
35958 this.baseScale = width / this.imageEl.OriginWidth;
35966 getScaleLevel : function()
35968 return this.baseScale * Math.pow(1.1, this.scale);
35971 onTouchStart : function(e)
35973 if(!this.canvasLoaded){
35974 this.beforeSelectFile(e);
35978 var touches = e.browserEvent.touches;
35984 if(touches.length == 1){
35985 this.onMouseDown(e);
35989 if(touches.length != 2){
35995 for(var i = 0, finger; finger = touches[i]; i++){
35996 coords.push(finger.pageX, finger.pageY);
35999 var x = Math.pow(coords[0] - coords[2], 2);
36000 var y = Math.pow(coords[1] - coords[3], 2);
36002 this.startDistance = Math.sqrt(x + y);
36004 this.startScale = this.scale;
36006 this.pinching = true;
36007 this.dragable = false;
36011 onTouchMove : function(e)
36013 if(!this.pinching && !this.dragable){
36017 var touches = e.browserEvent.touches;
36024 this.onMouseMove(e);
36030 for(var i = 0, finger; finger = touches[i]; i++){
36031 coords.push(finger.pageX, finger.pageY);
36034 var x = Math.pow(coords[0] - coords[2], 2);
36035 var y = Math.pow(coords[1] - coords[3], 2);
36037 this.endDistance = Math.sqrt(x + y);
36039 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36041 if(!this.zoomable()){
36042 this.scale = this.startScale;
36050 onTouchEnd : function(e)
36052 this.pinching = false;
36053 this.dragable = false;
36057 process : function(file, crop)
36060 this.maskEl.mask(this.loadingText);
36063 this.xhr = new XMLHttpRequest();
36065 file.xhr = this.xhr;
36067 this.xhr.open(this.method, this.url, true);
36070 "Accept": "application/json",
36071 "Cache-Control": "no-cache",
36072 "X-Requested-With": "XMLHttpRequest"
36075 for (var headerName in headers) {
36076 var headerValue = headers[headerName];
36078 this.xhr.setRequestHeader(headerName, headerValue);
36084 this.xhr.onload = function()
36086 _this.xhrOnLoad(_this.xhr);
36089 this.xhr.onerror = function()
36091 _this.xhrOnError(_this.xhr);
36094 var formData = new FormData();
36096 formData.append('returnHTML', 'NO');
36099 formData.append('crop', crop);
36102 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36103 formData.append(this.paramName, file, file.name);
36106 if(typeof(file.filename) != 'undefined'){
36107 formData.append('filename', file.filename);
36110 if(typeof(file.mimetype) != 'undefined'){
36111 formData.append('mimetype', file.mimetype);
36114 if(this.fireEvent('arrange', this, formData) != false){
36115 this.xhr.send(formData);
36119 xhrOnLoad : function(xhr)
36122 this.maskEl.unmask();
36125 if (xhr.readyState !== 4) {
36126 this.fireEvent('exception', this, xhr);
36130 var response = Roo.decode(xhr.responseText);
36132 if(!response.success){
36133 this.fireEvent('exception', this, xhr);
36137 var response = Roo.decode(xhr.responseText);
36139 this.fireEvent('upload', this, response);
36143 xhrOnError : function()
36146 this.maskEl.unmask();
36149 Roo.log('xhr on error');
36151 var response = Roo.decode(xhr.responseText);
36157 prepare : function(file)
36160 this.maskEl.mask(this.loadingText);
36166 if(typeof(file) === 'string'){
36167 this.loadCanvas(file);
36171 if(!file || !this.urlAPI){
36176 this.cropType = file.type;
36180 if(this.fireEvent('prepare', this, this.file) != false){
36182 var reader = new FileReader();
36184 reader.onload = function (e) {
36185 if (e.target.error) {
36186 Roo.log(e.target.error);
36190 var buffer = e.target.result,
36191 dataView = new DataView(buffer),
36193 maxOffset = dataView.byteLength - 4,
36197 if (dataView.getUint16(0) === 0xffd8) {
36198 while (offset < maxOffset) {
36199 markerBytes = dataView.getUint16(offset);
36201 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36202 markerLength = dataView.getUint16(offset + 2) + 2;
36203 if (offset + markerLength > dataView.byteLength) {
36204 Roo.log('Invalid meta data: Invalid segment size.');
36208 if(markerBytes == 0xffe1){
36209 _this.parseExifData(
36216 offset += markerLength;
36226 var url = _this.urlAPI.createObjectURL(_this.file);
36228 _this.loadCanvas(url);
36233 reader.readAsArrayBuffer(this.file);
36239 parseExifData : function(dataView, offset, length)
36241 var tiffOffset = offset + 10,
36245 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36246 // No Exif data, might be XMP data instead
36250 // Check for the ASCII code for "Exif" (0x45786966):
36251 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36252 // No Exif data, might be XMP data instead
36255 if (tiffOffset + 8 > dataView.byteLength) {
36256 Roo.log('Invalid Exif data: Invalid segment size.');
36259 // Check for the two null bytes:
36260 if (dataView.getUint16(offset + 8) !== 0x0000) {
36261 Roo.log('Invalid Exif data: Missing byte alignment offset.');
36264 // Check the byte alignment:
36265 switch (dataView.getUint16(tiffOffset)) {
36267 littleEndian = true;
36270 littleEndian = false;
36273 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36276 // Check for the TIFF tag marker (0x002A):
36277 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36278 Roo.log('Invalid Exif data: Missing TIFF marker.');
36281 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36282 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36284 this.parseExifTags(
36287 tiffOffset + dirOffset,
36292 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36297 if (dirOffset + 6 > dataView.byteLength) {
36298 Roo.log('Invalid Exif data: Invalid directory offset.');
36301 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36302 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36303 if (dirEndOffset + 4 > dataView.byteLength) {
36304 Roo.log('Invalid Exif data: Invalid directory size.');
36307 for (i = 0; i < tagsNumber; i += 1) {
36311 dirOffset + 2 + 12 * i, // tag offset
36315 // Return the offset to the next directory:
36316 return dataView.getUint32(dirEndOffset, littleEndian);
36319 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
36321 var tag = dataView.getUint16(offset, littleEndian);
36323 this.exif[tag] = this.getExifValue(
36327 dataView.getUint16(offset + 2, littleEndian), // tag type
36328 dataView.getUint32(offset + 4, littleEndian), // tag length
36333 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36335 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36344 Roo.log('Invalid Exif data: Invalid tag type.');
36348 tagSize = tagType.size * length;
36349 // Determine if the value is contained in the dataOffset bytes,
36350 // or if the value at the dataOffset is a pointer to the actual data:
36351 dataOffset = tagSize > 4 ?
36352 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36353 if (dataOffset + tagSize > dataView.byteLength) {
36354 Roo.log('Invalid Exif data: Invalid data offset.');
36357 if (length === 1) {
36358 return tagType.getValue(dataView, dataOffset, littleEndian);
36361 for (i = 0; i < length; i += 1) {
36362 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36365 if (tagType.ascii) {
36367 // Concatenate the chars:
36368 for (i = 0; i < values.length; i += 1) {
36370 // Ignore the terminating NULL byte(s):
36371 if (c === '\u0000') {
36383 Roo.apply(Roo.bootstrap.UploadCropbox, {
36385 'Orientation': 0x0112
36389 1: 0, //'top-left',
36391 3: 180, //'bottom-right',
36392 // 4: 'bottom-left',
36394 6: 90, //'right-top',
36395 // 7: 'right-bottom',
36396 8: 270 //'left-bottom'
36400 // byte, 8-bit unsigned int:
36402 getValue: function (dataView, dataOffset) {
36403 return dataView.getUint8(dataOffset);
36407 // ascii, 8-bit byte:
36409 getValue: function (dataView, dataOffset) {
36410 return String.fromCharCode(dataView.getUint8(dataOffset));
36415 // short, 16 bit int:
36417 getValue: function (dataView, dataOffset, littleEndian) {
36418 return dataView.getUint16(dataOffset, littleEndian);
36422 // long, 32 bit int:
36424 getValue: function (dataView, dataOffset, littleEndian) {
36425 return dataView.getUint32(dataOffset, littleEndian);
36429 // rational = two long values, first is numerator, second is denominator:
36431 getValue: function (dataView, dataOffset, littleEndian) {
36432 return dataView.getUint32(dataOffset, littleEndian) /
36433 dataView.getUint32(dataOffset + 4, littleEndian);
36437 // slong, 32 bit signed int:
36439 getValue: function (dataView, dataOffset, littleEndian) {
36440 return dataView.getInt32(dataOffset, littleEndian);
36444 // srational, two slongs, first is numerator, second is denominator:
36446 getValue: function (dataView, dataOffset, littleEndian) {
36447 return dataView.getInt32(dataOffset, littleEndian) /
36448 dataView.getInt32(dataOffset + 4, littleEndian);
36458 cls : 'btn-group roo-upload-cropbox-rotate-left',
36459 action : 'rotate-left',
36463 cls : 'btn btn-default',
36464 html : '<i class="fa fa-undo"></i>'
36470 cls : 'btn-group roo-upload-cropbox-picture',
36471 action : 'picture',
36475 cls : 'btn btn-default',
36476 html : '<i class="fa fa-picture-o"></i>'
36482 cls : 'btn-group roo-upload-cropbox-rotate-right',
36483 action : 'rotate-right',
36487 cls : 'btn btn-default',
36488 html : '<i class="fa fa-repeat"></i>'
36496 cls : 'btn-group roo-upload-cropbox-rotate-left',
36497 action : 'rotate-left',
36501 cls : 'btn btn-default',
36502 html : '<i class="fa fa-undo"></i>'
36508 cls : 'btn-group roo-upload-cropbox-download',
36509 action : 'download',
36513 cls : 'btn btn-default',
36514 html : '<i class="fa fa-download"></i>'
36520 cls : 'btn-group roo-upload-cropbox-crop',
36525 cls : 'btn btn-default',
36526 html : '<i class="fa fa-crop"></i>'
36532 cls : 'btn-group roo-upload-cropbox-trash',
36537 cls : 'btn btn-default',
36538 html : '<i class="fa fa-trash"></i>'
36544 cls : 'btn-group roo-upload-cropbox-rotate-right',
36545 action : 'rotate-right',
36549 cls : 'btn btn-default',
36550 html : '<i class="fa fa-repeat"></i>'
36558 cls : 'btn-group roo-upload-cropbox-rotate-left',
36559 action : 'rotate-left',
36563 cls : 'btn btn-default',
36564 html : '<i class="fa fa-undo"></i>'
36570 cls : 'btn-group roo-upload-cropbox-rotate-right',
36571 action : 'rotate-right',
36575 cls : 'btn btn-default',
36576 html : '<i class="fa fa-repeat"></i>'
36589 * @class Roo.bootstrap.DocumentManager
36590 * @extends Roo.bootstrap.Component
36591 * Bootstrap DocumentManager class
36592 * @cfg {String} paramName default 'imageUpload'
36593 * @cfg {String} toolTipName default 'filename'
36594 * @cfg {String} method default POST
36595 * @cfg {String} url action url
36596 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36597 * @cfg {Boolean} multiple multiple upload default true
36598 * @cfg {Number} thumbSize default 300
36599 * @cfg {String} fieldLabel
36600 * @cfg {Number} labelWidth default 4
36601 * @cfg {String} labelAlign (left|top) default left
36602 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36603 * @cfg {Number} labellg set the width of label (1-12)
36604 * @cfg {Number} labelmd set the width of label (1-12)
36605 * @cfg {Number} labelsm set the width of label (1-12)
36606 * @cfg {Number} labelxs set the width of label (1-12)
36609 * Create a new DocumentManager
36610 * @param {Object} config The config object
36613 Roo.bootstrap.DocumentManager = function(config){
36614 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36617 this.delegates = [];
36622 * Fire when initial the DocumentManager
36623 * @param {Roo.bootstrap.DocumentManager} this
36628 * inspect selected file
36629 * @param {Roo.bootstrap.DocumentManager} this
36630 * @param {File} file
36635 * Fire when xhr load exception
36636 * @param {Roo.bootstrap.DocumentManager} this
36637 * @param {XMLHttpRequest} xhr
36639 "exception" : true,
36641 * @event afterupload
36642 * Fire when xhr load exception
36643 * @param {Roo.bootstrap.DocumentManager} this
36644 * @param {XMLHttpRequest} xhr
36646 "afterupload" : true,
36649 * prepare the form data
36650 * @param {Roo.bootstrap.DocumentManager} this
36651 * @param {Object} formData
36656 * Fire when remove the file
36657 * @param {Roo.bootstrap.DocumentManager} this
36658 * @param {Object} file
36663 * Fire after refresh the file
36664 * @param {Roo.bootstrap.DocumentManager} this
36669 * Fire after click the image
36670 * @param {Roo.bootstrap.DocumentManager} this
36671 * @param {Object} file
36676 * Fire when upload a image and editable set to true
36677 * @param {Roo.bootstrap.DocumentManager} this
36678 * @param {Object} file
36682 * @event beforeselectfile
36683 * Fire before select file
36684 * @param {Roo.bootstrap.DocumentManager} this
36686 "beforeselectfile" : true,
36689 * Fire before process file
36690 * @param {Roo.bootstrap.DocumentManager} this
36691 * @param {Object} file
36695 * @event previewrendered
36696 * Fire when preview rendered
36697 * @param {Roo.bootstrap.DocumentManager} this
36698 * @param {Object} file
36700 "previewrendered" : true,
36703 "previewResize" : true
36708 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
36717 paramName : 'imageUpload',
36718 toolTipName : 'filename',
36721 labelAlign : 'left',
36731 getAutoCreate : function()
36733 var managerWidget = {
36735 cls : 'roo-document-manager',
36739 cls : 'roo-document-manager-selector',
36744 cls : 'roo-document-manager-uploader',
36748 cls : 'roo-document-manager-upload-btn',
36749 html : '<i class="fa fa-plus"></i>'
36760 cls : 'column col-md-12',
36765 if(this.fieldLabel.length){
36770 cls : 'column col-md-12',
36771 html : this.fieldLabel
36775 cls : 'column col-md-12',
36780 if(this.labelAlign == 'left'){
36785 html : this.fieldLabel
36794 if(this.labelWidth > 12){
36795 content[0].style = "width: " + this.labelWidth + 'px';
36798 if(this.labelWidth < 13 && this.labelmd == 0){
36799 this.labelmd = this.labelWidth;
36802 if(this.labellg > 0){
36803 content[0].cls += ' col-lg-' + this.labellg;
36804 content[1].cls += ' col-lg-' + (12 - this.labellg);
36807 if(this.labelmd > 0){
36808 content[0].cls += ' col-md-' + this.labelmd;
36809 content[1].cls += ' col-md-' + (12 - this.labelmd);
36812 if(this.labelsm > 0){
36813 content[0].cls += ' col-sm-' + this.labelsm;
36814 content[1].cls += ' col-sm-' + (12 - this.labelsm);
36817 if(this.labelxs > 0){
36818 content[0].cls += ' col-xs-' + this.labelxs;
36819 content[1].cls += ' col-xs-' + (12 - this.labelxs);
36827 cls : 'row clearfix',
36835 initEvents : function()
36837 this.managerEl = this.el.select('.roo-document-manager', true).first();
36838 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36840 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36841 this.selectorEl.hide();
36844 this.selectorEl.attr('multiple', 'multiple');
36847 this.selectorEl.on('change', this.onFileSelected, this);
36849 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36850 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36852 this.uploader.on('click', this.onUploaderClick, this);
36854 this.renderProgressDialog();
36858 window.addEventListener("resize", function() { _this.refresh(); } );
36860 this.fireEvent('initial', this);
36863 renderProgressDialog : function()
36867 this.progressDialog = new Roo.bootstrap.Modal({
36868 cls : 'roo-document-manager-progress-dialog',
36869 allow_close : false,
36880 btnclick : function() {
36881 _this.uploadCancel();
36887 this.progressDialog.render(Roo.get(document.body));
36889 this.progress = new Roo.bootstrap.Progress({
36890 cls : 'roo-document-manager-progress',
36895 this.progress.render(this.progressDialog.getChildContainer());
36897 this.progressBar = new Roo.bootstrap.ProgressBar({
36898 cls : 'roo-document-manager-progress-bar',
36901 aria_valuemax : 12,
36905 this.progressBar.render(this.progress.getChildContainer());
36908 onUploaderClick : function(e)
36910 e.preventDefault();
36912 if(this.fireEvent('beforeselectfile', this) != false){
36913 this.selectorEl.dom.click();
36918 onFileSelected : function(e)
36920 e.preventDefault();
36922 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36926 Roo.each(this.selectorEl.dom.files, function(file){
36927 if(this.fireEvent('inspect', this, file) != false){
36928 this.files.push(file);
36938 this.selectorEl.dom.value = '';
36940 if(!this.files || !this.files.length){
36944 if(this.boxes > 0 && this.files.length > this.boxes){
36945 this.files = this.files.slice(0, this.boxes);
36948 this.uploader.show();
36950 if(this.boxes > 0 && this.files.length > this.boxes - 1){
36951 this.uploader.hide();
36960 Roo.each(this.files, function(file){
36962 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36963 var f = this.renderPreview(file);
36968 if(file.type.indexOf('image') != -1){
36969 this.delegates.push(
36971 _this.process(file);
36972 }).createDelegate(this)
36980 _this.process(file);
36981 }).createDelegate(this)
36986 this.files = files;
36988 this.delegates = this.delegates.concat(docs);
36990 if(!this.delegates.length){
36995 this.progressBar.aria_valuemax = this.delegates.length;
37002 arrange : function()
37004 if(!this.delegates.length){
37005 this.progressDialog.hide();
37010 var delegate = this.delegates.shift();
37012 this.progressDialog.show();
37014 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37016 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37021 refresh : function()
37023 this.uploader.show();
37025 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37026 this.uploader.hide();
37029 Roo.isTouch ? this.closable(false) : this.closable(true);
37031 this.fireEvent('refresh', this);
37034 onRemove : function(e, el, o)
37036 e.preventDefault();
37038 this.fireEvent('remove', this, o);
37042 remove : function(o)
37046 Roo.each(this.files, function(file){
37047 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37056 this.files = files;
37063 Roo.each(this.files, function(file){
37068 file.target.remove();
37077 onClick : function(e, el, o)
37079 e.preventDefault();
37081 this.fireEvent('click', this, o);
37085 closable : function(closable)
37087 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37089 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37101 xhrOnLoad : function(xhr)
37103 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37107 if (xhr.readyState !== 4) {
37109 this.fireEvent('exception', this, xhr);
37113 var response = Roo.decode(xhr.responseText);
37115 if(!response.success){
37117 this.fireEvent('exception', this, xhr);
37121 var file = this.renderPreview(response.data);
37123 this.files.push(file);
37127 this.fireEvent('afterupload', this, xhr);
37131 xhrOnError : function(xhr)
37133 Roo.log('xhr on error');
37135 var response = Roo.decode(xhr.responseText);
37142 process : function(file)
37144 if(this.fireEvent('process', this, file) !== false){
37145 if(this.editable && file.type.indexOf('image') != -1){
37146 this.fireEvent('edit', this, file);
37150 this.uploadStart(file, false);
37157 uploadStart : function(file, crop)
37159 this.xhr = new XMLHttpRequest();
37161 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37166 file.xhr = this.xhr;
37168 this.managerEl.createChild({
37170 cls : 'roo-document-manager-loading',
37174 tooltip : file.name,
37175 cls : 'roo-document-manager-thumb',
37176 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37182 this.xhr.open(this.method, this.url, true);
37185 "Accept": "application/json",
37186 "Cache-Control": "no-cache",
37187 "X-Requested-With": "XMLHttpRequest"
37190 for (var headerName in headers) {
37191 var headerValue = headers[headerName];
37193 this.xhr.setRequestHeader(headerName, headerValue);
37199 this.xhr.onload = function()
37201 _this.xhrOnLoad(_this.xhr);
37204 this.xhr.onerror = function()
37206 _this.xhrOnError(_this.xhr);
37209 var formData = new FormData();
37211 formData.append('returnHTML', 'NO');
37214 formData.append('crop', crop);
37217 formData.append(this.paramName, file, file.name);
37224 if(this.fireEvent('prepare', this, formData, options) != false){
37226 if(options.manually){
37230 this.xhr.send(formData);
37234 this.uploadCancel();
37237 uploadCancel : function()
37243 this.delegates = [];
37245 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37252 renderPreview : function(file)
37254 if(typeof(file.target) != 'undefined' && file.target){
37258 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37260 var previewEl = this.managerEl.createChild({
37262 cls : 'roo-document-manager-preview',
37266 tooltip : file[this.toolTipName],
37267 cls : 'roo-document-manager-thumb',
37268 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37273 html : '<i class="fa fa-times-circle"></i>'
37278 var close = previewEl.select('button.close', true).first();
37280 close.on('click', this.onRemove, this, file);
37282 file.target = previewEl;
37284 var image = previewEl.select('img', true).first();
37288 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37290 image.on('click', this.onClick, this, file);
37292 this.fireEvent('previewrendered', this, file);
37298 onPreviewLoad : function(file, image)
37300 if(typeof(file.target) == 'undefined' || !file.target){
37304 var width = image.dom.naturalWidth || image.dom.width;
37305 var height = image.dom.naturalHeight || image.dom.height;
37307 if(!this.previewResize) {
37311 if(width > height){
37312 file.target.addClass('wide');
37316 file.target.addClass('tall');
37321 uploadFromSource : function(file, crop)
37323 this.xhr = new XMLHttpRequest();
37325 this.managerEl.createChild({
37327 cls : 'roo-document-manager-loading',
37331 tooltip : file.name,
37332 cls : 'roo-document-manager-thumb',
37333 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37339 this.xhr.open(this.method, this.url, true);
37342 "Accept": "application/json",
37343 "Cache-Control": "no-cache",
37344 "X-Requested-With": "XMLHttpRequest"
37347 for (var headerName in headers) {
37348 var headerValue = headers[headerName];
37350 this.xhr.setRequestHeader(headerName, headerValue);
37356 this.xhr.onload = function()
37358 _this.xhrOnLoad(_this.xhr);
37361 this.xhr.onerror = function()
37363 _this.xhrOnError(_this.xhr);
37366 var formData = new FormData();
37368 formData.append('returnHTML', 'NO');
37370 formData.append('crop', crop);
37372 if(typeof(file.filename) != 'undefined'){
37373 formData.append('filename', file.filename);
37376 if(typeof(file.mimetype) != 'undefined'){
37377 formData.append('mimetype', file.mimetype);
37382 if(this.fireEvent('prepare', this, formData) != false){
37383 this.xhr.send(formData);
37393 * @class Roo.bootstrap.DocumentViewer
37394 * @extends Roo.bootstrap.Component
37395 * Bootstrap DocumentViewer class
37396 * @cfg {Boolean} showDownload (true|false) show download button (default true)
37397 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37400 * Create a new DocumentViewer
37401 * @param {Object} config The config object
37404 Roo.bootstrap.DocumentViewer = function(config){
37405 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37410 * Fire after initEvent
37411 * @param {Roo.bootstrap.DocumentViewer} this
37417 * @param {Roo.bootstrap.DocumentViewer} this
37422 * Fire after download button
37423 * @param {Roo.bootstrap.DocumentViewer} this
37428 * Fire after trash button
37429 * @param {Roo.bootstrap.DocumentViewer} this
37436 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
37438 showDownload : true,
37442 getAutoCreate : function()
37446 cls : 'roo-document-viewer',
37450 cls : 'roo-document-viewer-body',
37454 cls : 'roo-document-viewer-thumb',
37458 cls : 'roo-document-viewer-image'
37466 cls : 'roo-document-viewer-footer',
37469 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37473 cls : 'btn-group roo-document-viewer-download',
37477 cls : 'btn btn-default',
37478 html : '<i class="fa fa-download"></i>'
37484 cls : 'btn-group roo-document-viewer-trash',
37488 cls : 'btn btn-default',
37489 html : '<i class="fa fa-trash"></i>'
37502 initEvents : function()
37504 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37505 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37507 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37508 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37510 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37511 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37513 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37514 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37516 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37517 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37519 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37520 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37522 this.bodyEl.on('click', this.onClick, this);
37523 this.downloadBtn.on('click', this.onDownload, this);
37524 this.trashBtn.on('click', this.onTrash, this);
37526 this.downloadBtn.hide();
37527 this.trashBtn.hide();
37529 if(this.showDownload){
37530 this.downloadBtn.show();
37533 if(this.showTrash){
37534 this.trashBtn.show();
37537 if(!this.showDownload && !this.showTrash) {
37538 this.footerEl.hide();
37543 initial : function()
37545 this.fireEvent('initial', this);
37549 onClick : function(e)
37551 e.preventDefault();
37553 this.fireEvent('click', this);
37556 onDownload : function(e)
37558 e.preventDefault();
37560 this.fireEvent('download', this);
37563 onTrash : function(e)
37565 e.preventDefault();
37567 this.fireEvent('trash', this);
37579 * @class Roo.bootstrap.form.FieldLabel
37580 * @extends Roo.bootstrap.Component
37581 * Bootstrap FieldLabel class
37582 * @cfg {String} html contents of the element
37583 * @cfg {String} tag tag of the element default label
37584 * @cfg {String} cls class of the element
37585 * @cfg {String} target label target
37586 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37587 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37588 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37589 * @cfg {String} iconTooltip default "This field is required"
37590 * @cfg {String} indicatorpos (left|right) default left
37593 * Create a new FieldLabel
37594 * @param {Object} config The config object
37597 Roo.bootstrap.form.FieldLabel = function(config){
37598 Roo.bootstrap.Element.superclass.constructor.call(this, config);
37603 * Fires after the field has been marked as invalid.
37604 * @param {Roo.form.FieldLabel} this
37605 * @param {String} msg The validation message
37610 * Fires after the field has been validated with no errors.
37611 * @param {Roo.form.FieldLabel} this
37617 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
37624 invalidClass : 'has-warning',
37625 validClass : 'has-success',
37626 iconTooltip : 'This field is required',
37627 indicatorpos : 'left',
37629 getAutoCreate : function(){
37632 if (!this.allowBlank) {
37638 cls : 'roo-bootstrap-field-label ' + this.cls,
37643 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37644 tooltip : this.iconTooltip
37653 if(this.indicatorpos == 'right'){
37656 cls : 'roo-bootstrap-field-label ' + this.cls,
37665 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37666 tooltip : this.iconTooltip
37675 initEvents: function()
37677 Roo.bootstrap.Element.superclass.initEvents.call(this);
37679 this.indicator = this.indicatorEl();
37681 if(this.indicator){
37682 this.indicator.removeClass('visible');
37683 this.indicator.addClass('invisible');
37686 Roo.bootstrap.form.FieldLabel.register(this);
37689 indicatorEl : function()
37691 var indicator = this.el.select('i.roo-required-indicator',true).first();
37702 * Mark this field as valid
37704 markValid : function()
37706 if(this.indicator){
37707 this.indicator.removeClass('visible');
37708 this.indicator.addClass('invisible');
37710 if (Roo.bootstrap.version == 3) {
37711 this.el.removeClass(this.invalidClass);
37712 this.el.addClass(this.validClass);
37714 this.el.removeClass('is-invalid');
37715 this.el.addClass('is-valid');
37719 this.fireEvent('valid', this);
37723 * Mark this field as invalid
37724 * @param {String} msg The validation message
37726 markInvalid : function(msg)
37728 if(this.indicator){
37729 this.indicator.removeClass('invisible');
37730 this.indicator.addClass('visible');
37732 if (Roo.bootstrap.version == 3) {
37733 this.el.removeClass(this.validClass);
37734 this.el.addClass(this.invalidClass);
37736 this.el.removeClass('is-valid');
37737 this.el.addClass('is-invalid');
37741 this.fireEvent('invalid', this, msg);
37747 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37752 * register a FieldLabel Group
37753 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37755 register : function(label)
37757 if(this.groups.hasOwnProperty(label.target)){
37761 this.groups[label.target] = label;
37765 * fetch a FieldLabel Group based on the target
37766 * @param {string} target
37767 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37769 get: function(target) {
37770 if (typeof(this.groups[target]) == 'undefined') {
37774 return this.groups[target] ;
37783 * page DateSplitField.
37789 * @class Roo.bootstrap.form.DateSplitField
37790 * @extends Roo.bootstrap.Component
37791 * Bootstrap DateSplitField class
37792 * @cfg {string} fieldLabel - the label associated
37793 * @cfg {Number} labelWidth set the width of label (0-12)
37794 * @cfg {String} labelAlign (top|left)
37795 * @cfg {Boolean} dayAllowBlank (true|false) default false
37796 * @cfg {Boolean} monthAllowBlank (true|false) default false
37797 * @cfg {Boolean} yearAllowBlank (true|false) default false
37798 * @cfg {string} dayPlaceholder
37799 * @cfg {string} monthPlaceholder
37800 * @cfg {string} yearPlaceholder
37801 * @cfg {string} dayFormat default 'd'
37802 * @cfg {string} monthFormat default 'm'
37803 * @cfg {string} yearFormat default 'Y'
37804 * @cfg {Number} labellg set the width of label (1-12)
37805 * @cfg {Number} labelmd set the width of label (1-12)
37806 * @cfg {Number} labelsm set the width of label (1-12)
37807 * @cfg {Number} labelxs set the width of label (1-12)
37811 * Create a new DateSplitField
37812 * @param {Object} config The config object
37815 Roo.bootstrap.form.DateSplitField = function(config){
37816 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37822 * getting the data of years
37823 * @param {Roo.bootstrap.form.DateSplitField} this
37824 * @param {Object} years
37829 * getting the data of days
37830 * @param {Roo.bootstrap.form.DateSplitField} this
37831 * @param {Object} days
37836 * Fires after the field has been marked as invalid.
37837 * @param {Roo.form.Field} this
37838 * @param {String} msg The validation message
37843 * Fires after the field has been validated with no errors.
37844 * @param {Roo.form.Field} this
37850 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
37853 labelAlign : 'top',
37855 dayAllowBlank : false,
37856 monthAllowBlank : false,
37857 yearAllowBlank : false,
37858 dayPlaceholder : '',
37859 monthPlaceholder : '',
37860 yearPlaceholder : '',
37864 isFormField : true,
37870 getAutoCreate : function()
37874 cls : 'row roo-date-split-field-group',
37879 cls : 'form-hidden-field roo-date-split-field-group-value',
37885 var labelCls = 'col-md-12';
37886 var contentCls = 'col-md-4';
37888 if(this.fieldLabel){
37892 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37896 html : this.fieldLabel
37901 if(this.labelAlign == 'left'){
37903 if(this.labelWidth > 12){
37904 label.style = "width: " + this.labelWidth + 'px';
37907 if(this.labelWidth < 13 && this.labelmd == 0){
37908 this.labelmd = this.labelWidth;
37911 if(this.labellg > 0){
37912 labelCls = ' col-lg-' + this.labellg;
37913 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37916 if(this.labelmd > 0){
37917 labelCls = ' col-md-' + this.labelmd;
37918 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37921 if(this.labelsm > 0){
37922 labelCls = ' col-sm-' + this.labelsm;
37923 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37926 if(this.labelxs > 0){
37927 labelCls = ' col-xs-' + this.labelxs;
37928 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37932 label.cls += ' ' + labelCls;
37934 cfg.cn.push(label);
37937 Roo.each(['day', 'month', 'year'], function(t){
37940 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37947 inputEl: function ()
37949 return this.el.select('.roo-date-split-field-group-value', true).first();
37952 onRender : function(ct, position)
37956 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37958 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37960 this.dayField = new Roo.bootstrap.form.ComboBox({
37961 allowBlank : this.dayAllowBlank,
37962 alwaysQuery : true,
37963 displayField : 'value',
37966 forceSelection : true,
37968 placeholder : this.dayPlaceholder,
37969 selectOnFocus : true,
37970 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37971 triggerAction : 'all',
37973 valueField : 'value',
37974 store : new Roo.data.SimpleStore({
37975 data : (function() {
37977 _this.fireEvent('days', _this, days);
37980 fields : [ 'value' ]
37983 select : function (_self, record, index)
37985 _this.setValue(_this.getValue());
37990 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37992 this.monthField = new Roo.bootstrap.form.MonthField({
37993 after : '<i class=\"fa fa-calendar\"></i>',
37994 allowBlank : this.monthAllowBlank,
37995 placeholder : this.monthPlaceholder,
37998 render : function (_self)
38000 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38001 e.preventDefault();
38005 select : function (_self, oldvalue, newvalue)
38007 _this.setValue(_this.getValue());
38012 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38014 this.yearField = new Roo.bootstrap.form.ComboBox({
38015 allowBlank : this.yearAllowBlank,
38016 alwaysQuery : true,
38017 displayField : 'value',
38020 forceSelection : true,
38022 placeholder : this.yearPlaceholder,
38023 selectOnFocus : true,
38024 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38025 triggerAction : 'all',
38027 valueField : 'value',
38028 store : new Roo.data.SimpleStore({
38029 data : (function() {
38031 _this.fireEvent('years', _this, years);
38034 fields : [ 'value' ]
38037 select : function (_self, record, index)
38039 _this.setValue(_this.getValue());
38044 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38047 setValue : function(v, format)
38049 this.inputEl.dom.value = v;
38051 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38053 var d = Date.parseDate(v, f);
38060 this.setDay(d.format(this.dayFormat));
38061 this.setMonth(d.format(this.monthFormat));
38062 this.setYear(d.format(this.yearFormat));
38069 setDay : function(v)
38071 this.dayField.setValue(v);
38072 this.inputEl.dom.value = this.getValue();
38077 setMonth : function(v)
38079 this.monthField.setValue(v, true);
38080 this.inputEl.dom.value = this.getValue();
38085 setYear : function(v)
38087 this.yearField.setValue(v);
38088 this.inputEl.dom.value = this.getValue();
38093 getDay : function()
38095 return this.dayField.getValue();
38098 getMonth : function()
38100 return this.monthField.getValue();
38103 getYear : function()
38105 return this.yearField.getValue();
38108 getValue : function()
38110 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38112 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38122 this.inputEl.dom.value = '';
38127 validate : function()
38129 var d = this.dayField.validate();
38130 var m = this.monthField.validate();
38131 var y = this.yearField.validate();
38136 (!this.dayAllowBlank && !d) ||
38137 (!this.monthAllowBlank && !m) ||
38138 (!this.yearAllowBlank && !y)
38143 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38152 this.markInvalid();
38157 markValid : function()
38160 var label = this.el.select('label', true).first();
38161 var icon = this.el.select('i.fa-star', true).first();
38167 this.fireEvent('valid', this);
38171 * Mark this field as invalid
38172 * @param {String} msg The validation message
38174 markInvalid : function(msg)
38177 var label = this.el.select('label', true).first();
38178 var icon = this.el.select('i.fa-star', true).first();
38180 if(label && !icon){
38181 this.el.select('.roo-date-split-field-label', true).createChild({
38183 cls : 'text-danger fa fa-lg fa-star',
38184 tooltip : 'This field is required',
38185 style : 'margin-right:5px;'
38189 this.fireEvent('invalid', this, msg);
38192 clearInvalid : function()
38194 var label = this.el.select('label', true).first();
38195 var icon = this.el.select('i.fa-star', true).first();
38201 this.fireEvent('valid', this);
38204 getName: function()
38214 * @class Roo.bootstrap.LayoutMasonry
38215 * @extends Roo.bootstrap.Component
38216 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38217 * Bootstrap Layout Masonry class
38220 * http://masonry.desandro.com
38222 * The idea is to render all the bricks based on vertical width...
38224 * The original code extends 'outlayer' - we might need to use that....
38227 * Create a new Element
38228 * @param {Object} config The config object
38231 Roo.bootstrap.LayoutMasonry = function(config){
38233 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38237 Roo.bootstrap.LayoutMasonry.register(this);
38243 * Fire after layout the items
38244 * @param {Roo.bootstrap.LayoutMasonry} this
38245 * @param {Roo.EventObject} e
38252 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
38255 * @cfg {Boolean} isLayoutInstant = no animation?
38257 isLayoutInstant : false, // needed?
38260 * @cfg {Number} boxWidth width of the columns
38265 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
38270 * @cfg {Number} padWidth padding below box..
38275 * @cfg {Number} gutter gutter width..
38280 * @cfg {Number} maxCols maximum number of columns
38286 * @cfg {Boolean} isAutoInitial defalut true
38288 isAutoInitial : true,
38293 * @cfg {Boolean} isHorizontal defalut false
38295 isHorizontal : false,
38297 currentSize : null,
38303 bricks: null, //CompositeElement
38307 _isLayoutInited : false,
38309 // isAlternative : false, // only use for vertical layout...
38312 * @cfg {Number} alternativePadWidth padding below box..
38314 alternativePadWidth : 50,
38316 selectedBrick : [],
38318 getAutoCreate : function(){
38320 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38324 cls: 'blog-masonary-wrapper ' + this.cls,
38326 cls : 'mas-boxes masonary'
38333 getChildContainer: function( )
38335 if (this.boxesEl) {
38336 return this.boxesEl;
38339 this.boxesEl = this.el.select('.mas-boxes').first();
38341 return this.boxesEl;
38345 initEvents : function()
38349 if(this.isAutoInitial){
38350 Roo.log('hook children rendered');
38351 this.on('childrenrendered', function() {
38352 Roo.log('children rendered');
38358 initial : function()
38360 this.selectedBrick = [];
38362 this.currentSize = this.el.getBox(true);
38364 Roo.EventManager.onWindowResize(this.resize, this);
38366 if(!this.isAutoInitial){
38374 //this.layout.defer(500,this);
38378 resize : function()
38380 var cs = this.el.getBox(true);
38383 this.currentSize.width == cs.width &&
38384 this.currentSize.x == cs.x &&
38385 this.currentSize.height == cs.height &&
38386 this.currentSize.y == cs.y
38388 Roo.log("no change in with or X or Y");
38392 this.currentSize = cs;
38398 layout : function()
38400 this._resetLayout();
38402 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38404 this.layoutItems( isInstant );
38406 this._isLayoutInited = true;
38408 this.fireEvent('layout', this);
38412 _resetLayout : function()
38414 if(this.isHorizontal){
38415 this.horizontalMeasureColumns();
38419 this.verticalMeasureColumns();
38423 verticalMeasureColumns : function()
38425 this.getContainerWidth();
38427 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38428 // this.colWidth = Math.floor(this.containerWidth * 0.8);
38432 var boxWidth = this.boxWidth + this.padWidth;
38434 if(this.containerWidth < this.boxWidth){
38435 boxWidth = this.containerWidth
38438 var containerWidth = this.containerWidth;
38440 var cols = Math.floor(containerWidth / boxWidth);
38442 this.cols = Math.max( cols, 1 );
38444 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38446 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38448 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38450 this.colWidth = boxWidth + avail - this.padWidth;
38452 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38453 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
38456 horizontalMeasureColumns : function()
38458 this.getContainerWidth();
38460 var boxWidth = this.boxWidth;
38462 if(this.containerWidth < boxWidth){
38463 boxWidth = this.containerWidth;
38466 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38468 this.el.setHeight(boxWidth);
38472 getContainerWidth : function()
38474 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38477 layoutItems : function( isInstant )
38479 Roo.log(this.bricks);
38481 var items = Roo.apply([], this.bricks);
38483 if(this.isHorizontal){
38484 this._horizontalLayoutItems( items , isInstant );
38488 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38489 // this._verticalAlternativeLayoutItems( items , isInstant );
38493 this._verticalLayoutItems( items , isInstant );
38497 _verticalLayoutItems : function ( items , isInstant)
38499 if ( !items || !items.length ) {
38504 ['xs', 'xs', 'xs', 'tall'],
38505 ['xs', 'xs', 'tall'],
38506 ['xs', 'xs', 'sm'],
38507 ['xs', 'xs', 'xs'],
38513 ['sm', 'xs', 'xs'],
38517 ['tall', 'xs', 'xs', 'xs'],
38518 ['tall', 'xs', 'xs'],
38530 Roo.each(items, function(item, k){
38532 switch (item.size) {
38533 // these layouts take up a full box,
38544 boxes.push([item]);
38567 var filterPattern = function(box, length)
38575 var pattern = box.slice(0, length);
38579 Roo.each(pattern, function(i){
38580 format.push(i.size);
38583 Roo.each(standard, function(s){
38585 if(String(s) != String(format)){
38594 if(!match && length == 1){
38599 filterPattern(box, length - 1);
38603 queue.push(pattern);
38605 box = box.slice(length, box.length);
38607 filterPattern(box, 4);
38613 Roo.each(boxes, function(box, k){
38619 if(box.length == 1){
38624 filterPattern(box, 4);
38628 this._processVerticalLayoutQueue( queue, isInstant );
38632 // _verticalAlternativeLayoutItems : function( items , isInstant )
38634 // if ( !items || !items.length ) {
38638 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
38642 _horizontalLayoutItems : function ( items , isInstant)
38644 if ( !items || !items.length || items.length < 3) {
38650 var eItems = items.slice(0, 3);
38652 items = items.slice(3, items.length);
38655 ['xs', 'xs', 'xs', 'wide'],
38656 ['xs', 'xs', 'wide'],
38657 ['xs', 'xs', 'sm'],
38658 ['xs', 'xs', 'xs'],
38664 ['sm', 'xs', 'xs'],
38668 ['wide', 'xs', 'xs', 'xs'],
38669 ['wide', 'xs', 'xs'],
38682 Roo.each(items, function(item, k){
38684 switch (item.size) {
38695 boxes.push([item]);
38719 var filterPattern = function(box, length)
38727 var pattern = box.slice(0, length);
38731 Roo.each(pattern, function(i){
38732 format.push(i.size);
38735 Roo.each(standard, function(s){
38737 if(String(s) != String(format)){
38746 if(!match && length == 1){
38751 filterPattern(box, length - 1);
38755 queue.push(pattern);
38757 box = box.slice(length, box.length);
38759 filterPattern(box, 4);
38765 Roo.each(boxes, function(box, k){
38771 if(box.length == 1){
38776 filterPattern(box, 4);
38783 var pos = this.el.getBox(true);
38787 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38789 var hit_end = false;
38791 Roo.each(queue, function(box){
38795 Roo.each(box, function(b){
38797 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38807 Roo.each(box, function(b){
38809 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38812 mx = Math.max(mx, b.x);
38816 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38820 Roo.each(box, function(b){
38822 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38836 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38839 /** Sets position of item in DOM
38840 * @param {Element} item
38841 * @param {Number} x - horizontal position
38842 * @param {Number} y - vertical position
38843 * @param {Boolean} isInstant - disables transitions
38845 _processVerticalLayoutQueue : function( queue, isInstant )
38847 var pos = this.el.getBox(true);
38852 for (var i = 0; i < this.cols; i++){
38856 Roo.each(queue, function(box, k){
38858 var col = k % this.cols;
38860 Roo.each(box, function(b,kk){
38862 b.el.position('absolute');
38864 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38865 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38867 if(b.size == 'md-left' || b.size == 'md-right'){
38868 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38869 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38872 b.el.setWidth(width);
38873 b.el.setHeight(height);
38875 b.el.select('iframe',true).setSize(width,height);
38879 for (var i = 0; i < this.cols; i++){
38881 if(maxY[i] < maxY[col]){
38886 col = Math.min(col, i);
38890 x = pos.x + col * (this.colWidth + this.padWidth);
38894 var positions = [];
38896 switch (box.length){
38898 positions = this.getVerticalOneBoxColPositions(x, y, box);
38901 positions = this.getVerticalTwoBoxColPositions(x, y, box);
38904 positions = this.getVerticalThreeBoxColPositions(x, y, box);
38907 positions = this.getVerticalFourBoxColPositions(x, y, box);
38913 Roo.each(box, function(b,kk){
38915 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38917 var sz = b.el.getSize();
38919 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38927 for (var i = 0; i < this.cols; i++){
38928 mY = Math.max(mY, maxY[i]);
38931 this.el.setHeight(mY - pos.y);
38935 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38937 // var pos = this.el.getBox(true);
38940 // var maxX = pos.right;
38942 // var maxHeight = 0;
38944 // Roo.each(items, function(item, k){
38948 // item.el.position('absolute');
38950 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38952 // item.el.setWidth(width);
38954 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38956 // item.el.setHeight(height);
38959 // item.el.setXY([x, y], isInstant ? false : true);
38961 // item.el.setXY([maxX - width, y], isInstant ? false : true);
38964 // y = y + height + this.alternativePadWidth;
38966 // maxHeight = maxHeight + height + this.alternativePadWidth;
38970 // this.el.setHeight(maxHeight);
38974 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38976 var pos = this.el.getBox(true);
38981 var maxX = pos.right;
38983 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38985 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38987 Roo.each(queue, function(box, k){
38989 Roo.each(box, function(b, kk){
38991 b.el.position('absolute');
38993 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38994 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38996 if(b.size == 'md-left' || b.size == 'md-right'){
38997 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38998 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39001 b.el.setWidth(width);
39002 b.el.setHeight(height);
39010 var positions = [];
39012 switch (box.length){
39014 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39017 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39020 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39023 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39029 Roo.each(box, function(b,kk){
39031 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39033 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39041 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39043 Roo.each(eItems, function(b,k){
39045 b.size = (k == 0) ? 'sm' : 'xs';
39046 b.x = (k == 0) ? 2 : 1;
39047 b.y = (k == 0) ? 2 : 1;
39049 b.el.position('absolute');
39051 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39053 b.el.setWidth(width);
39055 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39057 b.el.setHeight(height);
39061 var positions = [];
39064 x : maxX - this.unitWidth * 2 - this.gutter,
39069 x : maxX - this.unitWidth,
39070 y : minY + (this.unitWidth + this.gutter) * 2
39074 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39078 Roo.each(eItems, function(b,k){
39080 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39086 getVerticalOneBoxColPositions : function(x, y, box)
39090 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39092 if(box[0].size == 'md-left'){
39096 if(box[0].size == 'md-right'){
39101 x : x + (this.unitWidth + this.gutter) * rand,
39108 getVerticalTwoBoxColPositions : function(x, y, box)
39112 if(box[0].size == 'xs'){
39116 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39120 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39134 x : x + (this.unitWidth + this.gutter) * 2,
39135 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39142 getVerticalThreeBoxColPositions : function(x, y, box)
39146 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39154 x : x + (this.unitWidth + this.gutter) * 1,
39159 x : x + (this.unitWidth + this.gutter) * 2,
39167 if(box[0].size == 'xs' && box[1].size == 'xs'){
39176 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39180 x : x + (this.unitWidth + this.gutter) * 1,
39194 x : x + (this.unitWidth + this.gutter) * 2,
39199 x : x + (this.unitWidth + this.gutter) * 2,
39200 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39207 getVerticalFourBoxColPositions : function(x, y, box)
39211 if(box[0].size == 'xs'){
39220 y : y + (this.unitHeight + this.gutter) * 1
39225 y : y + (this.unitHeight + this.gutter) * 2
39229 x : x + (this.unitWidth + this.gutter) * 1,
39243 x : x + (this.unitWidth + this.gutter) * 2,
39248 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39249 y : y + (this.unitHeight + this.gutter) * 1
39253 x : x + (this.unitWidth + this.gutter) * 2,
39254 y : y + (this.unitWidth + this.gutter) * 2
39261 getHorizontalOneBoxColPositions : function(maxX, minY, box)
39265 if(box[0].size == 'md-left'){
39267 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39274 if(box[0].size == 'md-right'){
39276 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39277 y : minY + (this.unitWidth + this.gutter) * 1
39283 var rand = Math.floor(Math.random() * (4 - box[0].y));
39286 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39287 y : minY + (this.unitWidth + this.gutter) * rand
39294 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39298 if(box[0].size == 'xs'){
39301 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39306 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39307 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39315 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39320 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39321 y : minY + (this.unitWidth + this.gutter) * 2
39328 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39332 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39335 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39340 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39341 y : minY + (this.unitWidth + this.gutter) * 1
39345 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39346 y : minY + (this.unitWidth + this.gutter) * 2
39353 if(box[0].size == 'xs' && box[1].size == 'xs'){
39356 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39361 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39366 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39367 y : minY + (this.unitWidth + this.gutter) * 1
39375 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39380 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39381 y : minY + (this.unitWidth + this.gutter) * 2
39385 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39386 y : minY + (this.unitWidth + this.gutter) * 2
39393 getHorizontalFourBoxColPositions : function(maxX, minY, box)
39397 if(box[0].size == 'xs'){
39400 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39405 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39410 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),
39415 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39416 y : minY + (this.unitWidth + this.gutter) * 1
39424 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39429 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39430 y : minY + (this.unitWidth + this.gutter) * 2
39434 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39435 y : minY + (this.unitWidth + this.gutter) * 2
39439 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),
39440 y : minY + (this.unitWidth + this.gutter) * 2
39448 * remove a Masonry Brick
39449 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39451 removeBrick : function(brick_id)
39457 for (var i = 0; i<this.bricks.length; i++) {
39458 if (this.bricks[i].id == brick_id) {
39459 this.bricks.splice(i,1);
39460 this.el.dom.removeChild(Roo.get(brick_id).dom);
39467 * adds a Masonry Brick
39468 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39470 addBrick : function(cfg)
39472 var cn = new Roo.bootstrap.MasonryBrick(cfg);
39473 //this.register(cn);
39474 cn.parentId = this.id;
39475 cn.render(this.el);
39480 * register a Masonry Brick
39481 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39484 register : function(brick)
39486 this.bricks.push(brick);
39487 brick.masonryId = this.id;
39491 * clear all the Masonry Brick
39493 clearAll : function()
39496 //this.getChildContainer().dom.innerHTML = "";
39497 this.el.dom.innerHTML = '';
39500 getSelected : function()
39502 if (!this.selectedBrick) {
39506 return this.selectedBrick;
39510 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39514 * register a Masonry Layout
39515 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39518 register : function(layout)
39520 this.groups[layout.id] = layout;
39523 * fetch a Masonry Layout based on the masonry layout ID
39524 * @param {string} the masonry layout to add
39525 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39528 get: function(layout_id) {
39529 if (typeof(this.groups[layout_id]) == 'undefined') {
39532 return this.groups[layout_id] ;
39544 * http://masonry.desandro.com
39546 * The idea is to render all the bricks based on vertical width...
39548 * The original code extends 'outlayer' - we might need to use that....
39554 * @class Roo.bootstrap.LayoutMasonryAuto
39555 * @extends Roo.bootstrap.Component
39556 * Bootstrap Layout Masonry class
39559 * Create a new Element
39560 * @param {Object} config The config object
39563 Roo.bootstrap.LayoutMasonryAuto = function(config){
39564 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39567 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
39570 * @cfg {Boolean} isFitWidth - resize the width..
39572 isFitWidth : false, // options..
39574 * @cfg {Boolean} isOriginLeft = left align?
39576 isOriginLeft : true,
39578 * @cfg {Boolean} isOriginTop = top align?
39580 isOriginTop : false,
39582 * @cfg {Boolean} isLayoutInstant = no animation?
39584 isLayoutInstant : false, // needed?
39586 * @cfg {Boolean} isResizingContainer = not sure if this is used..
39588 isResizingContainer : true,
39590 * @cfg {Number} columnWidth width of the columns
39596 * @cfg {Number} maxCols maximum number of columns
39601 * @cfg {Number} padHeight padding below box..
39607 * @cfg {Boolean} isAutoInitial defalut true
39610 isAutoInitial : true,
39616 initialColumnWidth : 0,
39617 currentSize : null,
39619 colYs : null, // array.
39626 bricks: null, //CompositeElement
39627 cols : 0, // array?
39628 // element : null, // wrapped now this.el
39629 _isLayoutInited : null,
39632 getAutoCreate : function(){
39636 cls: 'blog-masonary-wrapper ' + this.cls,
39638 cls : 'mas-boxes masonary'
39645 getChildContainer: function( )
39647 if (this.boxesEl) {
39648 return this.boxesEl;
39651 this.boxesEl = this.el.select('.mas-boxes').first();
39653 return this.boxesEl;
39657 initEvents : function()
39661 if(this.isAutoInitial){
39662 Roo.log('hook children rendered');
39663 this.on('childrenrendered', function() {
39664 Roo.log('children rendered');
39671 initial : function()
39673 this.reloadItems();
39675 this.currentSize = this.el.getBox(true);
39677 /// was window resize... - let's see if this works..
39678 Roo.EventManager.onWindowResize(this.resize, this);
39680 if(!this.isAutoInitial){
39685 this.layout.defer(500,this);
39688 reloadItems: function()
39690 this.bricks = this.el.select('.masonry-brick', true);
39692 this.bricks.each(function(b) {
39693 //Roo.log(b.getSize());
39694 if (!b.attr('originalwidth')) {
39695 b.attr('originalwidth', b.getSize().width);
39700 Roo.log(this.bricks.elements.length);
39703 resize : function()
39706 var cs = this.el.getBox(true);
39708 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39709 Roo.log("no change in with or X");
39712 this.currentSize = cs;
39716 layout : function()
39719 this._resetLayout();
39720 //this._manageStamps();
39722 // don't animate first layout
39723 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39724 this.layoutItems( isInstant );
39726 // flag for initalized
39727 this._isLayoutInited = true;
39730 layoutItems : function( isInstant )
39732 //var items = this._getItemsForLayout( this.items );
39733 // original code supports filtering layout items.. we just ignore it..
39735 this._layoutItems( this.bricks , isInstant );
39737 this._postLayout();
39739 _layoutItems : function ( items , isInstant)
39741 //this.fireEvent( 'layout', this, items );
39744 if ( !items || !items.elements.length ) {
39745 // no items, emit event with empty array
39750 items.each(function(item) {
39751 Roo.log("layout item");
39753 // get x/y object from method
39754 var position = this._getItemLayoutPosition( item );
39756 position.item = item;
39757 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39758 queue.push( position );
39761 this._processLayoutQueue( queue );
39763 /** Sets position of item in DOM
39764 * @param {Element} item
39765 * @param {Number} x - horizontal position
39766 * @param {Number} y - vertical position
39767 * @param {Boolean} isInstant - disables transitions
39769 _processLayoutQueue : function( queue )
39771 for ( var i=0, len = queue.length; i < len; i++ ) {
39772 var obj = queue[i];
39773 obj.item.position('absolute');
39774 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39780 * Any logic you want to do after each layout,
39781 * i.e. size the container
39783 _postLayout : function()
39785 this.resizeContainer();
39788 resizeContainer : function()
39790 if ( !this.isResizingContainer ) {
39793 var size = this._getContainerSize();
39795 this.el.setSize(size.width,size.height);
39796 this.boxesEl.setSize(size.width,size.height);
39802 _resetLayout : function()
39804 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39805 this.colWidth = this.el.getWidth();
39806 //this.gutter = this.el.getWidth();
39808 this.measureColumns();
39814 this.colYs.push( 0 );
39820 measureColumns : function()
39822 this.getContainerWidth();
39823 // if columnWidth is 0, default to outerWidth of first item
39824 if ( !this.columnWidth ) {
39825 var firstItem = this.bricks.first();
39826 Roo.log(firstItem);
39827 this.columnWidth = this.containerWidth;
39828 if (firstItem && firstItem.attr('originalwidth') ) {
39829 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39831 // columnWidth fall back to item of first element
39832 Roo.log("set column width?");
39833 this.initialColumnWidth = this.columnWidth ;
39835 // if first elem has no width, default to size of container
39840 if (this.initialColumnWidth) {
39841 this.columnWidth = this.initialColumnWidth;
39846 // column width is fixed at the top - however if container width get's smaller we should
39849 // this bit calcs how man columns..
39851 var columnWidth = this.columnWidth += this.gutter;
39853 // calculate columns
39854 var containerWidth = this.containerWidth + this.gutter;
39856 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39857 // fix rounding errors, typically with gutters
39858 var excess = columnWidth - containerWidth % columnWidth;
39861 // if overshoot is less than a pixel, round up, otherwise floor it
39862 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39863 cols = Math[ mathMethod ]( cols );
39864 this.cols = Math.max( cols, 1 );
39865 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39867 // padding positioning..
39868 var totalColWidth = this.cols * this.columnWidth;
39869 var padavail = this.containerWidth - totalColWidth;
39870 // so for 2 columns - we need 3 'pads'
39872 var padNeeded = (1+this.cols) * this.padWidth;
39874 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39876 this.columnWidth += padExtra
39877 //this.padWidth = Math.floor(padavail / ( this.cols));
39879 // adjust colum width so that padding is fixed??
39881 // we have 3 columns ... total = width * 3
39882 // we have X left over... that should be used by
39884 //if (this.expandC) {
39892 getContainerWidth : function()
39894 /* // container is parent if fit width
39895 var container = this.isFitWidth ? this.element.parentNode : this.element;
39896 // check that this.size and size are there
39897 // IE8 triggers resize on body size change, so they might not be
39899 var size = getSize( container ); //FIXME
39900 this.containerWidth = size && size.innerWidth; //FIXME
39903 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39907 _getItemLayoutPosition : function( item ) // what is item?
39909 // we resize the item to our columnWidth..
39911 item.setWidth(this.columnWidth);
39912 item.autoBoxAdjust = false;
39914 var sz = item.getSize();
39916 // how many columns does this brick span
39917 var remainder = this.containerWidth % this.columnWidth;
39919 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39920 // round if off by 1 pixel, otherwise use ceil
39921 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
39922 colSpan = Math.min( colSpan, this.cols );
39924 // normally this should be '1' as we dont' currently allow multi width columns..
39926 var colGroup = this._getColGroup( colSpan );
39927 // get the minimum Y value from the columns
39928 var minimumY = Math.min.apply( Math, colGroup );
39929 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
39931 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
39933 // position the brick
39935 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39936 y: this.currentSize.y + minimumY + this.padHeight
39940 // apply setHeight to necessary columns
39941 var setHeight = minimumY + sz.height + this.padHeight;
39942 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
39944 var setSpan = this.cols + 1 - colGroup.length;
39945 for ( var i = 0; i < setSpan; i++ ) {
39946 this.colYs[ shortColIndex + i ] = setHeight ;
39953 * @param {Number} colSpan - number of columns the element spans
39954 * @returns {Array} colGroup
39956 _getColGroup : function( colSpan )
39958 if ( colSpan < 2 ) {
39959 // if brick spans only one column, use all the column Ys
39964 // how many different places could this brick fit horizontally
39965 var groupCount = this.cols + 1 - colSpan;
39966 // for each group potential horizontal position
39967 for ( var i = 0; i < groupCount; i++ ) {
39968 // make an array of colY values for that one group
39969 var groupColYs = this.colYs.slice( i, i + colSpan );
39970 // and get the max value of the array
39971 colGroup[i] = Math.max.apply( Math, groupColYs );
39976 _manageStamp : function( stamp )
39978 var stampSize = stamp.getSize();
39979 var offset = stamp.getBox();
39980 // get the columns that this stamp affects
39981 var firstX = this.isOriginLeft ? offset.x : offset.right;
39982 var lastX = firstX + stampSize.width;
39983 var firstCol = Math.floor( firstX / this.columnWidth );
39984 firstCol = Math.max( 0, firstCol );
39986 var lastCol = Math.floor( lastX / this.columnWidth );
39987 // lastCol should not go over if multiple of columnWidth #425
39988 lastCol -= lastX % this.columnWidth ? 0 : 1;
39989 lastCol = Math.min( this.cols - 1, lastCol );
39991 // set colYs to bottom of the stamp
39992 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39995 for ( var i = firstCol; i <= lastCol; i++ ) {
39996 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40001 _getContainerSize : function()
40003 this.maxY = Math.max.apply( Math, this.colYs );
40008 if ( this.isFitWidth ) {
40009 size.width = this._getContainerFitWidth();
40015 _getContainerFitWidth : function()
40017 var unusedCols = 0;
40018 // count unused columns
40021 if ( this.colYs[i] !== 0 ) {
40026 // fit container to columns that have been used
40027 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40030 needsResizeLayout : function()
40032 var previousWidth = this.containerWidth;
40033 this.getContainerWidth();
40034 return previousWidth !== this.containerWidth;
40049 * @class Roo.bootstrap.MasonryBrick
40050 * @extends Roo.bootstrap.Component
40051 * Bootstrap MasonryBrick class
40054 * Create a new MasonryBrick
40055 * @param {Object} config The config object
40058 Roo.bootstrap.MasonryBrick = function(config){
40060 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40062 Roo.bootstrap.MasonryBrick.register(this);
40068 * When a MasonryBrick is clcik
40069 * @param {Roo.bootstrap.MasonryBrick} this
40070 * @param {Roo.EventObject} e
40076 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40079 * @cfg {String} title
40083 * @cfg {String} html
40087 * @cfg {String} bgimage
40091 * @cfg {String} videourl
40095 * @cfg {String} cls
40099 * @cfg {String} href
40103 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40108 * @cfg {String} placetitle (center|bottom)
40113 * @cfg {Boolean} isFitContainer defalut true
40115 isFitContainer : true,
40118 * @cfg {Boolean} preventDefault defalut false
40120 preventDefault : false,
40123 * @cfg {Boolean} inverse defalut false
40125 maskInverse : false,
40127 getAutoCreate : function()
40129 if(!this.isFitContainer){
40130 return this.getSplitAutoCreate();
40133 var cls = 'masonry-brick masonry-brick-full';
40135 if(this.href.length){
40136 cls += ' masonry-brick-link';
40139 if(this.bgimage.length){
40140 cls += ' masonry-brick-image';
40143 if(this.maskInverse){
40144 cls += ' mask-inverse';
40147 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40148 cls += ' enable-mask';
40152 cls += ' masonry-' + this.size + '-brick';
40155 if(this.placetitle.length){
40157 switch (this.placetitle) {
40159 cls += ' masonry-center-title';
40162 cls += ' masonry-bottom-title';
40169 if(!this.html.length && !this.bgimage.length){
40170 cls += ' masonry-center-title';
40173 if(!this.html.length && this.bgimage.length){
40174 cls += ' masonry-bottom-title';
40179 cls += ' ' + this.cls;
40183 tag: (this.href.length) ? 'a' : 'div',
40188 cls: 'masonry-brick-mask'
40192 cls: 'masonry-brick-paragraph',
40198 if(this.href.length){
40199 cfg.href = this.href;
40202 var cn = cfg.cn[1].cn;
40204 if(this.title.length){
40207 cls: 'masonry-brick-title',
40212 if(this.html.length){
40215 cls: 'masonry-brick-text',
40220 if (!this.title.length && !this.html.length) {
40221 cfg.cn[1].cls += ' hide';
40224 if(this.bgimage.length){
40227 cls: 'masonry-brick-image-view',
40232 if(this.videourl.length){
40233 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40234 // youtube support only?
40237 cls: 'masonry-brick-image-view',
40240 allowfullscreen : true
40248 getSplitAutoCreate : function()
40250 var cls = 'masonry-brick masonry-brick-split';
40252 if(this.href.length){
40253 cls += ' masonry-brick-link';
40256 if(this.bgimage.length){
40257 cls += ' masonry-brick-image';
40261 cls += ' masonry-' + this.size + '-brick';
40264 switch (this.placetitle) {
40266 cls += ' masonry-center-title';
40269 cls += ' masonry-bottom-title';
40272 if(!this.bgimage.length){
40273 cls += ' masonry-center-title';
40276 if(this.bgimage.length){
40277 cls += ' masonry-bottom-title';
40283 cls += ' ' + this.cls;
40287 tag: (this.href.length) ? 'a' : 'div',
40292 cls: 'masonry-brick-split-head',
40296 cls: 'masonry-brick-paragraph',
40303 cls: 'masonry-brick-split-body',
40309 if(this.href.length){
40310 cfg.href = this.href;
40313 if(this.title.length){
40314 cfg.cn[0].cn[0].cn.push({
40316 cls: 'masonry-brick-title',
40321 if(this.html.length){
40322 cfg.cn[1].cn.push({
40324 cls: 'masonry-brick-text',
40329 if(this.bgimage.length){
40330 cfg.cn[0].cn.push({
40332 cls: 'masonry-brick-image-view',
40337 if(this.videourl.length){
40338 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40339 // youtube support only?
40340 cfg.cn[0].cn.cn.push({
40342 cls: 'masonry-brick-image-view',
40345 allowfullscreen : true
40352 initEvents: function()
40354 switch (this.size) {
40387 this.el.on('touchstart', this.onTouchStart, this);
40388 this.el.on('touchmove', this.onTouchMove, this);
40389 this.el.on('touchend', this.onTouchEnd, this);
40390 this.el.on('contextmenu', this.onContextMenu, this);
40392 this.el.on('mouseenter' ,this.enter, this);
40393 this.el.on('mouseleave', this.leave, this);
40394 this.el.on('click', this.onClick, this);
40397 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40398 this.parent().bricks.push(this);
40403 onClick: function(e, el)
40405 var time = this.endTimer - this.startTimer;
40406 // Roo.log(e.preventDefault());
40409 e.preventDefault();
40414 if(!this.preventDefault){
40418 e.preventDefault();
40420 if (this.activeClass != '') {
40421 this.selectBrick();
40424 this.fireEvent('click', this, e);
40427 enter: function(e, el)
40429 e.preventDefault();
40431 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40435 if(this.bgimage.length && this.html.length){
40436 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40440 leave: function(e, el)
40442 e.preventDefault();
40444 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40448 if(this.bgimage.length && this.html.length){
40449 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40453 onTouchStart: function(e, el)
40455 // e.preventDefault();
40457 this.touchmoved = false;
40459 if(!this.isFitContainer){
40463 if(!this.bgimage.length || !this.html.length){
40467 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40469 this.timer = new Date().getTime();
40473 onTouchMove: function(e, el)
40475 this.touchmoved = true;
40478 onContextMenu : function(e,el)
40480 e.preventDefault();
40481 e.stopPropagation();
40485 onTouchEnd: function(e, el)
40487 // e.preventDefault();
40489 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40496 if(!this.bgimage.length || !this.html.length){
40498 if(this.href.length){
40499 window.location.href = this.href;
40505 if(!this.isFitContainer){
40509 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40511 window.location.href = this.href;
40514 //selection on single brick only
40515 selectBrick : function() {
40517 if (!this.parentId) {
40521 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40522 var index = m.selectedBrick.indexOf(this.id);
40525 m.selectedBrick.splice(index,1);
40526 this.el.removeClass(this.activeClass);
40530 for(var i = 0; i < m.selectedBrick.length; i++) {
40531 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40532 b.el.removeClass(b.activeClass);
40535 m.selectedBrick = [];
40537 m.selectedBrick.push(this.id);
40538 this.el.addClass(this.activeClass);
40542 isSelected : function(){
40543 return this.el.hasClass(this.activeClass);
40548 Roo.apply(Roo.bootstrap.MasonryBrick, {
40551 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40553 * register a Masonry Brick
40554 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40557 register : function(brick)
40559 //this.groups[brick.id] = brick;
40560 this.groups.add(brick.id, brick);
40563 * fetch a masonry brick based on the masonry brick ID
40564 * @param {string} the masonry brick to add
40565 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40568 get: function(brick_id)
40570 // if (typeof(this.groups[brick_id]) == 'undefined') {
40573 // return this.groups[brick_id] ;
40575 if(this.groups.key(brick_id)) {
40576 return this.groups.key(brick_id);
40594 * @class Roo.bootstrap.Brick
40595 * @extends Roo.bootstrap.Component
40596 * Bootstrap Brick class
40599 * Create a new Brick
40600 * @param {Object} config The config object
40603 Roo.bootstrap.Brick = function(config){
40604 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40610 * When a Brick is click
40611 * @param {Roo.bootstrap.Brick} this
40612 * @param {Roo.EventObject} e
40618 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
40621 * @cfg {String} title
40625 * @cfg {String} html
40629 * @cfg {String} bgimage
40633 * @cfg {String} cls
40637 * @cfg {String} href
40641 * @cfg {String} video
40645 * @cfg {Boolean} square
40649 getAutoCreate : function()
40651 var cls = 'roo-brick';
40653 if(this.href.length){
40654 cls += ' roo-brick-link';
40657 if(this.bgimage.length){
40658 cls += ' roo-brick-image';
40661 if(!this.html.length && !this.bgimage.length){
40662 cls += ' roo-brick-center-title';
40665 if(!this.html.length && this.bgimage.length){
40666 cls += ' roo-brick-bottom-title';
40670 cls += ' ' + this.cls;
40674 tag: (this.href.length) ? 'a' : 'div',
40679 cls: 'roo-brick-paragraph',
40685 if(this.href.length){
40686 cfg.href = this.href;
40689 var cn = cfg.cn[0].cn;
40691 if(this.title.length){
40694 cls: 'roo-brick-title',
40699 if(this.html.length){
40702 cls: 'roo-brick-text',
40709 if(this.bgimage.length){
40712 cls: 'roo-brick-image-view',
40720 initEvents: function()
40722 if(this.title.length || this.html.length){
40723 this.el.on('mouseenter' ,this.enter, this);
40724 this.el.on('mouseleave', this.leave, this);
40727 Roo.EventManager.onWindowResize(this.resize, this);
40729 if(this.bgimage.length){
40730 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40731 this.imageEl.on('load', this.onImageLoad, this);
40738 onImageLoad : function()
40743 resize : function()
40745 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40747 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40749 if(this.bgimage.length){
40750 var image = this.el.select('.roo-brick-image-view', true).first();
40752 image.setWidth(paragraph.getWidth());
40755 image.setHeight(paragraph.getWidth());
40758 this.el.setHeight(image.getHeight());
40759 paragraph.setHeight(image.getHeight());
40765 enter: function(e, el)
40767 e.preventDefault();
40769 if(this.bgimage.length){
40770 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40771 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40775 leave: function(e, el)
40777 e.preventDefault();
40779 if(this.bgimage.length){
40780 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40781 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40796 * @class Roo.bootstrap.form.NumberField
40797 * @extends Roo.bootstrap.form.Input
40798 * Bootstrap NumberField class
40804 * Create a new NumberField
40805 * @param {Object} config The config object
40808 Roo.bootstrap.form.NumberField = function(config){
40809 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40812 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40815 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40817 allowDecimals : true,
40819 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40821 decimalSeparator : ".",
40823 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40825 decimalPrecision : 2,
40827 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40829 allowNegative : true,
40832 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40836 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40838 minValue : Number.NEGATIVE_INFINITY,
40840 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40842 maxValue : Number.MAX_VALUE,
40844 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40846 minText : "The minimum value for this field is {0}",
40848 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40850 maxText : "The maximum value for this field is {0}",
40852 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40853 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40855 nanText : "{0} is not a valid number",
40857 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40859 thousandsDelimiter : false,
40861 * @cfg {String} valueAlign alignment of value
40863 valueAlign : "left",
40865 getAutoCreate : function()
40867 var hiddenInput = {
40871 cls: 'hidden-number-input'
40875 hiddenInput.name = this.name;
40880 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40882 this.name = hiddenInput.name;
40884 if(cfg.cn.length > 0) {
40885 cfg.cn.push(hiddenInput);
40892 initEvents : function()
40894 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40896 var allowed = "0123456789";
40898 if(this.allowDecimals){
40899 allowed += this.decimalSeparator;
40902 if(this.allowNegative){
40906 if(this.thousandsDelimiter) {
40910 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40912 var keyPress = function(e){
40914 var k = e.getKey();
40916 var c = e.getCharCode();
40919 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40920 allowed.indexOf(String.fromCharCode(c)) === -1
40926 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40930 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40935 this.el.on("keypress", keyPress, this);
40938 validateValue : function(value)
40941 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40945 var num = this.parseValue(value);
40948 this.markInvalid(String.format(this.nanText, value));
40952 if(num < this.minValue){
40953 this.markInvalid(String.format(this.minText, this.minValue));
40957 if(num > this.maxValue){
40958 this.markInvalid(String.format(this.maxText, this.maxValue));
40965 getValue : function()
40967 var v = this.hiddenEl().getValue();
40969 return this.fixPrecision(this.parseValue(v));
40972 parseValue : function(value)
40974 if(this.thousandsDelimiter) {
40976 r = new RegExp(",", "g");
40977 value = value.replace(r, "");
40980 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40981 return isNaN(value) ? '' : value;
40984 fixPrecision : function(value)
40986 if(this.thousandsDelimiter) {
40988 r = new RegExp(",", "g");
40989 value = value.replace(r, "");
40992 var nan = isNaN(value);
40994 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40995 return nan ? '' : value;
40997 return parseFloat(value).toFixed(this.decimalPrecision);
41000 setValue : function(v)
41002 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41008 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41010 this.inputEl().dom.value = (v == '') ? '' :
41011 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41013 if(!this.allowZero && v === '0') {
41014 this.hiddenEl().dom.value = '';
41015 this.inputEl().dom.value = '';
41022 decimalPrecisionFcn : function(v)
41024 return Math.floor(v);
41027 beforeBlur : function()
41029 var v = this.parseValue(this.getRawValue());
41031 if(v || v === 0 || v === ''){
41036 hiddenEl : function()
41038 return this.el.select('input.hidden-number-input',true).first();
41050 * @class Roo.bootstrap.DocumentSlider
41051 * @extends Roo.bootstrap.Component
41052 * Bootstrap DocumentSlider class
41055 * Create a new DocumentViewer
41056 * @param {Object} config The config object
41059 Roo.bootstrap.DocumentSlider = function(config){
41060 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41067 * Fire after initEvent
41068 * @param {Roo.bootstrap.DocumentSlider} this
41073 * Fire after update
41074 * @param {Roo.bootstrap.DocumentSlider} this
41080 * @param {Roo.bootstrap.DocumentSlider} this
41086 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41092 getAutoCreate : function()
41096 cls : 'roo-document-slider',
41100 cls : 'roo-document-slider-header',
41104 cls : 'roo-document-slider-header-title'
41110 cls : 'roo-document-slider-body',
41114 cls : 'roo-document-slider-prev',
41118 cls : 'fa fa-chevron-left'
41124 cls : 'roo-document-slider-thumb',
41128 cls : 'roo-document-slider-image'
41134 cls : 'roo-document-slider-next',
41138 cls : 'fa fa-chevron-right'
41150 initEvents : function()
41152 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41153 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41155 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41156 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41158 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41159 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41161 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41162 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41164 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41165 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41167 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41168 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41170 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41171 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41173 this.thumbEl.on('click', this.onClick, this);
41175 this.prevIndicator.on('click', this.prev, this);
41177 this.nextIndicator.on('click', this.next, this);
41181 initial : function()
41183 if(this.files.length){
41184 this.indicator = 1;
41188 this.fireEvent('initial', this);
41191 update : function()
41193 this.imageEl.attr('src', this.files[this.indicator - 1]);
41195 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41197 this.prevIndicator.show();
41199 if(this.indicator == 1){
41200 this.prevIndicator.hide();
41203 this.nextIndicator.show();
41205 if(this.indicator == this.files.length){
41206 this.nextIndicator.hide();
41209 this.thumbEl.scrollTo('top');
41211 this.fireEvent('update', this);
41214 onClick : function(e)
41216 e.preventDefault();
41218 this.fireEvent('click', this);
41223 e.preventDefault();
41225 this.indicator = Math.max(1, this.indicator - 1);
41232 e.preventDefault();
41234 this.indicator = Math.min(this.files.length, this.indicator + 1);
41248 * @class Roo.bootstrap.form.RadioSet
41249 * @extends Roo.bootstrap.form.Input
41250 * @children Roo.bootstrap.form.Radio
41251 * Bootstrap RadioSet class
41252 * @cfg {String} indicatorpos (left|right) default left
41253 * @cfg {Boolean} inline (true|false) inline the element (default true)
41254 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41256 * Create a new RadioSet
41257 * @param {Object} config The config object
41260 Roo.bootstrap.form.RadioSet = function(config){
41262 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41266 Roo.bootstrap.form.RadioSet.register(this);
41271 * Fires when the element is checked or unchecked.
41272 * @param {Roo.bootstrap.form.RadioSet} this This radio
41273 * @param {Roo.bootstrap.form.Radio} item The checked item
41278 * Fires when the element is click.
41279 * @param {Roo.bootstrap.form.RadioSet} this This radio set
41280 * @param {Roo.bootstrap.form.Radio} item The checked item
41281 * @param {Roo.EventObject} e The event object
41288 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
41296 indicatorpos : 'left',
41298 getAutoCreate : function()
41302 cls : 'roo-radio-set-label',
41306 html : this.fieldLabel
41310 if (Roo.bootstrap.version == 3) {
41313 if(this.indicatorpos == 'left'){
41316 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41317 tooltip : 'This field is required'
41322 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41323 tooltip : 'This field is required'
41329 cls : 'roo-radio-set-items'
41332 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41334 if (align === 'left' && this.fieldLabel.length) {
41337 cls : "roo-radio-set-right",
41343 if(this.labelWidth > 12){
41344 label.style = "width: " + this.labelWidth + 'px';
41347 if(this.labelWidth < 13 && this.labelmd == 0){
41348 this.labelmd = this.labelWidth;
41351 if(this.labellg > 0){
41352 label.cls += ' col-lg-' + this.labellg;
41353 items.cls += ' col-lg-' + (12 - this.labellg);
41356 if(this.labelmd > 0){
41357 label.cls += ' col-md-' + this.labelmd;
41358 items.cls += ' col-md-' + (12 - this.labelmd);
41361 if(this.labelsm > 0){
41362 label.cls += ' col-sm-' + this.labelsm;
41363 items.cls += ' col-sm-' + (12 - this.labelsm);
41366 if(this.labelxs > 0){
41367 label.cls += ' col-xs-' + this.labelxs;
41368 items.cls += ' col-xs-' + (12 - this.labelxs);
41374 cls : 'roo-radio-set',
41378 cls : 'roo-radio-set-input',
41381 value : this.value ? this.value : ''
41388 if(this.weight.length){
41389 cfg.cls += ' roo-radio-' + this.weight;
41393 cfg.cls += ' roo-radio-set-inline';
41397 ['xs','sm','md','lg'].map(function(size){
41398 if (settings[size]) {
41399 cfg.cls += ' col-' + size + '-' + settings[size];
41407 initEvents : function()
41409 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41410 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41412 if(!this.fieldLabel.length){
41413 this.labelEl.hide();
41416 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41417 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41419 this.indicator = this.indicatorEl();
41421 if(this.indicator){
41422 this.indicator.addClass('invisible');
41425 this.originalValue = this.getValue();
41429 inputEl: function ()
41431 return this.el.select('.roo-radio-set-input', true).first();
41434 getChildContainer : function()
41436 return this.itemsEl;
41439 register : function(item)
41441 this.radioes.push(item);
41445 validate : function()
41447 if(this.getVisibilityEl().hasClass('hidden')){
41453 Roo.each(this.radioes, function(i){
41462 if(this.allowBlank) {
41466 if(this.disabled || valid){
41471 this.markInvalid();
41476 markValid : function()
41478 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41479 this.indicatorEl().removeClass('visible');
41480 this.indicatorEl().addClass('invisible');
41484 if (Roo.bootstrap.version == 3) {
41485 this.el.removeClass([this.invalidClass, this.validClass]);
41486 this.el.addClass(this.validClass);
41488 this.el.removeClass(['is-invalid','is-valid']);
41489 this.el.addClass(['is-valid']);
41491 this.fireEvent('valid', this);
41494 markInvalid : function(msg)
41496 if(this.allowBlank || this.disabled){
41500 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41501 this.indicatorEl().removeClass('invisible');
41502 this.indicatorEl().addClass('visible');
41504 if (Roo.bootstrap.version == 3) {
41505 this.el.removeClass([this.invalidClass, this.validClass]);
41506 this.el.addClass(this.invalidClass);
41508 this.el.removeClass(['is-invalid','is-valid']);
41509 this.el.addClass(['is-invalid']);
41512 this.fireEvent('invalid', this, msg);
41516 setValue : function(v, suppressEvent)
41518 if(this.value === v){
41525 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41528 Roo.each(this.radioes, function(i){
41530 i.el.removeClass('checked');
41533 Roo.each(this.radioes, function(i){
41535 if(i.value === v || i.value.toString() === v.toString()){
41537 i.el.addClass('checked');
41539 if(suppressEvent !== true){
41540 this.fireEvent('check', this, i);
41551 clearInvalid : function(){
41553 if(!this.el || this.preventMark){
41557 this.el.removeClass([this.invalidClass]);
41559 this.fireEvent('valid', this);
41564 Roo.apply(Roo.bootstrap.form.RadioSet, {
41568 register : function(set)
41570 this.groups[set.name] = set;
41573 get: function(name)
41575 if (typeof(this.groups[name]) == 'undefined') {
41579 return this.groups[name] ;
41585 * Ext JS Library 1.1.1
41586 * Copyright(c) 2006-2007, Ext JS, LLC.
41588 * Originally Released Under LGPL - original licence link has changed is not relivant.
41591 * <script type="text/javascript">
41596 * @class Roo.bootstrap.SplitBar
41597 * @extends Roo.util.Observable
41598 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41602 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41603 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41604 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41605 split.minSize = 100;
41606 split.maxSize = 600;
41607 split.animate = true;
41608 split.on('moved', splitterMoved);
41611 * Create a new SplitBar
41612 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
41613 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
41614 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41615 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
41616 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41617 position of the SplitBar).
41619 Roo.bootstrap.SplitBar = function(cfg){
41624 // dragElement : elm
41625 // resizingElement: el,
41627 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41628 // placement : Roo.bootstrap.SplitBar.LEFT ,
41629 // existingProxy ???
41632 this.el = Roo.get(cfg.dragElement, true);
41633 this.el.dom.unselectable = "on";
41635 this.resizingEl = Roo.get(cfg.resizingElement, true);
41639 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41640 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41643 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41646 * The minimum size of the resizing element. (Defaults to 0)
41652 * The maximum size of the resizing element. (Defaults to 2000)
41655 this.maxSize = 2000;
41658 * Whether to animate the transition to the new size
41661 this.animate = false;
41664 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41667 this.useShim = false;
41672 if(!cfg.existingProxy){
41674 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41676 this.proxy = Roo.get(cfg.existingProxy).dom;
41679 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41682 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41685 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41688 this.dragSpecs = {};
41691 * @private The adapter to use to positon and resize elements
41693 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41694 this.adapter.init(this);
41696 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41698 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41699 this.el.addClass("roo-splitbar-h");
41702 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41703 this.el.addClass("roo-splitbar-v");
41709 * Fires when the splitter is moved (alias for {@link #event-moved})
41710 * @param {Roo.bootstrap.SplitBar} this
41711 * @param {Number} newSize the new width or height
41716 * Fires when the splitter is moved
41717 * @param {Roo.bootstrap.SplitBar} this
41718 * @param {Number} newSize the new width or height
41722 * @event beforeresize
41723 * Fires before the splitter is dragged
41724 * @param {Roo.bootstrap.SplitBar} this
41726 "beforeresize" : true,
41728 "beforeapply" : true
41731 Roo.util.Observable.call(this);
41734 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41735 onStartProxyDrag : function(x, y){
41736 this.fireEvent("beforeresize", this);
41738 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
41740 o.enableDisplayMode("block");
41741 // all splitbars share the same overlay
41742 Roo.bootstrap.SplitBar.prototype.overlay = o;
41744 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41745 this.overlay.show();
41746 Roo.get(this.proxy).setDisplayed("block");
41747 var size = this.adapter.getElementSize(this);
41748 this.activeMinSize = this.getMinimumSize();;
41749 this.activeMaxSize = this.getMaximumSize();;
41750 var c1 = size - this.activeMinSize;
41751 var c2 = Math.max(this.activeMaxSize - size, 0);
41752 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41753 this.dd.resetConstraints();
41754 this.dd.setXConstraint(
41755 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
41756 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41758 this.dd.setYConstraint(0, 0);
41760 this.dd.resetConstraints();
41761 this.dd.setXConstraint(0, 0);
41762 this.dd.setYConstraint(
41763 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
41764 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41767 this.dragSpecs.startSize = size;
41768 this.dragSpecs.startPoint = [x, y];
41769 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41773 * @private Called after the drag operation by the DDProxy
41775 onEndProxyDrag : function(e){
41776 Roo.get(this.proxy).setDisplayed(false);
41777 var endPoint = Roo.lib.Event.getXY(e);
41779 this.overlay.hide();
41782 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41783 newSize = this.dragSpecs.startSize +
41784 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41785 endPoint[0] - this.dragSpecs.startPoint[0] :
41786 this.dragSpecs.startPoint[0] - endPoint[0]
41789 newSize = this.dragSpecs.startSize +
41790 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41791 endPoint[1] - this.dragSpecs.startPoint[1] :
41792 this.dragSpecs.startPoint[1] - endPoint[1]
41795 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41796 if(newSize != this.dragSpecs.startSize){
41797 if(this.fireEvent('beforeapply', this, newSize) !== false){
41798 this.adapter.setElementSize(this, newSize);
41799 this.fireEvent("moved", this, newSize);
41800 this.fireEvent("resize", this, newSize);
41806 * Get the adapter this SplitBar uses
41807 * @return The adapter object
41809 getAdapter : function(){
41810 return this.adapter;
41814 * Set the adapter this SplitBar uses
41815 * @param {Object} adapter A SplitBar adapter object
41817 setAdapter : function(adapter){
41818 this.adapter = adapter;
41819 this.adapter.init(this);
41823 * Gets the minimum size for the resizing element
41824 * @return {Number} The minimum size
41826 getMinimumSize : function(){
41827 return this.minSize;
41831 * Sets the minimum size for the resizing element
41832 * @param {Number} minSize The minimum size
41834 setMinimumSize : function(minSize){
41835 this.minSize = minSize;
41839 * Gets the maximum size for the resizing element
41840 * @return {Number} The maximum size
41842 getMaximumSize : function(){
41843 return this.maxSize;
41847 * Sets the maximum size for the resizing element
41848 * @param {Number} maxSize The maximum size
41850 setMaximumSize : function(maxSize){
41851 this.maxSize = maxSize;
41855 * Sets the initialize size for the resizing element
41856 * @param {Number} size The initial size
41858 setCurrentSize : function(size){
41859 var oldAnimate = this.animate;
41860 this.animate = false;
41861 this.adapter.setElementSize(this, size);
41862 this.animate = oldAnimate;
41866 * Destroy this splitbar.
41867 * @param {Boolean} removeEl True to remove the element
41869 destroy : function(removeEl){
41871 this.shim.remove();
41874 this.proxy.parentNode.removeChild(this.proxy);
41882 * @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.
41884 Roo.bootstrap.SplitBar.createProxy = function(dir){
41885 var proxy = new Roo.Element(document.createElement("div"));
41886 proxy.unselectable();
41887 var cls = 'roo-splitbar-proxy';
41888 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41889 document.body.appendChild(proxy.dom);
41894 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41895 * Default Adapter. It assumes the splitter and resizing element are not positioned
41896 * elements and only gets/sets the width of the element. Generally used for table based layouts.
41898 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41901 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41902 // do nothing for now
41903 init : function(s){
41907 * Called before drag operations to get the current size of the resizing element.
41908 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41910 getElementSize : function(s){
41911 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41912 return s.resizingEl.getWidth();
41914 return s.resizingEl.getHeight();
41919 * Called after drag operations to set the size of the resizing element.
41920 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41921 * @param {Number} newSize The new size to set
41922 * @param {Function} onComplete A function to be invoked when resizing is complete
41924 setElementSize : function(s, newSize, onComplete){
41925 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41927 s.resizingEl.setWidth(newSize);
41929 onComplete(s, newSize);
41932 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41937 s.resizingEl.setHeight(newSize);
41939 onComplete(s, newSize);
41942 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41949 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41950 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41951 * Adapter that moves the splitter element to align with the resized sizing element.
41952 * Used with an absolute positioned SplitBar.
41953 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41954 * document.body, make sure you assign an id to the body element.
41956 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41957 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41958 this.container = Roo.get(container);
41961 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41962 init : function(s){
41963 this.basic.init(s);
41966 getElementSize : function(s){
41967 return this.basic.getElementSize(s);
41970 setElementSize : function(s, newSize, onComplete){
41971 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41974 moveSplitter : function(s){
41975 var yes = Roo.bootstrap.SplitBar;
41976 switch(s.placement){
41978 s.el.setX(s.resizingEl.getRight());
41981 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41984 s.el.setY(s.resizingEl.getBottom());
41987 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41994 * Orientation constant - Create a vertical SplitBar
41998 Roo.bootstrap.SplitBar.VERTICAL = 1;
42001 * Orientation constant - Create a horizontal SplitBar
42005 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42008 * Placement constant - The resizing element is to the left of the splitter element
42012 Roo.bootstrap.SplitBar.LEFT = 1;
42015 * Placement constant - The resizing element is to the right of the splitter element
42019 Roo.bootstrap.SplitBar.RIGHT = 2;
42022 * Placement constant - The resizing element is positioned above the splitter element
42026 Roo.bootstrap.SplitBar.TOP = 3;
42029 * Placement constant - The resizing element is positioned under splitter element
42033 Roo.bootstrap.SplitBar.BOTTOM = 4;
42036 * Ext JS Library 1.1.1
42037 * Copyright(c) 2006-2007, Ext JS, LLC.
42039 * Originally Released Under LGPL - original licence link has changed is not relivant.
42042 * <script type="text/javascript">
42046 * @class Roo.bootstrap.layout.Manager
42047 * @extends Roo.bootstrap.Component
42049 * Base class for layout managers.
42051 Roo.bootstrap.layout.Manager = function(config)
42053 this.monitorWindowResize = true; // do this before we apply configuration.
42055 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42061 /** false to disable window resize monitoring @type Boolean */
42067 * Fires when a layout is performed.
42068 * @param {Roo.LayoutManager} this
42072 * @event regionresized
42073 * Fires when the user resizes a region.
42074 * @param {Roo.LayoutRegion} region The resized region
42075 * @param {Number} newSize The new size (width for east/west, height for north/south)
42077 "regionresized" : true,
42079 * @event regioncollapsed
42080 * Fires when a region is collapsed.
42081 * @param {Roo.LayoutRegion} region The collapsed region
42083 "regioncollapsed" : true,
42085 * @event regionexpanded
42086 * Fires when a region is expanded.
42087 * @param {Roo.LayoutRegion} region The expanded region
42089 "regionexpanded" : true
42091 this.updating = false;
42094 this.el = Roo.get(config.el);
42100 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42105 monitorWindowResize : true,
42111 onRender : function(ct, position)
42114 this.el = Roo.get(ct);
42117 //this.fireEvent('render',this);
42121 initEvents: function()
42125 // ie scrollbar fix
42126 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42127 document.body.scroll = "no";
42128 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42129 this.el.position('relative');
42131 this.id = this.el.id;
42132 this.el.addClass("roo-layout-container");
42133 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42134 if(this.el.dom != document.body ) {
42135 this.el.on('resize', this.layout,this);
42136 this.el.on('show', this.layout,this);
42142 * Returns true if this layout is currently being updated
42143 * @return {Boolean}
42145 isUpdating : function(){
42146 return this.updating;
42150 * Suspend the LayoutManager from doing auto-layouts while
42151 * making multiple add or remove calls
42153 beginUpdate : function(){
42154 this.updating = true;
42158 * Restore auto-layouts and optionally disable the manager from performing a layout
42159 * @param {Boolean} noLayout true to disable a layout update
42161 endUpdate : function(noLayout){
42162 this.updating = false;
42168 layout: function(){
42172 onRegionResized : function(region, newSize){
42173 this.fireEvent("regionresized", region, newSize);
42177 onRegionCollapsed : function(region){
42178 this.fireEvent("regioncollapsed", region);
42181 onRegionExpanded : function(region){
42182 this.fireEvent("regionexpanded", region);
42186 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42187 * performs box-model adjustments.
42188 * @return {Object} The size as an object {width: (the width), height: (the height)}
42190 getViewSize : function()
42193 if(this.el.dom != document.body){
42194 size = this.el.getSize();
42196 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42198 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42199 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42204 * Returns the Element this layout is bound to.
42205 * @return {Roo.Element}
42207 getEl : function(){
42212 * Returns the specified region.
42213 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42214 * @return {Roo.LayoutRegion}
42216 getRegion : function(target){
42217 return this.regions[target.toLowerCase()];
42220 onWindowResize : function(){
42221 if(this.monitorWindowResize){
42228 * Ext JS Library 1.1.1
42229 * Copyright(c) 2006-2007, Ext JS, LLC.
42231 * Originally Released Under LGPL - original licence link has changed is not relivant.
42234 * <script type="text/javascript">
42237 * @class Roo.bootstrap.layout.Border
42238 * @extends Roo.bootstrap.layout.Manager
42239 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42240 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42241 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42242 * please see: examples/bootstrap/nested.html<br><br>
42244 <b>The container the layout is rendered into can be either the body element or any other element.
42245 If it is not the body element, the container needs to either be an absolute positioned element,
42246 or you will need to add "position:relative" to the css of the container. You will also need to specify
42247 the container size if it is not the body element.</b>
42250 * Create a new Border
42251 * @param {Object} config Configuration options
42253 Roo.bootstrap.layout.Border = function(config){
42254 config = config || {};
42255 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42259 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42260 if(config[region]){
42261 config[region].region = region;
42262 this.addRegion(config[region]);
42268 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
42270 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42273 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42276 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42279 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42282 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42285 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42291 parent : false, // this might point to a 'nest' or a ???
42294 * Creates and adds a new region if it doesn't already exist.
42295 * @param {String} target The target region key (north, south, east, west or center).
42296 * @param {Object} config The regions config object
42297 * @return {BorderLayoutRegion} The new region
42299 addRegion : function(config)
42301 if(!this.regions[config.region]){
42302 var r = this.factory(config);
42303 this.bindRegion(r);
42305 return this.regions[config.region];
42309 bindRegion : function(r){
42310 this.regions[r.config.region] = r;
42312 r.on("visibilitychange", this.layout, this);
42313 r.on("paneladded", this.layout, this);
42314 r.on("panelremoved", this.layout, this);
42315 r.on("invalidated", this.layout, this);
42316 r.on("resized", this.onRegionResized, this);
42317 r.on("collapsed", this.onRegionCollapsed, this);
42318 r.on("expanded", this.onRegionExpanded, this);
42322 * Performs a layout update.
42324 layout : function()
42326 if(this.updating) {
42330 // render all the rebions if they have not been done alreayd?
42331 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42332 if(this.regions[region] && !this.regions[region].bodyEl){
42333 this.regions[region].onRender(this.el)
42337 var size = this.getViewSize();
42338 var w = size.width;
42339 var h = size.height;
42344 //var x = 0, y = 0;
42346 var rs = this.regions;
42347 var north = rs["north"];
42348 var south = rs["south"];
42349 var west = rs["west"];
42350 var east = rs["east"];
42351 var center = rs["center"];
42352 //if(this.hideOnLayout){ // not supported anymore
42353 //c.el.setStyle("display", "none");
42355 if(north && north.isVisible()){
42356 var b = north.getBox();
42357 var m = north.getMargins();
42358 b.width = w - (m.left+m.right);
42361 centerY = b.height + b.y + m.bottom;
42362 centerH -= centerY;
42363 north.updateBox(this.safeBox(b));
42365 if(south && south.isVisible()){
42366 var b = south.getBox();
42367 var m = south.getMargins();
42368 b.width = w - (m.left+m.right);
42370 var totalHeight = (b.height + m.top + m.bottom);
42371 b.y = h - totalHeight + m.top;
42372 centerH -= totalHeight;
42373 south.updateBox(this.safeBox(b));
42375 if(west && west.isVisible()){
42376 var b = west.getBox();
42377 var m = west.getMargins();
42378 b.height = centerH - (m.top+m.bottom);
42380 b.y = centerY + m.top;
42381 var totalWidth = (b.width + m.left + m.right);
42382 centerX += totalWidth;
42383 centerW -= totalWidth;
42384 west.updateBox(this.safeBox(b));
42386 if(east && east.isVisible()){
42387 var b = east.getBox();
42388 var m = east.getMargins();
42389 b.height = centerH - (m.top+m.bottom);
42390 var totalWidth = (b.width + m.left + m.right);
42391 b.x = w - totalWidth + m.left;
42392 b.y = centerY + m.top;
42393 centerW -= totalWidth;
42394 east.updateBox(this.safeBox(b));
42397 var m = center.getMargins();
42399 x: centerX + m.left,
42400 y: centerY + m.top,
42401 width: centerW - (m.left+m.right),
42402 height: centerH - (m.top+m.bottom)
42404 //if(this.hideOnLayout){
42405 //center.el.setStyle("display", "block");
42407 center.updateBox(this.safeBox(centerBox));
42410 this.fireEvent("layout", this);
42414 safeBox : function(box){
42415 box.width = Math.max(0, box.width);
42416 box.height = Math.max(0, box.height);
42421 * Adds a ContentPanel (or subclass) to this layout.
42422 * @param {String} target The target region key (north, south, east, west or center).
42423 * @param {Roo.ContentPanel} panel The panel to add
42424 * @return {Roo.ContentPanel} The added panel
42426 add : function(target, panel){
42428 target = target.toLowerCase();
42429 return this.regions[target].add(panel);
42433 * Remove a ContentPanel (or subclass) to this layout.
42434 * @param {String} target The target region key (north, south, east, west or center).
42435 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42436 * @return {Roo.ContentPanel} The removed panel
42438 remove : function(target, panel){
42439 target = target.toLowerCase();
42440 return this.regions[target].remove(panel);
42444 * Searches all regions for a panel with the specified id
42445 * @param {String} panelId
42446 * @return {Roo.ContentPanel} The panel or null if it wasn't found
42448 findPanel : function(panelId){
42449 var rs = this.regions;
42450 for(var target in rs){
42451 if(typeof rs[target] != "function"){
42452 var p = rs[target].getPanel(panelId);
42462 * Searches all regions for a panel with the specified id and activates (shows) it.
42463 * @param {String/ContentPanel} panelId The panels id or the panel itself
42464 * @return {Roo.ContentPanel} The shown panel or null
42466 showPanel : function(panelId) {
42467 var rs = this.regions;
42468 for(var target in rs){
42469 var r = rs[target];
42470 if(typeof r != "function"){
42471 if(r.hasPanel(panelId)){
42472 return r.showPanel(panelId);
42480 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42481 * @param {Roo.state.Provider} provider (optional) An alternate state provider
42484 restoreState : function(provider){
42486 provider = Roo.state.Manager;
42488 var sm = new Roo.LayoutStateManager();
42489 sm.init(this, provider);
42495 * Adds a xtype elements to the layout.
42499 xtype : 'ContentPanel',
42506 xtype : 'NestedLayoutPanel',
42512 items : [ ... list of content panels or nested layout panels.. ]
42516 * @param {Object} cfg Xtype definition of item to add.
42518 addxtype : function(cfg)
42520 // basically accepts a pannel...
42521 // can accept a layout region..!?!?
42522 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42525 // theory? children can only be panels??
42527 //if (!cfg.xtype.match(/Panel$/)) {
42532 if (typeof(cfg.region) == 'undefined') {
42533 Roo.log("Failed to add Panel, region was not set");
42537 var region = cfg.region;
42543 xitems = cfg.items;
42548 if ( region == 'center') {
42549 Roo.log("Center: " + cfg.title);
42555 case 'Content': // ContentPanel (el, cfg)
42556 case 'Scroll': // ContentPanel (el, cfg)
42558 cfg.autoCreate = cfg.autoCreate || true;
42559 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42561 // var el = this.el.createChild();
42562 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42565 this.add(region, ret);
42569 case 'TreePanel': // our new panel!
42570 cfg.el = this.el.createChild();
42571 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42572 this.add(region, ret);
42577 // create a new Layout (which is a Border Layout...
42579 var clayout = cfg.layout;
42580 clayout.el = this.el.createChild();
42581 clayout.items = clayout.items || [];
42585 // replace this exitems with the clayout ones..
42586 xitems = clayout.items;
42588 // force background off if it's in center...
42589 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42590 cfg.background = false;
42592 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
42595 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42596 //console.log('adding nested layout panel ' + cfg.toSource());
42597 this.add(region, ret);
42598 nb = {}; /// find first...
42603 // needs grid and region
42605 //var el = this.getRegion(region).el.createChild();
42607 *var el = this.el.createChild();
42608 // create the grid first...
42609 cfg.grid.container = el;
42610 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42613 if (region == 'center' && this.active ) {
42614 cfg.background = false;
42617 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42619 this.add(region, ret);
42621 if (cfg.background) {
42622 // render grid on panel activation (if panel background)
42623 ret.on('activate', function(gp) {
42624 if (!gp.grid.rendered) {
42625 // gp.grid.render(el);
42629 // cfg.grid.render(el);
42635 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42636 // it was the old xcomponent building that caused this before.
42637 // espeically if border is the top element in the tree.
42647 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42649 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42650 this.add(region, ret);
42654 throw "Can not add '" + cfg.xtype + "' to Border";
42660 this.beginUpdate();
42664 Roo.each(xitems, function(i) {
42665 region = nb && i.region ? i.region : false;
42667 var add = ret.addxtype(i);
42670 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42671 if (!i.background) {
42672 abn[region] = nb[region] ;
42679 // make the last non-background panel active..
42680 //if (nb) { Roo.log(abn); }
42683 for(var r in abn) {
42684 region = this.getRegion(r);
42686 // tried using nb[r], but it does not work..
42688 region.showPanel(abn[r]);
42699 factory : function(cfg)
42702 var validRegions = Roo.bootstrap.layout.Border.regions;
42704 var target = cfg.region;
42707 var r = Roo.bootstrap.layout;
42711 return new r.North(cfg);
42713 return new r.South(cfg);
42715 return new r.East(cfg);
42717 return new r.West(cfg);
42719 return new r.Center(cfg);
42721 throw 'Layout region "'+target+'" not supported.';
42728 * Ext JS Library 1.1.1
42729 * Copyright(c) 2006-2007, Ext JS, LLC.
42731 * Originally Released Under LGPL - original licence link has changed is not relivant.
42734 * <script type="text/javascript">
42738 * @class Roo.bootstrap.layout.Basic
42739 * @extends Roo.util.Observable
42740 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42741 * and does not have a titlebar, tabs or any other features. All it does is size and position
42742 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42743 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
42744 * @cfg {string} region the region that it inhabits..
42745 * @cfg {bool} skipConfig skip config?
42749 Roo.bootstrap.layout.Basic = function(config){
42751 this.mgr = config.mgr;
42753 this.position = config.region;
42755 var skipConfig = config.skipConfig;
42759 * @scope Roo.BasicLayoutRegion
42763 * @event beforeremove
42764 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42765 * @param {Roo.LayoutRegion} this
42766 * @param {Roo.ContentPanel} panel The panel
42767 * @param {Object} e The cancel event object
42769 "beforeremove" : true,
42771 * @event invalidated
42772 * Fires when the layout for this region is changed.
42773 * @param {Roo.LayoutRegion} this
42775 "invalidated" : true,
42777 * @event visibilitychange
42778 * Fires when this region is shown or hidden
42779 * @param {Roo.LayoutRegion} this
42780 * @param {Boolean} visibility true or false
42782 "visibilitychange" : true,
42784 * @event paneladded
42785 * Fires when a panel is added.
42786 * @param {Roo.LayoutRegion} this
42787 * @param {Roo.ContentPanel} panel The panel
42789 "paneladded" : true,
42791 * @event panelremoved
42792 * Fires when a panel is removed.
42793 * @param {Roo.LayoutRegion} this
42794 * @param {Roo.ContentPanel} panel The panel
42796 "panelremoved" : true,
42798 * @event beforecollapse
42799 * Fires when this region before collapse.
42800 * @param {Roo.LayoutRegion} this
42802 "beforecollapse" : true,
42805 * Fires when this region is collapsed.
42806 * @param {Roo.LayoutRegion} this
42808 "collapsed" : true,
42811 * Fires when this region is expanded.
42812 * @param {Roo.LayoutRegion} this
42817 * Fires when this region is slid into view.
42818 * @param {Roo.LayoutRegion} this
42820 "slideshow" : true,
42823 * Fires when this region slides out of view.
42824 * @param {Roo.LayoutRegion} this
42826 "slidehide" : true,
42828 * @event panelactivated
42829 * Fires when a panel is activated.
42830 * @param {Roo.LayoutRegion} this
42831 * @param {Roo.ContentPanel} panel The activated panel
42833 "panelactivated" : true,
42836 * Fires when the user resizes this region.
42837 * @param {Roo.LayoutRegion} this
42838 * @param {Number} newSize The new size (width for east/west, height for north/south)
42842 /** A collection of panels in this region. @type Roo.util.MixedCollection */
42843 this.panels = new Roo.util.MixedCollection();
42844 this.panels.getKey = this.getPanelId.createDelegate(this);
42846 this.activePanel = null;
42847 // ensure listeners are added...
42849 if (config.listeners || config.events) {
42850 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42851 listeners : config.listeners || {},
42852 events : config.events || {}
42856 if(skipConfig !== true){
42857 this.applyConfig(config);
42861 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42863 getPanelId : function(p){
42867 applyConfig : function(config){
42868 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42869 this.config = config;
42874 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
42875 * the width, for horizontal (north, south) the height.
42876 * @param {Number} newSize The new width or height
42878 resizeTo : function(newSize){
42879 var el = this.el ? this.el :
42880 (this.activePanel ? this.activePanel.getEl() : null);
42882 switch(this.position){
42885 el.setWidth(newSize);
42886 this.fireEvent("resized", this, newSize);
42890 el.setHeight(newSize);
42891 this.fireEvent("resized", this, newSize);
42897 getBox : function(){
42898 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42901 getMargins : function(){
42902 return this.margins;
42905 updateBox : function(box){
42907 var el = this.activePanel.getEl();
42908 el.dom.style.left = box.x + "px";
42909 el.dom.style.top = box.y + "px";
42910 this.activePanel.setSize(box.width, box.height);
42914 * Returns the container element for this region.
42915 * @return {Roo.Element}
42917 getEl : function(){
42918 return this.activePanel;
42922 * Returns true if this region is currently visible.
42923 * @return {Boolean}
42925 isVisible : function(){
42926 return this.activePanel ? true : false;
42929 setActivePanel : function(panel){
42930 panel = this.getPanel(panel);
42931 if(this.activePanel && this.activePanel != panel){
42932 this.activePanel.setActiveState(false);
42933 this.activePanel.getEl().setLeftTop(-10000,-10000);
42935 this.activePanel = panel;
42936 panel.setActiveState(true);
42938 panel.setSize(this.box.width, this.box.height);
42940 this.fireEvent("panelactivated", this, panel);
42941 this.fireEvent("invalidated");
42945 * Show the specified panel.
42946 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42947 * @return {Roo.ContentPanel} The shown panel or null
42949 showPanel : function(panel){
42950 panel = this.getPanel(panel);
42952 this.setActivePanel(panel);
42958 * Get the active panel for this region.
42959 * @return {Roo.ContentPanel} The active panel or null
42961 getActivePanel : function(){
42962 return this.activePanel;
42966 * Add the passed ContentPanel(s)
42967 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42968 * @return {Roo.ContentPanel} The panel added (if only one was added)
42970 add : function(panel){
42971 if(arguments.length > 1){
42972 for(var i = 0, len = arguments.length; i < len; i++) {
42973 this.add(arguments[i]);
42977 if(this.hasPanel(panel)){
42978 this.showPanel(panel);
42981 var el = panel.getEl();
42982 if(el.dom.parentNode != this.mgr.el.dom){
42983 this.mgr.el.dom.appendChild(el.dom);
42985 if(panel.setRegion){
42986 panel.setRegion(this);
42988 this.panels.add(panel);
42989 el.setStyle("position", "absolute");
42990 if(!panel.background){
42991 this.setActivePanel(panel);
42992 if(this.config.initialSize && this.panels.getCount()==1){
42993 this.resizeTo(this.config.initialSize);
42996 this.fireEvent("paneladded", this, panel);
43001 * Returns true if the panel is in this region.
43002 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43003 * @return {Boolean}
43005 hasPanel : function(panel){
43006 if(typeof panel == "object"){ // must be panel obj
43007 panel = panel.getId();
43009 return this.getPanel(panel) ? true : false;
43013 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43014 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43015 * @param {Boolean} preservePanel Overrides the config preservePanel option
43016 * @return {Roo.ContentPanel} The panel that was removed
43018 remove : function(panel, preservePanel){
43019 panel = this.getPanel(panel);
43024 this.fireEvent("beforeremove", this, panel, e);
43025 if(e.cancel === true){
43028 var panelId = panel.getId();
43029 this.panels.removeKey(panelId);
43034 * Returns the panel specified or null if it's not in this region.
43035 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43036 * @return {Roo.ContentPanel}
43038 getPanel : function(id){
43039 if(typeof id == "object"){ // must be panel obj
43042 return this.panels.get(id);
43046 * Returns this regions position (north/south/east/west/center).
43049 getPosition: function(){
43050 return this.position;
43054 * Ext JS Library 1.1.1
43055 * Copyright(c) 2006-2007, Ext JS, LLC.
43057 * Originally Released Under LGPL - original licence link has changed is not relivant.
43060 * <script type="text/javascript">
43064 * @class Roo.bootstrap.layout.Region
43065 * @extends Roo.bootstrap.layout.Basic
43066 * This class represents a region in a layout manager.
43068 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43069 * @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})
43070 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43071 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43072 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43073 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43074 * @cfg {String} title The title for the region (overrides panel titles)
43075 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43076 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43077 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43078 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43079 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43080 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43081 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43082 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43083 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43084 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43086 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43087 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43088 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43089 * @cfg {Number} width For East/West panels
43090 * @cfg {Number} height For North/South panels
43091 * @cfg {Boolean} split To show the splitter
43092 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43094 * @cfg {string} cls Extra CSS classes to add to region
43096 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43097 * @cfg {string} region the region that it inhabits..
43100 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43101 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43103 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43104 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43105 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43107 Roo.bootstrap.layout.Region = function(config)
43109 this.applyConfig(config);
43111 var mgr = config.mgr;
43112 var pos = config.region;
43113 config.skipConfig = true;
43114 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43117 this.onRender(mgr.el);
43120 this.visible = true;
43121 this.collapsed = false;
43122 this.unrendered_panels = [];
43125 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43127 position: '', // set by wrapper (eg. north/south etc..)
43128 unrendered_panels : null, // unrendered panels.
43130 tabPosition : false,
43132 mgr: false, // points to 'Border'
43135 createBody : function(){
43136 /** This region's body element
43137 * @type Roo.Element */
43138 this.bodyEl = this.el.createChild({
43140 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43144 onRender: function(ctr, pos)
43146 var dh = Roo.DomHelper;
43147 /** This region's container element
43148 * @type Roo.Element */
43149 this.el = dh.append(ctr.dom, {
43151 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43153 /** This region's title element
43154 * @type Roo.Element */
43156 this.titleEl = dh.append(this.el.dom, {
43158 unselectable: "on",
43159 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43161 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43162 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43166 this.titleEl.enableDisplayMode();
43167 /** This region's title text element
43168 * @type HTMLElement */
43169 this.titleTextEl = this.titleEl.dom.firstChild;
43170 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43172 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43173 this.closeBtn.enableDisplayMode();
43174 this.closeBtn.on("click", this.closeClicked, this);
43175 this.closeBtn.hide();
43177 this.createBody(this.config);
43178 if(this.config.hideWhenEmpty){
43180 this.on("paneladded", this.validateVisibility, this);
43181 this.on("panelremoved", this.validateVisibility, this);
43183 if(this.autoScroll){
43184 this.bodyEl.setStyle("overflow", "auto");
43186 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43188 //if(c.titlebar !== false){
43189 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43190 this.titleEl.hide();
43192 this.titleEl.show();
43193 if(this.config.title){
43194 this.titleTextEl.innerHTML = this.config.title;
43198 if(this.config.collapsed){
43199 this.collapse(true);
43201 if(this.config.hidden){
43205 if (this.unrendered_panels && this.unrendered_panels.length) {
43206 for (var i =0;i< this.unrendered_panels.length; i++) {
43207 this.add(this.unrendered_panels[i]);
43209 this.unrendered_panels = null;
43215 applyConfig : function(c)
43218 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43219 var dh = Roo.DomHelper;
43220 if(c.titlebar !== false){
43221 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43222 this.collapseBtn.on("click", this.collapse, this);
43223 this.collapseBtn.enableDisplayMode();
43225 if(c.showPin === true || this.showPin){
43226 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43227 this.stickBtn.enableDisplayMode();
43228 this.stickBtn.on("click", this.expand, this);
43229 this.stickBtn.hide();
43234 /** This region's collapsed element
43235 * @type Roo.Element */
43238 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43239 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43242 if(c.floatable !== false){
43243 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43244 this.collapsedEl.on("click", this.collapseClick, this);
43247 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43248 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43249 id: "message", unselectable: "on", style:{"float":"left"}});
43250 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43252 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43253 this.expandBtn.on("click", this.expand, this);
43257 if(this.collapseBtn){
43258 this.collapseBtn.setVisible(c.collapsible == true);
43261 this.cmargins = c.cmargins || this.cmargins ||
43262 (this.position == "west" || this.position == "east" ?
43263 {top: 0, left: 2, right:2, bottom: 0} :
43264 {top: 2, left: 0, right:0, bottom: 2});
43266 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43269 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43271 this.autoScroll = c.autoScroll || false;
43276 this.duration = c.duration || .30;
43277 this.slideDuration = c.slideDuration || .45;
43282 * Returns true if this region is currently visible.
43283 * @return {Boolean}
43285 isVisible : function(){
43286 return this.visible;
43290 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43291 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
43293 //setCollapsedTitle : function(title){
43294 // title = title || " ";
43295 // if(this.collapsedTitleTextEl){
43296 // this.collapsedTitleTextEl.innerHTML = title;
43300 getBox : function(){
43302 // if(!this.collapsed){
43303 b = this.el.getBox(false, true);
43305 // b = this.collapsedEl.getBox(false, true);
43310 getMargins : function(){
43311 return this.margins;
43312 //return this.collapsed ? this.cmargins : this.margins;
43315 highlight : function(){
43316 this.el.addClass("x-layout-panel-dragover");
43319 unhighlight : function(){
43320 this.el.removeClass("x-layout-panel-dragover");
43323 updateBox : function(box)
43325 if (!this.bodyEl) {
43326 return; // not rendered yet..
43330 if(!this.collapsed){
43331 this.el.dom.style.left = box.x + "px";
43332 this.el.dom.style.top = box.y + "px";
43333 this.updateBody(box.width, box.height);
43335 this.collapsedEl.dom.style.left = box.x + "px";
43336 this.collapsedEl.dom.style.top = box.y + "px";
43337 this.collapsedEl.setSize(box.width, box.height);
43340 this.tabs.autoSizeTabs();
43344 updateBody : function(w, h)
43347 this.el.setWidth(w);
43348 w -= this.el.getBorderWidth("rl");
43349 if(this.config.adjustments){
43350 w += this.config.adjustments[0];
43353 if(h !== null && h > 0){
43354 this.el.setHeight(h);
43355 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43356 h -= this.el.getBorderWidth("tb");
43357 if(this.config.adjustments){
43358 h += this.config.adjustments[1];
43360 this.bodyEl.setHeight(h);
43362 h = this.tabs.syncHeight(h);
43365 if(this.panelSize){
43366 w = w !== null ? w : this.panelSize.width;
43367 h = h !== null ? h : this.panelSize.height;
43369 if(this.activePanel){
43370 var el = this.activePanel.getEl();
43371 w = w !== null ? w : el.getWidth();
43372 h = h !== null ? h : el.getHeight();
43373 this.panelSize = {width: w, height: h};
43374 this.activePanel.setSize(w, h);
43376 if(Roo.isIE && this.tabs){
43377 this.tabs.el.repaint();
43382 * Returns the container element for this region.
43383 * @return {Roo.Element}
43385 getEl : function(){
43390 * Hides this region.
43393 //if(!this.collapsed){
43394 this.el.dom.style.left = "-2000px";
43397 // this.collapsedEl.dom.style.left = "-2000px";
43398 // this.collapsedEl.hide();
43400 this.visible = false;
43401 this.fireEvent("visibilitychange", this, false);
43405 * Shows this region if it was previously hidden.
43408 //if(!this.collapsed){
43411 // this.collapsedEl.show();
43413 this.visible = true;
43414 this.fireEvent("visibilitychange", this, true);
43417 closeClicked : function(){
43418 if(this.activePanel){
43419 this.remove(this.activePanel);
43423 collapseClick : function(e){
43425 e.stopPropagation();
43428 e.stopPropagation();
43434 * Collapses this region.
43435 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43438 collapse : function(skipAnim, skipCheck = false){
43439 if(this.collapsed) {
43443 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43445 this.collapsed = true;
43447 this.split.el.hide();
43449 if(this.config.animate && skipAnim !== true){
43450 this.fireEvent("invalidated", this);
43451 this.animateCollapse();
43453 this.el.setLocation(-20000,-20000);
43455 this.collapsedEl.show();
43456 this.fireEvent("collapsed", this);
43457 this.fireEvent("invalidated", this);
43463 animateCollapse : function(){
43468 * Expands this region if it was previously collapsed.
43469 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43470 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43473 expand : function(e, skipAnim){
43475 e.stopPropagation();
43477 if(!this.collapsed || this.el.hasActiveFx()) {
43481 this.afterSlideIn();
43484 this.collapsed = false;
43485 if(this.config.animate && skipAnim !== true){
43486 this.animateExpand();
43490 this.split.el.show();
43492 this.collapsedEl.setLocation(-2000,-2000);
43493 this.collapsedEl.hide();
43494 this.fireEvent("invalidated", this);
43495 this.fireEvent("expanded", this);
43499 animateExpand : function(){
43503 initTabs : function()
43505 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43507 var ts = new Roo.bootstrap.panel.Tabs({
43508 el: this.bodyEl.dom,
43510 tabPosition: this.tabPosition ? this.tabPosition : 'top',
43511 disableTooltips: this.config.disableTabTips,
43512 toolbar : this.config.toolbar
43515 if(this.config.hideTabs){
43516 ts.stripWrap.setDisplayed(false);
43519 ts.resizeTabs = this.config.resizeTabs === true;
43520 ts.minTabWidth = this.config.minTabWidth || 40;
43521 ts.maxTabWidth = this.config.maxTabWidth || 250;
43522 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43523 ts.monitorResize = false;
43524 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43525 ts.bodyEl.addClass('roo-layout-tabs-body');
43526 this.panels.each(this.initPanelAsTab, this);
43529 initPanelAsTab : function(panel){
43530 var ti = this.tabs.addTab(
43534 this.config.closeOnTab && panel.isClosable(),
43537 if(panel.tabTip !== undefined){
43538 ti.setTooltip(panel.tabTip);
43540 ti.on("activate", function(){
43541 this.setActivePanel(panel);
43544 if(this.config.closeOnTab){
43545 ti.on("beforeclose", function(t, e){
43547 this.remove(panel);
43551 panel.tabItem = ti;
43556 updatePanelTitle : function(panel, title)
43558 if(this.activePanel == panel){
43559 this.updateTitle(title);
43562 var ti = this.tabs.getTab(panel.getEl().id);
43564 if(panel.tabTip !== undefined){
43565 ti.setTooltip(panel.tabTip);
43570 updateTitle : function(title){
43571 if(this.titleTextEl && !this.config.title){
43572 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
43576 setActivePanel : function(panel)
43578 panel = this.getPanel(panel);
43579 if(this.activePanel && this.activePanel != panel){
43580 if(this.activePanel.setActiveState(false) === false){
43584 this.activePanel = panel;
43585 panel.setActiveState(true);
43586 if(this.panelSize){
43587 panel.setSize(this.panelSize.width, this.panelSize.height);
43590 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43592 this.updateTitle(panel.getTitle());
43594 this.fireEvent("invalidated", this);
43596 this.fireEvent("panelactivated", this, panel);
43600 * Shows the specified panel.
43601 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43602 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43604 showPanel : function(panel)
43606 panel = this.getPanel(panel);
43609 var tab = this.tabs.getTab(panel.getEl().id);
43610 if(tab.isHidden()){
43611 this.tabs.unhideTab(tab.id);
43615 this.setActivePanel(panel);
43622 * Get the active panel for this region.
43623 * @return {Roo.ContentPanel} The active panel or null
43625 getActivePanel : function(){
43626 return this.activePanel;
43629 validateVisibility : function(){
43630 if(this.panels.getCount() < 1){
43631 this.updateTitle(" ");
43632 this.closeBtn.hide();
43635 if(!this.isVisible()){
43642 * Adds the passed ContentPanel(s) to this region.
43643 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43644 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43646 add : function(panel)
43648 if(arguments.length > 1){
43649 for(var i = 0, len = arguments.length; i < len; i++) {
43650 this.add(arguments[i]);
43655 // if we have not been rendered yet, then we can not really do much of this..
43656 if (!this.bodyEl) {
43657 this.unrendered_panels.push(panel);
43664 if(this.hasPanel(panel)){
43665 this.showPanel(panel);
43668 panel.setRegion(this);
43669 this.panels.add(panel);
43670 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43671 // sinle panel - no tab...?? would it not be better to render it with the tabs,
43672 // and hide them... ???
43673 this.bodyEl.dom.appendChild(panel.getEl().dom);
43674 if(panel.background !== true){
43675 this.setActivePanel(panel);
43677 this.fireEvent("paneladded", this, panel);
43684 this.initPanelAsTab(panel);
43688 if(panel.background !== true){
43689 this.tabs.activate(panel.getEl().id);
43691 this.fireEvent("paneladded", this, panel);
43696 * Hides the tab for the specified panel.
43697 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43699 hidePanel : function(panel){
43700 if(this.tabs && (panel = this.getPanel(panel))){
43701 this.tabs.hideTab(panel.getEl().id);
43706 * Unhides the tab for a previously hidden panel.
43707 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43709 unhidePanel : function(panel){
43710 if(this.tabs && (panel = this.getPanel(panel))){
43711 this.tabs.unhideTab(panel.getEl().id);
43715 clearPanels : function(){
43716 while(this.panels.getCount() > 0){
43717 this.remove(this.panels.first());
43722 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43723 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43724 * @param {Boolean} preservePanel Overrides the config preservePanel option
43725 * @return {Roo.ContentPanel} The panel that was removed
43727 remove : function(panel, preservePanel)
43729 panel = this.getPanel(panel);
43734 this.fireEvent("beforeremove", this, panel, e);
43735 if(e.cancel === true){
43738 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43739 var panelId = panel.getId();
43740 this.panels.removeKey(panelId);
43742 document.body.appendChild(panel.getEl().dom);
43745 this.tabs.removeTab(panel.getEl().id);
43746 }else if (!preservePanel){
43747 this.bodyEl.dom.removeChild(panel.getEl().dom);
43749 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43750 var p = this.panels.first();
43751 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43752 tempEl.appendChild(p.getEl().dom);
43753 this.bodyEl.update("");
43754 this.bodyEl.dom.appendChild(p.getEl().dom);
43756 this.updateTitle(p.getTitle());
43758 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43759 this.setActivePanel(p);
43761 panel.setRegion(null);
43762 if(this.activePanel == panel){
43763 this.activePanel = null;
43765 if(this.config.autoDestroy !== false && preservePanel !== true){
43766 try{panel.destroy();}catch(e){}
43768 this.fireEvent("panelremoved", this, panel);
43773 * Returns the TabPanel component used by this region
43774 * @return {Roo.TabPanel}
43776 getTabs : function(){
43780 createTool : function(parentEl, className){
43781 var btn = Roo.DomHelper.append(parentEl, {
43783 cls: "x-layout-tools-button",
43786 cls: "roo-layout-tools-button-inner " + className,
43790 btn.addClassOnOver("roo-layout-tools-button-over");
43795 * Ext JS Library 1.1.1
43796 * Copyright(c) 2006-2007, Ext JS, LLC.
43798 * Originally Released Under LGPL - original licence link has changed is not relivant.
43801 * <script type="text/javascript">
43807 * @class Roo.SplitLayoutRegion
43808 * @extends Roo.LayoutRegion
43809 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43811 Roo.bootstrap.layout.Split = function(config){
43812 this.cursor = config.cursor;
43813 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43816 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43818 splitTip : "Drag to resize.",
43819 collapsibleSplitTip : "Drag to resize. Double click to hide.",
43820 useSplitTips : false,
43822 applyConfig : function(config){
43823 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43826 onRender : function(ctr,pos) {
43828 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43829 if(!this.config.split){
43834 var splitEl = Roo.DomHelper.append(ctr.dom, {
43836 id: this.el.id + "-split",
43837 cls: "roo-layout-split roo-layout-split-"+this.position,
43840 /** The SplitBar for this region
43841 * @type Roo.SplitBar */
43842 // does not exist yet...
43843 Roo.log([this.position, this.orientation]);
43845 this.split = new Roo.bootstrap.SplitBar({
43846 dragElement : splitEl,
43847 resizingElement: this.el,
43848 orientation : this.orientation
43851 this.split.on("moved", this.onSplitMove, this);
43852 this.split.useShim = this.config.useShim === true;
43853 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43854 if(this.useSplitTips){
43855 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43857 //if(config.collapsible){
43858 // this.split.el.on("dblclick", this.collapse, this);
43861 if(typeof this.config.minSize != "undefined"){
43862 this.split.minSize = this.config.minSize;
43864 if(typeof this.config.maxSize != "undefined"){
43865 this.split.maxSize = this.config.maxSize;
43867 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43868 this.hideSplitter();
43873 getHMaxSize : function(){
43874 var cmax = this.config.maxSize || 10000;
43875 var center = this.mgr.getRegion("center");
43876 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43879 getVMaxSize : function(){
43880 var cmax = this.config.maxSize || 10000;
43881 var center = this.mgr.getRegion("center");
43882 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43885 onSplitMove : function(split, newSize){
43886 this.fireEvent("resized", this, newSize);
43890 * Returns the {@link Roo.SplitBar} for this region.
43891 * @return {Roo.SplitBar}
43893 getSplitBar : function(){
43898 this.hideSplitter();
43899 Roo.bootstrap.layout.Split.superclass.hide.call(this);
43902 hideSplitter : function(){
43904 this.split.el.setLocation(-2000,-2000);
43905 this.split.el.hide();
43911 this.split.el.show();
43913 Roo.bootstrap.layout.Split.superclass.show.call(this);
43916 beforeSlide: function(){
43917 if(Roo.isGecko){// firefox overflow auto bug workaround
43918 this.bodyEl.clip();
43920 this.tabs.bodyEl.clip();
43922 if(this.activePanel){
43923 this.activePanel.getEl().clip();
43925 if(this.activePanel.beforeSlide){
43926 this.activePanel.beforeSlide();
43932 afterSlide : function(){
43933 if(Roo.isGecko){// firefox overflow auto bug workaround
43934 this.bodyEl.unclip();
43936 this.tabs.bodyEl.unclip();
43938 if(this.activePanel){
43939 this.activePanel.getEl().unclip();
43940 if(this.activePanel.afterSlide){
43941 this.activePanel.afterSlide();
43947 initAutoHide : function(){
43948 if(this.autoHide !== false){
43949 if(!this.autoHideHd){
43950 var st = new Roo.util.DelayedTask(this.slideIn, this);
43951 this.autoHideHd = {
43952 "mouseout": function(e){
43953 if(!e.within(this.el, true)){
43957 "mouseover" : function(e){
43963 this.el.on(this.autoHideHd);
43967 clearAutoHide : function(){
43968 if(this.autoHide !== false){
43969 this.el.un("mouseout", this.autoHideHd.mouseout);
43970 this.el.un("mouseover", this.autoHideHd.mouseover);
43974 clearMonitor : function(){
43975 Roo.get(document).un("click", this.slideInIf, this);
43978 // these names are backwards but not changed for compat
43979 slideOut : function(){
43980 if(this.isSlid || this.el.hasActiveFx()){
43983 this.isSlid = true;
43984 if(this.collapseBtn){
43985 this.collapseBtn.hide();
43987 this.closeBtnState = this.closeBtn.getStyle('display');
43988 this.closeBtn.hide();
43990 this.stickBtn.show();
43993 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43994 this.beforeSlide();
43995 this.el.setStyle("z-index", 10001);
43996 this.el.slideIn(this.getSlideAnchor(), {
43997 callback: function(){
43999 this.initAutoHide();
44000 Roo.get(document).on("click", this.slideInIf, this);
44001 this.fireEvent("slideshow", this);
44008 afterSlideIn : function(){
44009 this.clearAutoHide();
44010 this.isSlid = false;
44011 this.clearMonitor();
44012 this.el.setStyle("z-index", "");
44013 if(this.collapseBtn){
44014 this.collapseBtn.show();
44016 this.closeBtn.setStyle('display', this.closeBtnState);
44018 this.stickBtn.hide();
44020 this.fireEvent("slidehide", this);
44023 slideIn : function(cb){
44024 if(!this.isSlid || this.el.hasActiveFx()){
44028 this.isSlid = false;
44029 this.beforeSlide();
44030 this.el.slideOut(this.getSlideAnchor(), {
44031 callback: function(){
44032 this.el.setLeftTop(-10000, -10000);
44034 this.afterSlideIn();
44042 slideInIf : function(e){
44043 if(!e.within(this.el)){
44048 animateCollapse : function(){
44049 this.beforeSlide();
44050 this.el.setStyle("z-index", 20000);
44051 var anchor = this.getSlideAnchor();
44052 this.el.slideOut(anchor, {
44053 callback : function(){
44054 this.el.setStyle("z-index", "");
44055 this.collapsedEl.slideIn(anchor, {duration:.3});
44057 this.el.setLocation(-10000,-10000);
44059 this.fireEvent("collapsed", this);
44066 animateExpand : function(){
44067 this.beforeSlide();
44068 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44069 this.el.setStyle("z-index", 20000);
44070 this.collapsedEl.hide({
44073 this.el.slideIn(this.getSlideAnchor(), {
44074 callback : function(){
44075 this.el.setStyle("z-index", "");
44078 this.split.el.show();
44080 this.fireEvent("invalidated", this);
44081 this.fireEvent("expanded", this);
44109 getAnchor : function(){
44110 return this.anchors[this.position];
44113 getCollapseAnchor : function(){
44114 return this.canchors[this.position];
44117 getSlideAnchor : function(){
44118 return this.sanchors[this.position];
44121 getAlignAdj : function(){
44122 var cm = this.cmargins;
44123 switch(this.position){
44139 getExpandAdj : function(){
44140 var c = this.collapsedEl, cm = this.cmargins;
44141 switch(this.position){
44143 return [-(cm.right+c.getWidth()+cm.left), 0];
44146 return [cm.right+c.getWidth()+cm.left, 0];
44149 return [0, -(cm.top+cm.bottom+c.getHeight())];
44152 return [0, cm.top+cm.bottom+c.getHeight()];
44158 * Ext JS Library 1.1.1
44159 * Copyright(c) 2006-2007, Ext JS, LLC.
44161 * Originally Released Under LGPL - original licence link has changed is not relivant.
44164 * <script type="text/javascript">
44167 * These classes are private internal classes
44169 Roo.bootstrap.layout.Center = function(config){
44170 config.region = "center";
44171 Roo.bootstrap.layout.Region.call(this, config);
44172 this.visible = true;
44173 this.minWidth = config.minWidth || 20;
44174 this.minHeight = config.minHeight || 20;
44177 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44179 // center panel can't be hidden
44183 // center panel can't be hidden
44186 getMinWidth: function(){
44187 return this.minWidth;
44190 getMinHeight: function(){
44191 return this.minHeight;
44205 Roo.bootstrap.layout.North = function(config)
44207 config.region = 'north';
44208 config.cursor = 'n-resize';
44210 Roo.bootstrap.layout.Split.call(this, config);
44214 this.split.placement = Roo.bootstrap.SplitBar.TOP;
44215 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44216 this.split.el.addClass("roo-layout-split-v");
44218 //var size = config.initialSize || config.height;
44219 //if(this.el && typeof size != "undefined"){
44220 // this.el.setHeight(size);
44223 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44225 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44228 onRender : function(ctr, pos)
44230 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44231 var size = this.config.initialSize || this.config.height;
44232 if(this.el && typeof size != "undefined"){
44233 this.el.setHeight(size);
44238 getBox : function(){
44239 if(this.collapsed){
44240 return this.collapsedEl.getBox();
44242 var box = this.el.getBox();
44244 box.height += this.split.el.getHeight();
44249 updateBox : function(box){
44250 if(this.split && !this.collapsed){
44251 box.height -= this.split.el.getHeight();
44252 this.split.el.setLeft(box.x);
44253 this.split.el.setTop(box.y+box.height);
44254 this.split.el.setWidth(box.width);
44256 if(this.collapsed){
44257 this.updateBody(box.width, null);
44259 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44267 Roo.bootstrap.layout.South = function(config){
44268 config.region = 'south';
44269 config.cursor = 's-resize';
44270 Roo.bootstrap.layout.Split.call(this, config);
44272 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44273 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44274 this.split.el.addClass("roo-layout-split-v");
44279 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44280 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44282 onRender : function(ctr, pos)
44284 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44285 var size = this.config.initialSize || this.config.height;
44286 if(this.el && typeof size != "undefined"){
44287 this.el.setHeight(size);
44292 getBox : function(){
44293 if(this.collapsed){
44294 return this.collapsedEl.getBox();
44296 var box = this.el.getBox();
44298 var sh = this.split.el.getHeight();
44305 updateBox : function(box){
44306 if(this.split && !this.collapsed){
44307 var sh = this.split.el.getHeight();
44310 this.split.el.setLeft(box.x);
44311 this.split.el.setTop(box.y-sh);
44312 this.split.el.setWidth(box.width);
44314 if(this.collapsed){
44315 this.updateBody(box.width, null);
44317 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44321 Roo.bootstrap.layout.East = function(config){
44322 config.region = "east";
44323 config.cursor = "e-resize";
44324 Roo.bootstrap.layout.Split.call(this, config);
44326 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44327 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44328 this.split.el.addClass("roo-layout-split-h");
44332 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44333 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44335 onRender : function(ctr, pos)
44337 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44338 var size = this.config.initialSize || this.config.width;
44339 if(this.el && typeof size != "undefined"){
44340 this.el.setWidth(size);
44345 getBox : function(){
44346 if(this.collapsed){
44347 return this.collapsedEl.getBox();
44349 var box = this.el.getBox();
44351 var sw = this.split.el.getWidth();
44358 updateBox : function(box){
44359 if(this.split && !this.collapsed){
44360 var sw = this.split.el.getWidth();
44362 this.split.el.setLeft(box.x);
44363 this.split.el.setTop(box.y);
44364 this.split.el.setHeight(box.height);
44367 if(this.collapsed){
44368 this.updateBody(null, box.height);
44370 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44374 Roo.bootstrap.layout.West = function(config){
44375 config.region = "west";
44376 config.cursor = "w-resize";
44378 Roo.bootstrap.layout.Split.call(this, config);
44380 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44381 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44382 this.split.el.addClass("roo-layout-split-h");
44386 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44387 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44389 onRender: function(ctr, pos)
44391 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44392 var size = this.config.initialSize || this.config.width;
44393 if(typeof size != "undefined"){
44394 this.el.setWidth(size);
44398 getBox : function(){
44399 if(this.collapsed){
44400 return this.collapsedEl.getBox();
44402 var box = this.el.getBox();
44403 if (box.width == 0) {
44404 box.width = this.config.width; // kludge?
44407 box.width += this.split.el.getWidth();
44412 updateBox : function(box){
44413 if(this.split && !this.collapsed){
44414 var sw = this.split.el.getWidth();
44416 this.split.el.setLeft(box.x+box.width);
44417 this.split.el.setTop(box.y);
44418 this.split.el.setHeight(box.height);
44420 if(this.collapsed){
44421 this.updateBody(null, box.height);
44423 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44427 * Ext JS Library 1.1.1
44428 * Copyright(c) 2006-2007, Ext JS, LLC.
44430 * Originally Released Under LGPL - original licence link has changed is not relivant.
44433 * <script type="text/javascript">
44436 * @class Roo.bootstrap.paenl.Content
44437 * @extends Roo.util.Observable
44438 * @children Roo.bootstrap.Component
44439 * @parent builder Roo.bootstrap.layout.Border
44440 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44441 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
44442 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
44443 * @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
44444 * @cfg {Boolean} closable True if the panel can be closed/removed
44445 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44446 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44447 * @cfg {Toolbar} toolbar A toolbar for this panel
44448 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44449 * @cfg {String} title The title for this panel
44450 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44451 * @cfg {String} url Calls {@link #setUrl} with this value
44452 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44453 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44454 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44455 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
44456 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
44457 * @cfg {Boolean} badges render the badges
44458 * @cfg {String} cls extra classes to use
44459 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44462 * Create a new ContentPanel.
44463 * @param {String/Object} config A string to set only the title or a config object
44466 Roo.bootstrap.panel.Content = function( config){
44468 this.tpl = config.tpl || false;
44470 var el = config.el;
44471 var content = config.content;
44473 if(config.autoCreate){ // xtype is available if this is called from factory
44476 this.el = Roo.get(el);
44477 if(!this.el && config && config.autoCreate){
44478 if(typeof config.autoCreate == "object"){
44479 if(!config.autoCreate.id){
44480 config.autoCreate.id = config.id||el;
44482 this.el = Roo.DomHelper.append(document.body,
44483 config.autoCreate, true);
44487 cls: (config.cls || '') +
44488 (config.background ? ' bg-' + config.background : '') +
44489 " roo-layout-inactive-content",
44492 if (config.iframe) {
44496 style : 'border: 0px',
44497 src : 'about:blank'
44503 elcfg.html = config.html;
44507 this.el = Roo.DomHelper.append(document.body, elcfg , true);
44508 if (config.iframe) {
44509 this.iframeEl = this.el.select('iframe',true).first();
44514 this.closable = false;
44515 this.loaded = false;
44516 this.active = false;
44519 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44521 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44523 this.wrapEl = this.el; //this.el.wrap();
44525 if (config.toolbar.items) {
44526 ti = config.toolbar.items ;
44527 delete config.toolbar.items ;
44531 this.toolbar.render(this.wrapEl, 'before');
44532 for(var i =0;i < ti.length;i++) {
44533 // Roo.log(['add child', items[i]]);
44534 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44536 this.toolbar.items = nitems;
44537 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44538 delete config.toolbar;
44542 // xtype created footer. - not sure if will work as we normally have to render first..
44543 if (this.footer && !this.footer.el && this.footer.xtype) {
44544 if (!this.wrapEl) {
44545 this.wrapEl = this.el.wrap();
44548 this.footer.container = this.wrapEl.createChild();
44550 this.footer = Roo.factory(this.footer, Roo);
44555 if(typeof config == "string"){
44556 this.title = config;
44558 Roo.apply(this, config);
44562 this.resizeEl = Roo.get(this.resizeEl, true);
44564 this.resizeEl = this.el;
44566 // handle view.xtype
44574 * Fires when this panel is activated.
44575 * @param {Roo.ContentPanel} this
44579 * @event deactivate
44580 * Fires when this panel is activated.
44581 * @param {Roo.ContentPanel} this
44583 "deactivate" : true,
44587 * Fires when this panel is resized if fitToFrame is true.
44588 * @param {Roo.ContentPanel} this
44589 * @param {Number} width The width after any component adjustments
44590 * @param {Number} height The height after any component adjustments
44596 * Fires when this tab is created
44597 * @param {Roo.ContentPanel} this
44603 * Fires when this content is scrolled
44604 * @param {Roo.ContentPanel} this
44605 * @param {Event} scrollEvent
44616 if(this.autoScroll && !this.iframe){
44617 this.resizeEl.setStyle("overflow", "auto");
44618 this.resizeEl.on('scroll', this.onScroll, this);
44620 // fix randome scrolling
44621 //this.el.on('scroll', function() {
44622 // Roo.log('fix random scolling');
44623 // this.scrollTo('top',0);
44626 content = content || this.content;
44628 this.setContent(content);
44630 if(config && config.url){
44631 this.setUrl(this.url, this.params, this.loadOnce);
44636 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44638 if (this.view && typeof(this.view.xtype) != 'undefined') {
44639 this.view.el = this.el.appendChild(document.createElement("div"));
44640 this.view = Roo.factory(this.view);
44641 this.view.render && this.view.render(false, '');
44645 this.fireEvent('render', this);
44648 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44658 /* Resize Element - use this to work out scroll etc. */
44661 setRegion : function(region){
44662 this.region = region;
44663 this.setActiveClass(region && !this.background);
44667 setActiveClass: function(state)
44670 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44671 this.el.setStyle('position','relative');
44673 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44674 this.el.setStyle('position', 'absolute');
44679 * Returns the toolbar for this Panel if one was configured.
44680 * @return {Roo.Toolbar}
44682 getToolbar : function(){
44683 return this.toolbar;
44686 setActiveState : function(active)
44688 this.active = active;
44689 this.setActiveClass(active);
44691 if(this.fireEvent("deactivate", this) === false){
44696 this.fireEvent("activate", this);
44700 * Updates this panel's element (not for iframe)
44701 * @param {String} content The new content
44702 * @param {Boolean} loadScripts (optional) true to look for and process scripts
44704 setContent : function(content, loadScripts){
44709 this.el.update(content, loadScripts);
44712 ignoreResize : function(w, h)
44714 //return false; // always resize?
44715 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44718 this.lastSize = {width: w, height: h};
44723 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44724 * @return {Roo.UpdateManager} The UpdateManager
44726 getUpdateManager : function(){
44730 return this.el.getUpdateManager();
44733 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44734 * Does not work with IFRAME contents
44735 * @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:
44738 url: "your-url.php",
44739 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44740 callback: yourFunction,
44741 scope: yourObject, //(optional scope)
44744 text: "Loading...",
44750 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44751 * 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.
44752 * @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}
44753 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44754 * @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.
44755 * @return {Roo.ContentPanel} this
44763 var um = this.el.getUpdateManager();
44764 um.update.apply(um, arguments);
44770 * 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.
44771 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44772 * @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)
44773 * @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)
44774 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44776 setUrl : function(url, params, loadOnce){
44778 this.iframeEl.dom.src = url;
44782 if(this.refreshDelegate){
44783 this.removeListener("activate", this.refreshDelegate);
44785 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44786 this.on("activate", this.refreshDelegate);
44787 return this.el.getUpdateManager();
44790 _handleRefresh : function(url, params, loadOnce){
44791 if(!loadOnce || !this.loaded){
44792 var updater = this.el.getUpdateManager();
44793 updater.update(url, params, this._setLoaded.createDelegate(this));
44797 _setLoaded : function(){
44798 this.loaded = true;
44802 * Returns this panel's id
44805 getId : function(){
44810 * Returns this panel's element - used by regiosn to add.
44811 * @return {Roo.Element}
44813 getEl : function(){
44814 return this.wrapEl || this.el;
44819 adjustForComponents : function(width, height)
44821 //Roo.log('adjustForComponents ');
44822 if(this.resizeEl != this.el){
44823 width -= this.el.getFrameWidth('lr');
44824 height -= this.el.getFrameWidth('tb');
44827 var te = this.toolbar.getEl();
44828 te.setWidth(width);
44829 height -= te.getHeight();
44832 var te = this.footer.getEl();
44833 te.setWidth(width);
44834 height -= te.getHeight();
44838 if(this.adjustments){
44839 width += this.adjustments[0];
44840 height += this.adjustments[1];
44842 return {"width": width, "height": height};
44845 setSize : function(width, height){
44846 if(this.fitToFrame && !this.ignoreResize(width, height)){
44847 if(this.fitContainer && this.resizeEl != this.el){
44848 this.el.setSize(width, height);
44850 var size = this.adjustForComponents(width, height);
44852 this.iframeEl.setSize(width,height);
44855 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44856 this.fireEvent('resize', this, size.width, size.height);
44863 * Returns this panel's title
44866 getTitle : function(){
44868 if (typeof(this.title) != 'object') {
44873 for (var k in this.title) {
44874 if (!this.title.hasOwnProperty(k)) {
44878 if (k.indexOf('-') >= 0) {
44879 var s = k.split('-');
44880 for (var i = 0; i<s.length; i++) {
44881 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44884 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44891 * Set this panel's title
44892 * @param {String} title
44894 setTitle : function(title){
44895 this.title = title;
44897 this.region.updatePanelTitle(this, title);
44902 * Returns true is this panel was configured to be closable
44903 * @return {Boolean}
44905 isClosable : function(){
44906 return this.closable;
44909 beforeSlide : function(){
44911 this.resizeEl.clip();
44914 afterSlide : function(){
44916 this.resizeEl.unclip();
44920 * Force a content refresh from the URL specified in the {@link #setUrl} method.
44921 * Will fail silently if the {@link #setUrl} method has not been called.
44922 * This does not activate the panel, just updates its content.
44924 refresh : function(){
44925 if(this.refreshDelegate){
44926 this.loaded = false;
44927 this.refreshDelegate();
44932 * Destroys this panel
44934 destroy : function(){
44935 this.el.removeAllListeners();
44936 var tempEl = document.createElement("span");
44937 tempEl.appendChild(this.el.dom);
44938 tempEl.innerHTML = "";
44944 * form - if the content panel contains a form - this is a reference to it.
44945 * @type {Roo.form.Form}
44949 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44950 * This contains a reference to it.
44956 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44966 * @param {Object} cfg Xtype definition of item to add.
44970 getChildContainer: function () {
44971 return this.getEl();
44975 onScroll : function(e)
44977 this.fireEvent('scroll', this, e);
44982 var ret = new Roo.factory(cfg);
44987 if (cfg.xtype.match(/^Form$/)) {
44990 //if (this.footer) {
44991 // el = this.footer.container.insertSibling(false, 'before');
44993 el = this.el.createChild();
44996 this.form = new Roo.form.Form(cfg);
44999 if ( this.form.allItems.length) {
45000 this.form.render(el.dom);
45004 // should only have one of theses..
45005 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45006 // views.. should not be just added - used named prop 'view''
45008 cfg.el = this.el.appendChild(document.createElement("div"));
45011 var ret = new Roo.factory(cfg);
45013 ret.render && ret.render(false, ''); // render blank..
45023 * @class Roo.bootstrap.panel.Grid
45024 * @extends Roo.bootstrap.panel.Content
45026 * Create a new GridPanel.
45027 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45028 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45029 * @param {Object} config A the config object
45035 Roo.bootstrap.panel.Grid = function(config)
45039 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45040 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45042 config.el = this.wrapper;
45043 //this.el = this.wrapper;
45045 if (config.container) {
45046 // ctor'ed from a Border/panel.grid
45049 this.wrapper.setStyle("overflow", "hidden");
45050 this.wrapper.addClass('roo-grid-container');
45055 if(config.toolbar){
45056 var tool_el = this.wrapper.createChild();
45057 this.toolbar = Roo.factory(config.toolbar);
45059 if (config.toolbar.items) {
45060 ti = config.toolbar.items ;
45061 delete config.toolbar.items ;
45065 this.toolbar.render(tool_el);
45066 for(var i =0;i < ti.length;i++) {
45067 // Roo.log(['add child', items[i]]);
45068 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45070 this.toolbar.items = nitems;
45072 delete config.toolbar;
45075 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45076 config.grid.scrollBody = true;;
45077 config.grid.monitorWindowResize = false; // turn off autosizing
45078 config.grid.autoHeight = false;
45079 config.grid.autoWidth = false;
45081 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45083 if (config.background) {
45084 // render grid on panel activation (if panel background)
45085 this.on('activate', function(gp) {
45086 if (!gp.grid.rendered) {
45087 gp.grid.render(this.wrapper);
45088 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45093 this.grid.render(this.wrapper);
45094 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45097 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45098 // ??? needed ??? config.el = this.wrapper;
45103 // xtype created footer. - not sure if will work as we normally have to render first..
45104 if (this.footer && !this.footer.el && this.footer.xtype) {
45106 var ctr = this.grid.getView().getFooterPanel(true);
45107 this.footer.dataSource = this.grid.dataSource;
45108 this.footer = Roo.factory(this.footer, Roo);
45109 this.footer.render(ctr);
45119 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45122 getId : function(){
45123 return this.grid.id;
45127 * Returns the grid for this panel
45128 * @return {Roo.bootstrap.Table}
45130 getGrid : function(){
45134 setSize : function(width, height)
45137 //if(!this.ignoreResize(width, height)){
45138 var grid = this.grid;
45139 var size = this.adjustForComponents(width, height);
45140 // tfoot is not a footer?
45143 var gridel = grid.getGridEl();
45144 gridel.setSize(size.width, size.height);
45146 var tbd = grid.getGridEl().select('tbody', true).first();
45147 var thd = grid.getGridEl().select('thead',true).first();
45148 var tbf= grid.getGridEl().select('tfoot', true).first();
45151 size.height -= tbf.getHeight();
45154 size.height -= thd.getHeight();
45157 tbd.setSize(size.width, size.height );
45158 // this is for the account management tab -seems to work there.
45159 var thd = grid.getGridEl().select('thead',true).first();
45161 // tbd.setSize(size.width, size.height - thd.getHeight());
45171 beforeSlide : function(){
45172 this.grid.getView().scroller.clip();
45175 afterSlide : function(){
45176 this.grid.getView().scroller.unclip();
45179 destroy : function(){
45180 this.grid.destroy();
45182 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45187 * @class Roo.bootstrap.panel.Nest
45188 * @extends Roo.bootstrap.panel.Content
45190 * Create a new Panel, that can contain a layout.Border.
45193 * @param {String/Object} config A string to set only the title or a config object
45195 Roo.bootstrap.panel.Nest = function(config)
45197 // construct with only one argument..
45198 /* FIXME - implement nicer consturctors
45199 if (layout.layout) {
45201 layout = config.layout;
45202 delete config.layout;
45204 if (layout.xtype && !layout.getEl) {
45205 // then layout needs constructing..
45206 layout = Roo.factory(layout, Roo);
45210 config.el = config.layout.getEl();
45212 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45214 config.layout.monitorWindowResize = false; // turn off autosizing
45215 this.layout = config.layout;
45216 this.layout.getEl().addClass("roo-layout-nested-layout");
45217 this.layout.parent = this;
45224 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45226 * @cfg {Roo.BorderLayout} layout The layout for this panel
45230 setSize : function(width, height){
45231 if(!this.ignoreResize(width, height)){
45232 var size = this.adjustForComponents(width, height);
45233 var el = this.layout.getEl();
45234 if (size.height < 1) {
45235 el.setWidth(size.width);
45237 el.setSize(size.width, size.height);
45239 var touch = el.dom.offsetWidth;
45240 this.layout.layout();
45241 // ie requires a double layout on the first pass
45242 if(Roo.isIE && !this.initialized){
45243 this.initialized = true;
45244 this.layout.layout();
45249 // activate all subpanels if not currently active..
45251 setActiveState : function(active){
45252 this.active = active;
45253 this.setActiveClass(active);
45256 this.fireEvent("deactivate", this);
45260 this.fireEvent("activate", this);
45261 // not sure if this should happen before or after..
45262 if (!this.layout) {
45263 return; // should not happen..
45266 for (var r in this.layout.regions) {
45267 reg = this.layout.getRegion(r);
45268 if (reg.getActivePanel()) {
45269 //reg.showPanel(reg.getActivePanel()); // force it to activate..
45270 reg.setActivePanel(reg.getActivePanel());
45273 if (!reg.panels.length) {
45276 reg.showPanel(reg.getPanel(0));
45285 * Returns the nested BorderLayout for this panel
45286 * @return {Roo.BorderLayout}
45288 getLayout : function(){
45289 return this.layout;
45293 * Adds a xtype elements to the layout of the nested panel
45297 xtype : 'ContentPanel',
45304 xtype : 'NestedLayoutPanel',
45310 items : [ ... list of content panels or nested layout panels.. ]
45314 * @param {Object} cfg Xtype definition of item to add.
45316 addxtype : function(cfg) {
45317 return this.layout.addxtype(cfg);
45322 * Ext JS Library 1.1.1
45323 * Copyright(c) 2006-2007, Ext JS, LLC.
45325 * Originally Released Under LGPL - original licence link has changed is not relivant.
45328 * <script type="text/javascript">
45331 * @class Roo.TabPanel
45332 * @extends Roo.util.Observable
45333 * A lightweight tab container.
45337 // basic tabs 1, built from existing content
45338 var tabs = new Roo.TabPanel("tabs1");
45339 tabs.addTab("script", "View Script");
45340 tabs.addTab("markup", "View Markup");
45341 tabs.activate("script");
45343 // more advanced tabs, built from javascript
45344 var jtabs = new Roo.TabPanel("jtabs");
45345 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45347 // set up the UpdateManager
45348 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45349 var updater = tab2.getUpdateManager();
45350 updater.setDefaultUrl("ajax1.htm");
45351 tab2.on('activate', updater.refresh, updater, true);
45353 // Use setUrl for Ajax loading
45354 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45355 tab3.setUrl("ajax2.htm", null, true);
45358 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45361 jtabs.activate("jtabs-1");
45364 * Create a new TabPanel.
45365 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45366 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45368 Roo.bootstrap.panel.Tabs = function(config){
45370 * The container element for this TabPanel.
45371 * @type Roo.Element
45373 this.el = Roo.get(config.el);
45376 if(typeof config == "boolean"){
45377 this.tabPosition = config ? "bottom" : "top";
45379 Roo.apply(this, config);
45383 if(this.tabPosition == "bottom"){
45384 // if tabs are at the bottom = create the body first.
45385 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45386 this.el.addClass("roo-tabs-bottom");
45388 // next create the tabs holders
45390 if (this.tabPosition == "west"){
45392 var reg = this.region; // fake it..
45394 if (!reg.mgr.parent) {
45397 reg = reg.mgr.parent.region;
45399 Roo.log("got nest?");
45401 if (reg.mgr.getRegion('west')) {
45402 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45403 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45404 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45405 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45406 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45414 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45415 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45416 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45417 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45422 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45425 // finally - if tabs are at the top, then create the body last..
45426 if(this.tabPosition != "bottom"){
45427 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45428 * @type Roo.Element
45430 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45431 this.el.addClass("roo-tabs-top");
45435 this.bodyEl.setStyle("position", "relative");
45437 this.active = null;
45438 this.activateDelegate = this.activate.createDelegate(this);
45443 * Fires when the active tab changes
45444 * @param {Roo.TabPanel} this
45445 * @param {Roo.TabPanelItem} activePanel The new active tab
45449 * @event beforetabchange
45450 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45451 * @param {Roo.TabPanel} this
45452 * @param {Object} e Set cancel to true on this object to cancel the tab change
45453 * @param {Roo.TabPanelItem} tab The tab being changed to
45455 "beforetabchange" : true
45458 Roo.EventManager.onWindowResize(this.onResize, this);
45459 this.cpad = this.el.getPadding("lr");
45460 this.hiddenCount = 0;
45463 // toolbar on the tabbar support...
45464 if (this.toolbar) {
45465 alert("no toolbar support yet");
45466 this.toolbar = false;
45468 var tcfg = this.toolbar;
45469 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
45470 this.toolbar = new Roo.Toolbar(tcfg);
45471 if (Roo.isSafari) {
45472 var tbl = tcfg.container.child('table', true);
45473 tbl.setAttribute('width', '100%');
45481 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45484 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45486 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45488 tabPosition : "top",
45490 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45492 currentTabWidth : 0,
45494 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45498 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45502 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45504 preferredTabWidth : 175,
45506 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45508 resizeTabs : false,
45510 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45512 monitorResize : true,
45514 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
45516 toolbar : false, // set by caller..
45518 region : false, /// set by caller
45520 disableTooltips : true, // not used yet...
45523 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45524 * @param {String} id The id of the div to use <b>or create</b>
45525 * @param {String} text The text for the tab
45526 * @param {String} content (optional) Content to put in the TabPanelItem body
45527 * @param {Boolean} closable (optional) True to create a close icon on the tab
45528 * @return {Roo.TabPanelItem} The created TabPanelItem
45530 addTab : function(id, text, content, closable, tpl)
45532 var item = new Roo.bootstrap.panel.TabItem({
45536 closable : closable,
45539 this.addTabItem(item);
45541 item.setContent(content);
45547 * Returns the {@link Roo.TabPanelItem} with the specified id/index
45548 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45549 * @return {Roo.TabPanelItem}
45551 getTab : function(id){
45552 return this.items[id];
45556 * Hides the {@link Roo.TabPanelItem} with the specified id/index
45557 * @param {String/Number} id The id or index of the TabPanelItem to hide.
45559 hideTab : function(id){
45560 var t = this.items[id];
45563 this.hiddenCount++;
45564 this.autoSizeTabs();
45569 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45570 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45572 unhideTab : function(id){
45573 var t = this.items[id];
45575 t.setHidden(false);
45576 this.hiddenCount--;
45577 this.autoSizeTabs();
45582 * Adds an existing {@link Roo.TabPanelItem}.
45583 * @param {Roo.TabPanelItem} item The TabPanelItem to add
45585 addTabItem : function(item)
45587 this.items[item.id] = item;
45588 this.items.push(item);
45589 this.autoSizeTabs();
45590 // if(this.resizeTabs){
45591 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45592 // this.autoSizeTabs();
45594 // item.autoSize();
45599 * Removes a {@link Roo.TabPanelItem}.
45600 * @param {String/Number} id The id or index of the TabPanelItem to remove.
45602 removeTab : function(id){
45603 var items = this.items;
45604 var tab = items[id];
45605 if(!tab) { return; }
45606 var index = items.indexOf(tab);
45607 if(this.active == tab && items.length > 1){
45608 var newTab = this.getNextAvailable(index);
45613 this.stripEl.dom.removeChild(tab.pnode.dom);
45614 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45615 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45617 items.splice(index, 1);
45618 delete this.items[tab.id];
45619 tab.fireEvent("close", tab);
45620 tab.purgeListeners();
45621 this.autoSizeTabs();
45624 getNextAvailable : function(start){
45625 var items = this.items;
45627 // look for a next tab that will slide over to
45628 // replace the one being removed
45629 while(index < items.length){
45630 var item = items[++index];
45631 if(item && !item.isHidden()){
45635 // if one isn't found select the previous tab (on the left)
45638 var item = items[--index];
45639 if(item && !item.isHidden()){
45647 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45648 * @param {String/Number} id The id or index of the TabPanelItem to disable.
45650 disableTab : function(id){
45651 var tab = this.items[id];
45652 if(tab && this.active != tab){
45658 * Enables a {@link Roo.TabPanelItem} that is disabled.
45659 * @param {String/Number} id The id or index of the TabPanelItem to enable.
45661 enableTab : function(id){
45662 var tab = this.items[id];
45667 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45668 * @param {String/Number} id The id or index of the TabPanelItem to activate.
45669 * @return {Roo.TabPanelItem} The TabPanelItem.
45671 activate : function(id)
45673 //Roo.log('activite:' + id);
45675 var tab = this.items[id];
45679 if(tab == this.active || tab.disabled){
45683 this.fireEvent("beforetabchange", this, e, tab);
45684 if(e.cancel !== true && !tab.disabled){
45686 this.active.hide();
45688 this.active = this.items[id];
45689 this.active.show();
45690 this.fireEvent("tabchange", this, this.active);
45696 * Gets the active {@link Roo.TabPanelItem}.
45697 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45699 getActiveTab : function(){
45700 return this.active;
45704 * Updates the tab body element to fit the height of the container element
45705 * for overflow scrolling
45706 * @param {Number} targetHeight (optional) Override the starting height from the elements height
45708 syncHeight : function(targetHeight){
45709 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45710 var bm = this.bodyEl.getMargins();
45711 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45712 this.bodyEl.setHeight(newHeight);
45716 onResize : function(){
45717 if(this.monitorResize){
45718 this.autoSizeTabs();
45723 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45725 beginUpdate : function(){
45726 this.updating = true;
45730 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45732 endUpdate : function(){
45733 this.updating = false;
45734 this.autoSizeTabs();
45738 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45740 autoSizeTabs : function()
45742 var count = this.items.length;
45743 var vcount = count - this.hiddenCount;
45746 this.stripEl.hide();
45748 this.stripEl.show();
45751 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45756 var w = Math.max(this.el.getWidth() - this.cpad, 10);
45757 var availWidth = Math.floor(w / vcount);
45758 var b = this.stripBody;
45759 if(b.getWidth() > w){
45760 var tabs = this.items;
45761 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45762 if(availWidth < this.minTabWidth){
45763 /*if(!this.sleft){ // incomplete scrolling code
45764 this.createScrollButtons();
45767 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45770 if(this.currentTabWidth < this.preferredTabWidth){
45771 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45777 * Returns the number of tabs in this TabPanel.
45780 getCount : function(){
45781 return this.items.length;
45785 * Resizes all the tabs to the passed width
45786 * @param {Number} The new width
45788 setTabWidth : function(width){
45789 this.currentTabWidth = width;
45790 for(var i = 0, len = this.items.length; i < len; i++) {
45791 if(!this.items[i].isHidden()) {
45792 this.items[i].setWidth(width);
45798 * Destroys this TabPanel
45799 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45801 destroy : function(removeEl){
45802 Roo.EventManager.removeResizeListener(this.onResize, this);
45803 for(var i = 0, len = this.items.length; i < len; i++){
45804 this.items[i].purgeListeners();
45806 if(removeEl === true){
45807 this.el.update("");
45812 createStrip : function(container)
45814 var strip = document.createElement("nav");
45815 strip.className = Roo.bootstrap.version == 4 ?
45816 "navbar-light bg-light" :
45817 "navbar navbar-default"; //"x-tabs-wrap";
45818 container.appendChild(strip);
45822 createStripList : function(strip)
45824 // div wrapper for retard IE
45825 // returns the "tr" element.
45826 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45827 //'<div class="x-tabs-strip-wrap">'+
45828 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45829 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45830 return strip.firstChild; //.firstChild.firstChild.firstChild;
45832 createBody : function(container)
45834 var body = document.createElement("div");
45835 Roo.id(body, "tab-body");
45836 //Roo.fly(body).addClass("x-tabs-body");
45837 Roo.fly(body).addClass("tab-content");
45838 container.appendChild(body);
45841 createItemBody :function(bodyEl, id){
45842 var body = Roo.getDom(id);
45844 body = document.createElement("div");
45847 //Roo.fly(body).addClass("x-tabs-item-body");
45848 Roo.fly(body).addClass("tab-pane");
45849 bodyEl.insertBefore(body, bodyEl.firstChild);
45853 createStripElements : function(stripEl, text, closable, tpl)
45855 var td = document.createElement("li"); // was td..
45856 td.className = 'nav-item';
45858 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45861 stripEl.appendChild(td);
45863 td.className = "x-tabs-closable";
45864 if(!this.closeTpl){
45865 this.closeTpl = new Roo.Template(
45866 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45867 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45868 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
45871 var el = this.closeTpl.overwrite(td, {"text": text});
45872 var close = el.getElementsByTagName("div")[0];
45873 var inner = el.getElementsByTagName("em")[0];
45874 return {"el": el, "close": close, "inner": inner};
45877 // not sure what this is..
45878 // if(!this.tabTpl){
45879 //this.tabTpl = new Roo.Template(
45880 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45881 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45883 // this.tabTpl = new Roo.Template(
45884 // '<a href="#">' +
45885 // '<span unselectable="on"' +
45886 // (this.disableTooltips ? '' : ' title="{text}"') +
45887 // ' >{text}</span></a>'
45893 var template = tpl || this.tabTpl || false;
45896 template = new Roo.Template(
45897 Roo.bootstrap.version == 4 ?
45899 '<a class="nav-link" href="#" unselectable="on"' +
45900 (this.disableTooltips ? '' : ' title="{text}"') +
45903 '<a class="nav-link" href="#">' +
45904 '<span unselectable="on"' +
45905 (this.disableTooltips ? '' : ' title="{text}"') +
45906 ' >{text}</span></a>'
45911 switch (typeof(template)) {
45915 template = new Roo.Template(template);
45921 var el = template.overwrite(td, {"text": text});
45923 var inner = el.getElementsByTagName("span")[0];
45925 return {"el": el, "inner": inner};
45933 * @class Roo.TabPanelItem
45934 * @extends Roo.util.Observable
45935 * Represents an individual item (tab plus body) in a TabPanel.
45936 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45937 * @param {String} id The id of this TabPanelItem
45938 * @param {String} text The text for the tab of this TabPanelItem
45939 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45941 Roo.bootstrap.panel.TabItem = function(config){
45943 * The {@link Roo.TabPanel} this TabPanelItem belongs to
45944 * @type Roo.TabPanel
45946 this.tabPanel = config.panel;
45948 * The id for this TabPanelItem
45951 this.id = config.id;
45953 this.disabled = false;
45955 this.text = config.text;
45957 this.loaded = false;
45958 this.closable = config.closable;
45961 * The body element for this TabPanelItem.
45962 * @type Roo.Element
45964 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45965 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45966 this.bodyEl.setStyle("display", "block");
45967 this.bodyEl.setStyle("zoom", "1");
45968 //this.hideAction();
45970 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45972 this.el = Roo.get(els.el);
45973 this.inner = Roo.get(els.inner, true);
45974 this.textEl = Roo.bootstrap.version == 4 ?
45975 this.el : Roo.get(this.el.dom.firstChild, true);
45977 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45978 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45981 // this.el.on("mousedown", this.onTabMouseDown, this);
45982 this.el.on("click", this.onTabClick, this);
45984 if(config.closable){
45985 var c = Roo.get(els.close, true);
45986 c.dom.title = this.closeText;
45987 c.addClassOnOver("close-over");
45988 c.on("click", this.closeClick, this);
45994 * Fires when this tab becomes the active tab.
45995 * @param {Roo.TabPanel} tabPanel The parent TabPanel
45996 * @param {Roo.TabPanelItem} this
46000 * @event beforeclose
46001 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46002 * @param {Roo.TabPanelItem} this
46003 * @param {Object} e Set cancel to true on this object to cancel the close.
46005 "beforeclose": true,
46008 * Fires when this tab is closed.
46009 * @param {Roo.TabPanelItem} this
46013 * @event deactivate
46014 * Fires when this tab is no longer the active tab.
46015 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46016 * @param {Roo.TabPanelItem} this
46018 "deactivate" : true
46020 this.hidden = false;
46022 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46025 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46027 purgeListeners : function(){
46028 Roo.util.Observable.prototype.purgeListeners.call(this);
46029 this.el.removeAllListeners();
46032 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46035 this.status_node.addClass("active");
46038 this.tabPanel.stripWrap.repaint();
46040 this.fireEvent("activate", this.tabPanel, this);
46044 * Returns true if this tab is the active tab.
46045 * @return {Boolean}
46047 isActive : function(){
46048 return this.tabPanel.getActiveTab() == this;
46052 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46055 this.status_node.removeClass("active");
46057 this.fireEvent("deactivate", this.tabPanel, this);
46060 hideAction : function(){
46061 this.bodyEl.hide();
46062 this.bodyEl.setStyle("position", "absolute");
46063 this.bodyEl.setLeft("-20000px");
46064 this.bodyEl.setTop("-20000px");
46067 showAction : function(){
46068 this.bodyEl.setStyle("position", "relative");
46069 this.bodyEl.setTop("");
46070 this.bodyEl.setLeft("");
46071 this.bodyEl.show();
46075 * Set the tooltip for the tab.
46076 * @param {String} tooltip The tab's tooltip
46078 setTooltip : function(text){
46079 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46080 this.textEl.dom.qtip = text;
46081 this.textEl.dom.removeAttribute('title');
46083 this.textEl.dom.title = text;
46087 onTabClick : function(e){
46088 e.preventDefault();
46089 this.tabPanel.activate(this.id);
46092 onTabMouseDown : function(e){
46093 e.preventDefault();
46094 this.tabPanel.activate(this.id);
46097 getWidth : function(){
46098 return this.inner.getWidth();
46101 setWidth : function(width){
46102 var iwidth = width - this.linode.getPadding("lr");
46103 this.inner.setWidth(iwidth);
46104 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46105 this.linode.setWidth(width);
46109 * Show or hide the tab
46110 * @param {Boolean} hidden True to hide or false to show.
46112 setHidden : function(hidden){
46113 this.hidden = hidden;
46114 this.linode.setStyle("display", hidden ? "none" : "");
46118 * Returns true if this tab is "hidden"
46119 * @return {Boolean}
46121 isHidden : function(){
46122 return this.hidden;
46126 * Returns the text for this tab
46129 getText : function(){
46133 autoSize : function(){
46134 //this.el.beginMeasure();
46135 this.textEl.setWidth(1);
46137 * #2804 [new] Tabs in Roojs
46138 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46140 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46141 //this.el.endMeasure();
46145 * Sets the text for the tab (Note: this also sets the tooltip text)
46146 * @param {String} text The tab's text and tooltip
46148 setText : function(text){
46150 this.textEl.update(text);
46151 this.setTooltip(text);
46152 //if(!this.tabPanel.resizeTabs){
46153 // this.autoSize();
46157 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46159 activate : function(){
46160 this.tabPanel.activate(this.id);
46164 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46166 disable : function(){
46167 if(this.tabPanel.active != this){
46168 this.disabled = true;
46169 this.status_node.addClass("disabled");
46174 * Enables this TabPanelItem if it was previously disabled.
46176 enable : function(){
46177 this.disabled = false;
46178 this.status_node.removeClass("disabled");
46182 * Sets the content for this TabPanelItem.
46183 * @param {String} content The content
46184 * @param {Boolean} loadScripts true to look for and load scripts
46186 setContent : function(content, loadScripts){
46187 this.bodyEl.update(content, loadScripts);
46191 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46192 * @return {Roo.UpdateManager} The UpdateManager
46194 getUpdateManager : function(){
46195 return this.bodyEl.getUpdateManager();
46199 * Set a URL to be used to load the content for this TabPanelItem.
46200 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46201 * @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)
46202 * @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)
46203 * @return {Roo.UpdateManager} The UpdateManager
46205 setUrl : function(url, params, loadOnce){
46206 if(this.refreshDelegate){
46207 this.un('activate', this.refreshDelegate);
46209 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46210 this.on("activate", this.refreshDelegate);
46211 return this.bodyEl.getUpdateManager();
46215 _handleRefresh : function(url, params, loadOnce){
46216 if(!loadOnce || !this.loaded){
46217 var updater = this.bodyEl.getUpdateManager();
46218 updater.update(url, params, this._setLoaded.createDelegate(this));
46223 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
46224 * Will fail silently if the setUrl method has not been called.
46225 * This does not activate the panel, just updates its content.
46227 refresh : function(){
46228 if(this.refreshDelegate){
46229 this.loaded = false;
46230 this.refreshDelegate();
46235 _setLoaded : function(){
46236 this.loaded = true;
46240 closeClick : function(e){
46243 this.fireEvent("beforeclose", this, o);
46244 if(o.cancel !== true){
46245 this.tabPanel.removeTab(this.id);
46249 * The text displayed in the tooltip for the close icon.
46252 closeText : "Close this tab"
46255 * This script refer to:
46256 * Title: International Telephone Input
46257 * Author: Jack O'Connor
46258 * Code version: v12.1.12
46259 * Availability: https://github.com/jackocnr/intl-tel-input.git
46262 Roo.bootstrap.form.PhoneInputData = function() {
46265 "Afghanistan (افغانستان)",
46270 "Albania (Shqipëri)",
46275 "Algeria (الجزائر)",
46300 "Antigua and Barbuda",
46310 "Armenia (Հայաստան)",
46326 "Austria (Österreich)",
46331 "Azerbaijan (Azərbaycan)",
46341 "Bahrain (البحرين)",
46346 "Bangladesh (বাংলাদেশ)",
46356 "Belarus (Беларусь)",
46361 "Belgium (België)",
46391 "Bosnia and Herzegovina (Босна и Херцеговина)",
46406 "British Indian Ocean Territory",
46411 "British Virgin Islands",
46421 "Bulgaria (България)",
46431 "Burundi (Uburundi)",
46436 "Cambodia (កម្ពុជា)",
46441 "Cameroon (Cameroun)",
46450 ["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"]
46453 "Cape Verde (Kabu Verdi)",
46458 "Caribbean Netherlands",
46469 "Central African Republic (République centrafricaine)",
46489 "Christmas Island",
46495 "Cocos (Keeling) Islands",
46506 "Comoros (جزر القمر)",
46511 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46516 "Congo (Republic) (Congo-Brazzaville)",
46536 "Croatia (Hrvatska)",
46557 "Czech Republic (Česká republika)",
46562 "Denmark (Danmark)",
46577 "Dominican Republic (República Dominicana)",
46581 ["809", "829", "849"]
46599 "Equatorial Guinea (Guinea Ecuatorial)",
46619 "Falkland Islands (Islas Malvinas)",
46624 "Faroe Islands (Føroyar)",
46645 "French Guiana (Guyane française)",
46650 "French Polynesia (Polynésie française)",
46665 "Georgia (საქართველო)",
46670 "Germany (Deutschland)",
46690 "Greenland (Kalaallit Nunaat)",
46727 "Guinea-Bissau (Guiné Bissau)",
46752 "Hungary (Magyarország)",
46757 "Iceland (Ísland)",
46777 "Iraq (العراق)",
46793 "Israel (ישראל)",
46820 "Jordan (الأردن)",
46825 "Kazakhstan (Казахстан)",
46846 "Kuwait (الكويت)",
46851 "Kyrgyzstan (Кыргызстан)",
46861 "Latvia (Latvija)",
46866 "Lebanon (لبنان)",
46881 "Libya (ليبيا)",
46891 "Lithuania (Lietuva)",
46906 "Macedonia (FYROM) (Македонија)",
46911 "Madagascar (Madagasikara)",
46941 "Marshall Islands",
46951 "Mauritania (موريتانيا)",
46956 "Mauritius (Moris)",
46977 "Moldova (Republica Moldova)",
46987 "Mongolia (Монгол)",
46992 "Montenegro (Crna Gora)",
47002 "Morocco (المغرب)",
47008 "Mozambique (Moçambique)",
47013 "Myanmar (Burma) (မြန်မာ)",
47018 "Namibia (Namibië)",
47033 "Netherlands (Nederland)",
47038 "New Caledonia (Nouvelle-Calédonie)",
47073 "North Korea (조선 민주주의 인민 공화국)",
47078 "Northern Mariana Islands",
47094 "Pakistan (پاکستان)",
47104 "Palestine (فلسطين)",
47114 "Papua New Guinea",
47156 "Réunion (La Réunion)",
47162 "Romania (România)",
47178 "Saint Barthélemy",
47189 "Saint Kitts and Nevis",
47199 "Saint Martin (Saint-Martin (partie française))",
47205 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47210 "Saint Vincent and the Grenadines",
47225 "São Tomé and Príncipe (São Tomé e Príncipe)",
47230 "Saudi Arabia (المملكة العربية السعودية)",
47235 "Senegal (Sénégal)",
47265 "Slovakia (Slovensko)",
47270 "Slovenia (Slovenija)",
47280 "Somalia (Soomaaliya)",
47290 "South Korea (대한민국)",
47295 "South Sudan (جنوب السودان)",
47305 "Sri Lanka (ශ්රී ලංකාව)",
47310 "Sudan (السودان)",
47320 "Svalbard and Jan Mayen",
47331 "Sweden (Sverige)",
47336 "Switzerland (Schweiz)",
47341 "Syria (سوريا)",
47386 "Trinidad and Tobago",
47391 "Tunisia (تونس)",
47396 "Turkey (Türkiye)",
47406 "Turks and Caicos Islands",
47416 "U.S. Virgin Islands",
47426 "Ukraine (Україна)",
47431 "United Arab Emirates (الإمارات العربية المتحدة)",
47453 "Uzbekistan (Oʻzbekiston)",
47463 "Vatican City (Città del Vaticano)",
47474 "Vietnam (Việt Nam)",
47479 "Wallis and Futuna (Wallis-et-Futuna)",
47484 "Western Sahara (الصحراء الغربية)",
47490 "Yemen (اليمن)",
47514 * This script refer to:
47515 * Title: International Telephone Input
47516 * Author: Jack O'Connor
47517 * Code version: v12.1.12
47518 * Availability: https://github.com/jackocnr/intl-tel-input.git
47522 * @class Roo.bootstrap.form.PhoneInput
47523 * @extends Roo.bootstrap.form.TriggerField
47524 * An input with International dial-code selection
47526 * @cfg {String} defaultDialCode default '+852'
47527 * @cfg {Array} preferedCountries default []
47530 * Create a new PhoneInput.
47531 * @param {Object} config Configuration options
47534 Roo.bootstrap.form.PhoneInput = function(config) {
47535 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47538 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47540 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47542 listWidth: undefined,
47544 selectedClass: 'active',
47546 invalidClass : "has-warning",
47548 validClass: 'has-success',
47550 allowed: '0123456789',
47555 * @cfg {String} defaultDialCode The default dial code when initializing the input
47557 defaultDialCode: '+852',
47560 * @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
47562 preferedCountries: false,
47564 getAutoCreate : function()
47566 var data = Roo.bootstrap.form.PhoneInputData();
47567 var align = this.labelAlign || this.parentLabelAlign();
47570 this.allCountries = [];
47571 this.dialCodeMapping = [];
47573 for (var i = 0; i < data.length; i++) {
47575 this.allCountries[i] = {
47579 priority: c[3] || 0,
47580 areaCodes: c[4] || null
47582 this.dialCodeMapping[c[2]] = {
47585 priority: c[3] || 0,
47586 areaCodes: c[4] || null
47598 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47599 maxlength: this.max_length,
47600 cls : 'form-control tel-input',
47601 autocomplete: 'new-password'
47604 var hiddenInput = {
47607 cls: 'hidden-tel-input'
47611 hiddenInput.name = this.name;
47614 if (this.disabled) {
47615 input.disabled = true;
47618 var flag_container = {
47635 cls: this.hasFeedback ? 'has-feedback' : '',
47641 cls: 'dial-code-holder',
47648 cls: 'roo-select2-container input-group',
47655 if (this.fieldLabel.length) {
47658 tooltip: 'This field is required'
47664 cls: 'control-label',
47670 html: this.fieldLabel
47673 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47679 if(this.indicatorpos == 'right') {
47680 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47687 if(align == 'left') {
47695 if(this.labelWidth > 12){
47696 label.style = "width: " + this.labelWidth + 'px';
47698 if(this.labelWidth < 13 && this.labelmd == 0){
47699 this.labelmd = this.labelWidth;
47701 if(this.labellg > 0){
47702 label.cls += ' col-lg-' + this.labellg;
47703 input.cls += ' col-lg-' + (12 - this.labellg);
47705 if(this.labelmd > 0){
47706 label.cls += ' col-md-' + this.labelmd;
47707 container.cls += ' col-md-' + (12 - this.labelmd);
47709 if(this.labelsm > 0){
47710 label.cls += ' col-sm-' + this.labelsm;
47711 container.cls += ' col-sm-' + (12 - this.labelsm);
47713 if(this.labelxs > 0){
47714 label.cls += ' col-xs-' + this.labelxs;
47715 container.cls += ' col-xs-' + (12 - this.labelxs);
47725 var settings = this;
47727 ['xs','sm','md','lg'].map(function(size){
47728 if (settings[size]) {
47729 cfg.cls += ' col-' + size + '-' + settings[size];
47733 this.store = new Roo.data.Store({
47734 proxy : new Roo.data.MemoryProxy({}),
47735 reader : new Roo.data.JsonReader({
47746 'name' : 'dialCode',
47750 'name' : 'priority',
47754 'name' : 'areaCodes',
47761 if(!this.preferedCountries) {
47762 this.preferedCountries = [
47769 var p = this.preferedCountries.reverse();
47772 for (var i = 0; i < p.length; i++) {
47773 for (var j = 0; j < this.allCountries.length; j++) {
47774 if(this.allCountries[j].iso2 == p[i]) {
47775 var t = this.allCountries[j];
47776 this.allCountries.splice(j,1);
47777 this.allCountries.unshift(t);
47783 this.store.proxy.data = {
47785 data: this.allCountries
47791 initEvents : function()
47794 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47796 this.indicator = this.indicatorEl();
47797 this.flag = this.flagEl();
47798 this.dialCodeHolder = this.dialCodeHolderEl();
47800 this.trigger = this.el.select('div.flag-box',true).first();
47801 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47806 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47807 _this.list.setWidth(lw);
47810 this.list.on('mouseover', this.onViewOver, this);
47811 this.list.on('mousemove', this.onViewMove, this);
47812 this.inputEl().on("keyup", this.onKeyUp, this);
47813 this.inputEl().on("keypress", this.onKeyPress, this);
47815 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47817 this.view = new Roo.View(this.list, this.tpl, {
47818 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47821 this.view.on('click', this.onViewClick, this);
47822 this.setValue(this.defaultDialCode);
47825 onTriggerClick : function(e)
47827 Roo.log('trigger click');
47832 if(this.isExpanded()){
47834 this.hasFocus = false;
47836 this.store.load({});
47837 this.hasFocus = true;
47842 isExpanded : function()
47844 return this.list.isVisible();
47847 collapse : function()
47849 if(!this.isExpanded()){
47853 Roo.get(document).un('mousedown', this.collapseIf, this);
47854 Roo.get(document).un('mousewheel', this.collapseIf, this);
47855 this.fireEvent('collapse', this);
47859 expand : function()
47863 if(this.isExpanded() || !this.hasFocus){
47867 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47868 this.list.setWidth(lw);
47871 this.restrictHeight();
47873 Roo.get(document).on('mousedown', this.collapseIf, this);
47874 Roo.get(document).on('mousewheel', this.collapseIf, this);
47876 this.fireEvent('expand', this);
47879 restrictHeight : function()
47881 this.list.alignTo(this.inputEl(), this.listAlign);
47882 this.list.alignTo(this.inputEl(), this.listAlign);
47885 onViewOver : function(e, t)
47887 if(this.inKeyMode){
47890 var item = this.view.findItemFromChild(t);
47893 var index = this.view.indexOf(item);
47894 this.select(index, false);
47899 onViewClick : function(view, doFocus, el, e)
47901 var index = this.view.getSelectedIndexes()[0];
47903 var r = this.store.getAt(index);
47906 this.onSelect(r, index);
47908 if(doFocus !== false && !this.blockFocus){
47909 this.inputEl().focus();
47913 onViewMove : function(e, t)
47915 this.inKeyMode = false;
47918 select : function(index, scrollIntoView)
47920 this.selectedIndex = index;
47921 this.view.select(index);
47922 if(scrollIntoView !== false){
47923 var el = this.view.getNode(index);
47925 this.list.scrollChildIntoView(el, false);
47930 createList : function()
47932 this.list = Roo.get(document.body).createChild({
47934 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47935 style: 'display:none'
47938 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47941 collapseIf : function(e)
47943 var in_combo = e.within(this.el);
47944 var in_list = e.within(this.list);
47945 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47947 if (in_combo || in_list || is_list) {
47953 onSelect : function(record, index)
47955 if(this.fireEvent('beforeselect', this, record, index) !== false){
47957 this.setFlagClass(record.data.iso2);
47958 this.setDialCode(record.data.dialCode);
47959 this.hasFocus = false;
47961 this.fireEvent('select', this, record, index);
47965 flagEl : function()
47967 var flag = this.el.select('div.flag',true).first();
47974 dialCodeHolderEl : function()
47976 var d = this.el.select('input.dial-code-holder',true).first();
47983 setDialCode : function(v)
47985 this.dialCodeHolder.dom.value = '+'+v;
47988 setFlagClass : function(n)
47990 this.flag.dom.className = 'flag '+n;
47993 getValue : function()
47995 var v = this.inputEl().getValue();
47996 if(this.dialCodeHolder) {
47997 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48002 setValue : function(v)
48004 var d = this.getDialCode(v);
48006 //invalid dial code
48007 if(v.length == 0 || !d || d.length == 0) {
48009 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48010 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48016 this.setFlagClass(this.dialCodeMapping[d].iso2);
48017 this.setDialCode(d);
48018 this.inputEl().dom.value = v.replace('+'+d,'');
48019 this.hiddenEl().dom.value = this.getValue();
48024 getDialCode : function(v)
48028 if (v.length == 0) {
48029 return this.dialCodeHolder.dom.value;
48033 if (v.charAt(0) != "+") {
48036 var numericChars = "";
48037 for (var i = 1; i < v.length; i++) {
48038 var c = v.charAt(i);
48041 if (this.dialCodeMapping[numericChars]) {
48042 dialCode = v.substr(1, i);
48044 if (numericChars.length == 4) {
48054 this.setValue(this.defaultDialCode);
48058 hiddenEl : function()
48060 return this.el.select('input.hidden-tel-input',true).first();
48063 // after setting val
48064 onKeyUp : function(e){
48065 this.setValue(this.getValue());
48068 onKeyPress : function(e){
48069 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48076 * @class Roo.bootstrap.form.MoneyField
48077 * @extends Roo.bootstrap.form.ComboBox
48078 * Bootstrap MoneyField class
48081 * Create a new MoneyField.
48082 * @param {Object} config Configuration options
48085 Roo.bootstrap.form.MoneyField = function(config) {
48087 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48091 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48094 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48096 allowDecimals : true,
48098 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48100 decimalSeparator : ".",
48102 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48104 decimalPrecision : 0,
48106 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48108 allowNegative : true,
48110 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48114 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48116 minValue : Number.NEGATIVE_INFINITY,
48118 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48120 maxValue : Number.MAX_VALUE,
48122 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48124 minText : "The minimum value for this field is {0}",
48126 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48128 maxText : "The maximum value for this field is {0}",
48130 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48131 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48133 nanText : "{0} is not a valid number",
48135 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48139 * @cfg {String} defaults currency of the MoneyField
48140 * value should be in lkey
48142 defaultCurrency : false,
48144 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48146 thousandsDelimiter : false,
48148 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48157 * @cfg {Roo.data.Store} store Store to lookup currency??
48161 getAutoCreate : function()
48163 var align = this.labelAlign || this.parentLabelAlign();
48175 cls : 'form-control roo-money-amount-input',
48176 autocomplete: 'new-password'
48179 var hiddenInput = {
48183 cls: 'hidden-number-input'
48186 if(this.max_length) {
48187 input.maxlength = this.max_length;
48191 hiddenInput.name = this.name;
48194 if (this.disabled) {
48195 input.disabled = true;
48198 var clg = 12 - this.inputlg;
48199 var cmd = 12 - this.inputmd;
48200 var csm = 12 - this.inputsm;
48201 var cxs = 12 - this.inputxs;
48205 cls : 'row roo-money-field',
48209 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48213 cls: 'roo-select2-container input-group',
48217 cls : 'form-control roo-money-currency-input',
48218 autocomplete: 'new-password',
48220 name : this.currencyName
48224 cls : 'input-group-addon',
48238 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48242 cls: this.hasFeedback ? 'has-feedback' : '',
48253 if (this.fieldLabel.length) {
48256 tooltip: 'This field is required'
48262 cls: 'control-label',
48268 html: this.fieldLabel
48271 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48277 if(this.indicatorpos == 'right') {
48278 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48285 if(align == 'left') {
48293 if(this.labelWidth > 12){
48294 label.style = "width: " + this.labelWidth + 'px';
48296 if(this.labelWidth < 13 && this.labelmd == 0){
48297 this.labelmd = this.labelWidth;
48299 if(this.labellg > 0){
48300 label.cls += ' col-lg-' + this.labellg;
48301 input.cls += ' col-lg-' + (12 - this.labellg);
48303 if(this.labelmd > 0){
48304 label.cls += ' col-md-' + this.labelmd;
48305 container.cls += ' col-md-' + (12 - this.labelmd);
48307 if(this.labelsm > 0){
48308 label.cls += ' col-sm-' + this.labelsm;
48309 container.cls += ' col-sm-' + (12 - this.labelsm);
48311 if(this.labelxs > 0){
48312 label.cls += ' col-xs-' + this.labelxs;
48313 container.cls += ' col-xs-' + (12 - this.labelxs);
48324 var settings = this;
48326 ['xs','sm','md','lg'].map(function(size){
48327 if (settings[size]) {
48328 cfg.cls += ' col-' + size + '-' + settings[size];
48335 initEvents : function()
48337 this.indicator = this.indicatorEl();
48339 this.initCurrencyEvent();
48341 this.initNumberEvent();
48344 initCurrencyEvent : function()
48347 throw "can not find store for combo";
48350 this.store = Roo.factory(this.store, Roo.data);
48351 this.store.parent = this;
48355 this.triggerEl = this.el.select('.input-group-addon', true).first();
48357 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48362 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48363 _this.list.setWidth(lw);
48366 this.list.on('mouseover', this.onViewOver, this);
48367 this.list.on('mousemove', this.onViewMove, this);
48368 this.list.on('scroll', this.onViewScroll, this);
48371 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48374 this.view = new Roo.View(this.list, this.tpl, {
48375 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48378 this.view.on('click', this.onViewClick, this);
48380 this.store.on('beforeload', this.onBeforeLoad, this);
48381 this.store.on('load', this.onLoad, this);
48382 this.store.on('loadexception', this.onLoadException, this);
48384 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48385 "up" : function(e){
48386 this.inKeyMode = true;
48390 "down" : function(e){
48391 if(!this.isExpanded()){
48392 this.onTriggerClick();
48394 this.inKeyMode = true;
48399 "enter" : function(e){
48402 if(this.fireEvent("specialkey", this, e)){
48403 this.onViewClick(false);
48409 "esc" : function(e){
48413 "tab" : function(e){
48416 if(this.fireEvent("specialkey", this, e)){
48417 this.onViewClick(false);
48425 doRelay : function(foo, bar, hname){
48426 if(hname == 'down' || this.scope.isExpanded()){
48427 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48435 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48439 initNumberEvent : function(e)
48441 this.inputEl().on("keydown" , this.fireKey, this);
48442 this.inputEl().on("focus", this.onFocus, this);
48443 this.inputEl().on("blur", this.onBlur, this);
48445 this.inputEl().relayEvent('keyup', this);
48447 if(this.indicator){
48448 this.indicator.addClass('invisible');
48451 this.originalValue = this.getValue();
48453 if(this.validationEvent == 'keyup'){
48454 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48455 this.inputEl().on('keyup', this.filterValidation, this);
48457 else if(this.validationEvent !== false){
48458 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48461 if(this.selectOnFocus){
48462 this.on("focus", this.preFocus, this);
48465 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48466 this.inputEl().on("keypress", this.filterKeys, this);
48468 this.inputEl().relayEvent('keypress', this);
48471 var allowed = "0123456789";
48473 if(this.allowDecimals){
48474 allowed += this.decimalSeparator;
48477 if(this.allowNegative){
48481 if(this.thousandsDelimiter) {
48485 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48487 var keyPress = function(e){
48489 var k = e.getKey();
48491 var c = e.getCharCode();
48494 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48495 allowed.indexOf(String.fromCharCode(c)) === -1
48501 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48505 if(allowed.indexOf(String.fromCharCode(c)) === -1){
48510 this.inputEl().on("keypress", keyPress, this);
48514 onTriggerClick : function(e)
48521 this.loadNext = false;
48523 if(this.isExpanded()){
48528 this.hasFocus = true;
48530 if(this.triggerAction == 'all') {
48531 this.doQuery(this.allQuery, true);
48535 this.doQuery(this.getRawValue());
48538 getCurrency : function()
48540 var v = this.currencyEl().getValue();
48545 restrictHeight : function()
48547 this.list.alignTo(this.currencyEl(), this.listAlign);
48548 this.list.alignTo(this.currencyEl(), this.listAlign);
48551 onViewClick : function(view, doFocus, el, e)
48553 var index = this.view.getSelectedIndexes()[0];
48555 var r = this.store.getAt(index);
48558 this.onSelect(r, index);
48562 onSelect : function(record, index){
48564 if(this.fireEvent('beforeselect', this, record, index) !== false){
48566 this.setFromCurrencyData(index > -1 ? record.data : false);
48570 this.fireEvent('select', this, record, index);
48574 setFromCurrencyData : function(o)
48578 this.lastCurrency = o;
48580 if (this.currencyField) {
48581 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48583 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
48586 this.lastSelectionText = currency;
48588 //setting default currency
48589 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48590 this.setCurrency(this.defaultCurrency);
48594 this.setCurrency(currency);
48597 setFromData : function(o)
48601 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48603 this.setFromCurrencyData(c);
48608 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48610 Roo.log('no value set for '+ (this.name ? this.name : this.id));
48613 this.setValue(value);
48617 setCurrency : function(v)
48619 this.currencyValue = v;
48622 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48627 setValue : function(v)
48629 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48635 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48637 this.inputEl().dom.value = (v == '') ? '' :
48638 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48640 if(!this.allowZero && v === '0') {
48641 this.hiddenEl().dom.value = '';
48642 this.inputEl().dom.value = '';
48649 getRawValue : function()
48651 var v = this.inputEl().getValue();
48656 getValue : function()
48658 return this.fixPrecision(this.parseValue(this.getRawValue()));
48661 parseValue : function(value)
48663 if(this.thousandsDelimiter) {
48665 r = new RegExp(",", "g");
48666 value = value.replace(r, "");
48669 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48670 return isNaN(value) ? '' : value;
48674 fixPrecision : function(value)
48676 if(this.thousandsDelimiter) {
48678 r = new RegExp(",", "g");
48679 value = value.replace(r, "");
48682 var nan = isNaN(value);
48684 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48685 return nan ? '' : value;
48687 return parseFloat(value).toFixed(this.decimalPrecision);
48690 decimalPrecisionFcn : function(v)
48692 return Math.floor(v);
48695 validateValue : function(value)
48697 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48701 var num = this.parseValue(value);
48704 this.markInvalid(String.format(this.nanText, value));
48708 if(num < this.minValue){
48709 this.markInvalid(String.format(this.minText, this.minValue));
48713 if(num > this.maxValue){
48714 this.markInvalid(String.format(this.maxText, this.maxValue));
48721 validate : function()
48723 if(this.disabled || this.allowBlank){
48728 var currency = this.getCurrency();
48730 if(this.validateValue(this.getRawValue()) && currency.length){
48735 this.markInvalid();
48739 getName: function()
48744 beforeBlur : function()
48750 var v = this.parseValue(this.getRawValue());
48757 onBlur : function()
48761 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48762 //this.el.removeClass(this.focusClass);
48765 this.hasFocus = false;
48767 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48771 var v = this.getValue();
48773 if(String(v) !== String(this.startValue)){
48774 this.fireEvent('change', this, v, this.startValue);
48777 this.fireEvent("blur", this);
48780 inputEl : function()
48782 return this.el.select('.roo-money-amount-input', true).first();
48785 currencyEl : function()
48787 return this.el.select('.roo-money-currency-input', true).first();
48790 hiddenEl : function()
48792 return this.el.select('input.hidden-number-input',true).first();
48796 * @class Roo.bootstrap.BezierSignature
48797 * @extends Roo.bootstrap.Component
48798 * Bootstrap BezierSignature class
48799 * This script refer to:
48800 * Title: Signature Pad
48802 * Availability: https://github.com/szimek/signature_pad
48805 * Create a new BezierSignature
48806 * @param {Object} config The config object
48809 Roo.bootstrap.BezierSignature = function(config){
48810 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48816 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48823 mouse_btn_down: true,
48826 * @cfg {int} canvas height
48828 canvas_height: '200px',
48831 * @cfg {float|function} Radius of a single dot.
48836 * @cfg {float} Minimum width of a line. Defaults to 0.5.
48841 * @cfg {float} Maximum width of a line. Defaults to 2.5.
48846 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48851 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48856 * @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.
48858 bg_color: 'rgba(0, 0, 0, 0)',
48861 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48863 dot_color: 'black',
48866 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48868 velocity_filter_weight: 0.7,
48871 * @cfg {function} Callback when stroke begin.
48876 * @cfg {function} Callback when stroke end.
48880 getAutoCreate : function()
48882 var cls = 'roo-signature column';
48885 cls += ' ' + this.cls;
48895 for(var i = 0; i < col_sizes.length; i++) {
48896 if(this[col_sizes[i]]) {
48897 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48907 cls: 'roo-signature-body',
48911 cls: 'roo-signature-body-canvas',
48912 height: this.canvas_height,
48913 width: this.canvas_width
48920 style: 'display: none'
48928 initEvents: function()
48930 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48932 var canvas = this.canvasEl();
48934 // mouse && touch event swapping...
48935 canvas.dom.style.touchAction = 'none';
48936 canvas.dom.style.msTouchAction = 'none';
48938 this.mouse_btn_down = false;
48939 canvas.on('mousedown', this._handleMouseDown, this);
48940 canvas.on('mousemove', this._handleMouseMove, this);
48941 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48943 if (window.PointerEvent) {
48944 canvas.on('pointerdown', this._handleMouseDown, this);
48945 canvas.on('pointermove', this._handleMouseMove, this);
48946 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48949 if ('ontouchstart' in window) {
48950 canvas.on('touchstart', this._handleTouchStart, this);
48951 canvas.on('touchmove', this._handleTouchMove, this);
48952 canvas.on('touchend', this._handleTouchEnd, this);
48955 Roo.EventManager.onWindowResize(this.resize, this, true);
48957 // file input event
48958 this.fileEl().on('change', this.uploadImage, this);
48965 resize: function(){
48967 var canvas = this.canvasEl().dom;
48968 var ctx = this.canvasElCtx();
48969 var img_data = false;
48971 if(canvas.width > 0) {
48972 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48974 // setting canvas width will clean img data
48977 var style = window.getComputedStyle ?
48978 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48980 var padding_left = parseInt(style.paddingLeft) || 0;
48981 var padding_right = parseInt(style.paddingRight) || 0;
48983 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48986 ctx.putImageData(img_data, 0, 0);
48990 _handleMouseDown: function(e)
48992 if (e.browserEvent.which === 1) {
48993 this.mouse_btn_down = true;
48994 this.strokeBegin(e);
48998 _handleMouseMove: function (e)
49000 if (this.mouse_btn_down) {
49001 this.strokeMoveUpdate(e);
49005 _handleMouseUp: function (e)
49007 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49008 this.mouse_btn_down = false;
49013 _handleTouchStart: function (e) {
49015 e.preventDefault();
49016 if (e.browserEvent.targetTouches.length === 1) {
49017 // var touch = e.browserEvent.changedTouches[0];
49018 // this.strokeBegin(touch);
49020 this.strokeBegin(e); // assume e catching the correct xy...
49024 _handleTouchMove: function (e) {
49025 e.preventDefault();
49026 // var touch = event.targetTouches[0];
49027 // _this._strokeMoveUpdate(touch);
49028 this.strokeMoveUpdate(e);
49031 _handleTouchEnd: function (e) {
49032 var wasCanvasTouched = e.target === this.canvasEl().dom;
49033 if (wasCanvasTouched) {
49034 e.preventDefault();
49035 // var touch = event.changedTouches[0];
49036 // _this._strokeEnd(touch);
49041 reset: function () {
49042 this._lastPoints = [];
49043 this._lastVelocity = 0;
49044 this._lastWidth = (this.min_width + this.max_width) / 2;
49045 this.canvasElCtx().fillStyle = this.dot_color;
49048 strokeMoveUpdate: function(e)
49050 this.strokeUpdate(e);
49052 if (this.throttle) {
49053 this.throttleStroke(this.strokeUpdate, this.throttle);
49056 this.strokeUpdate(e);
49060 strokeBegin: function(e)
49062 var newPointGroup = {
49063 color: this.dot_color,
49067 if (typeof this.onBegin === 'function') {
49071 this.curve_data.push(newPointGroup);
49073 this.strokeUpdate(e);
49076 strokeUpdate: function(e)
49078 var rect = this.canvasEl().dom.getBoundingClientRect();
49079 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49080 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49081 var lastPoints = lastPointGroup.points;
49082 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49083 var isLastPointTooClose = lastPoint
49084 ? point.distanceTo(lastPoint) <= this.min_distance
49086 var color = lastPointGroup.color;
49087 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49088 var curve = this.addPoint(point);
49090 this.drawDot({color: color, point: point});
49093 this.drawCurve({color: color, curve: curve});
49103 strokeEnd: function(e)
49105 this.strokeUpdate(e);
49106 if (typeof this.onEnd === 'function') {
49111 addPoint: function (point) {
49112 var _lastPoints = this._lastPoints;
49113 _lastPoints.push(point);
49114 if (_lastPoints.length > 2) {
49115 if (_lastPoints.length === 3) {
49116 _lastPoints.unshift(_lastPoints[0]);
49118 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49119 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49120 _lastPoints.shift();
49126 calculateCurveWidths: function (startPoint, endPoint) {
49127 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49128 (1 - this.velocity_filter_weight) * this._lastVelocity;
49130 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49133 start: this._lastWidth
49136 this._lastVelocity = velocity;
49137 this._lastWidth = newWidth;
49141 drawDot: function (_a) {
49142 var color = _a.color, point = _a.point;
49143 var ctx = this.canvasElCtx();
49144 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49146 this.drawCurveSegment(point.x, point.y, width);
49148 ctx.fillStyle = color;
49152 drawCurve: function (_a) {
49153 var color = _a.color, curve = _a.curve;
49154 var ctx = this.canvasElCtx();
49155 var widthDelta = curve.endWidth - curve.startWidth;
49156 var drawSteps = Math.floor(curve.length()) * 2;
49158 ctx.fillStyle = color;
49159 for (var i = 0; i < drawSteps; i += 1) {
49160 var t = i / drawSteps;
49166 var x = uuu * curve.startPoint.x;
49167 x += 3 * uu * t * curve.control1.x;
49168 x += 3 * u * tt * curve.control2.x;
49169 x += ttt * curve.endPoint.x;
49170 var y = uuu * curve.startPoint.y;
49171 y += 3 * uu * t * curve.control1.y;
49172 y += 3 * u * tt * curve.control2.y;
49173 y += ttt * curve.endPoint.y;
49174 var width = curve.startWidth + ttt * widthDelta;
49175 this.drawCurveSegment(x, y, width);
49181 drawCurveSegment: function (x, y, width) {
49182 var ctx = this.canvasElCtx();
49184 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49185 this.is_empty = false;
49190 var ctx = this.canvasElCtx();
49191 var canvas = this.canvasEl().dom;
49192 ctx.fillStyle = this.bg_color;
49193 ctx.clearRect(0, 0, canvas.width, canvas.height);
49194 ctx.fillRect(0, 0, canvas.width, canvas.height);
49195 this.curve_data = [];
49197 this.is_empty = true;
49202 return this.el.select('input',true).first();
49205 canvasEl: function()
49207 return this.el.select('canvas',true).first();
49210 canvasElCtx: function()
49212 return this.el.select('canvas',true).first().dom.getContext('2d');
49215 getImage: function(type)
49217 if(this.is_empty) {
49222 return this.canvasEl().dom.toDataURL('image/'+type, 1);
49225 drawFromImage: function(img_src)
49227 var img = new Image();
49229 img.onload = function(){
49230 this.canvasElCtx().drawImage(img, 0, 0);
49235 this.is_empty = false;
49238 selectImage: function()
49240 this.fileEl().dom.click();
49243 uploadImage: function(e)
49245 var reader = new FileReader();
49247 reader.onload = function(e){
49248 var img = new Image();
49249 img.onload = function(){
49251 this.canvasElCtx().drawImage(img, 0, 0);
49253 img.src = e.target.result;
49256 reader.readAsDataURL(e.target.files[0]);
49259 // Bezier Point Constructor
49260 Point: (function () {
49261 function Point(x, y, time) {
49264 this.time = time || Date.now();
49266 Point.prototype.distanceTo = function (start) {
49267 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49269 Point.prototype.equals = function (other) {
49270 return this.x === other.x && this.y === other.y && this.time === other.time;
49272 Point.prototype.velocityFrom = function (start) {
49273 return this.time !== start.time
49274 ? this.distanceTo(start) / (this.time - start.time)
49281 // Bezier Constructor
49282 Bezier: (function () {
49283 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49284 this.startPoint = startPoint;
49285 this.control2 = control2;
49286 this.control1 = control1;
49287 this.endPoint = endPoint;
49288 this.startWidth = startWidth;
49289 this.endWidth = endWidth;
49291 Bezier.fromPoints = function (points, widths, scope) {
49292 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49293 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49294 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49296 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49297 var dx1 = s1.x - s2.x;
49298 var dy1 = s1.y - s2.y;
49299 var dx2 = s2.x - s3.x;
49300 var dy2 = s2.y - s3.y;
49301 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49302 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49303 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49304 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49305 var dxm = m1.x - m2.x;
49306 var dym = m1.y - m2.y;
49307 var k = l2 / (l1 + l2);
49308 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49309 var tx = s2.x - cm.x;
49310 var ty = s2.y - cm.y;
49312 c1: new scope.Point(m1.x + tx, m1.y + ty),
49313 c2: new scope.Point(m2.x + tx, m2.y + ty)
49316 Bezier.prototype.length = function () {
49321 for (var i = 0; i <= steps; i += 1) {
49323 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49324 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49326 var xdiff = cx - px;
49327 var ydiff = cy - py;
49328 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49335 Bezier.prototype.point = function (t, start, c1, c2, end) {
49336 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49337 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49338 + (3.0 * c2 * (1.0 - t) * t * t)
49339 + (end * t * t * t);
49344 throttleStroke: function(fn, wait) {
49345 if (wait === void 0) { wait = 250; }
49347 var timeout = null;
49351 var later = function () {
49352 previous = Date.now();
49354 result = fn.apply(storedContext, storedArgs);
49356 storedContext = null;
49360 return function wrapper() {
49362 for (var _i = 0; _i < arguments.length; _i++) {
49363 args[_i] = arguments[_i];
49365 var now = Date.now();
49366 var remaining = wait - (now - previous);
49367 storedContext = this;
49369 if (remaining <= 0 || remaining > wait) {
49371 clearTimeout(timeout);
49375 result = fn.apply(storedContext, storedArgs);
49377 storedContext = null;
49381 else if (!timeout) {
49382 timeout = window.setTimeout(later, remaining);
49392 // old names for form elements
49393 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
49394 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
49395 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
49396 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
49397 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
49398 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
49399 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
49400 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
49401 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
49402 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
49403 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
49404 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
49405 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
49406 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
49407 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
49408 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
49409 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
49410 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
49411 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
49412 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
49413 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
49414 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
49415 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
49416 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
49417 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
49418 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
49420 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
49421 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49423 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
49424 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
49426 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
49427 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49428 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
49429 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator