2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
22 * Ext JS Library 1.1.1
23 * Copyright(c) 2006-2007, Ext JS, LLC.
25 * Originally Released Under LGPL - original licence link has changed is not relivant.
28 * <script type="text/javascript">
34 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
35 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
36 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
39 * @param {Object} config The config object
41 Roo.Shadow = function(config){
42 Roo.apply(this, config);
43 if(typeof this.mode != "string"){
44 this.mode = this.defaultMode;
46 var o = this.offset, a = {h: 0};
47 var rad = Math.floor(this.offset/2);
48 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
54 a.l -= this.offset + rad;
55 a.t -= this.offset + rad;
66 a.l -= (this.offset - rad);
67 a.t -= this.offset + rad;
69 a.w -= (this.offset - rad)*2;
80 a.l -= (this.offset - rad);
81 a.t -= (this.offset - rad);
83 a.w -= (this.offset + rad + 1);
84 a.h -= (this.offset + rad);
93 Roo.Shadow.prototype = {
96 * The shadow display mode. Supports the following options:<br />
97 * sides: Shadow displays on both sides and bottom only<br />
98 * frame: Shadow displays equally on all four sides<br />
99 * drop: Traditional bottom-right drop shadow (default)
103 * @cfg {String} offset
104 * The number of pixels to offset the shadow from the element (defaults to 4)
112 * Displays the shadow under the target element
113 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
115 show : function(target){
116 target = Roo.get(target);
118 this.el = Roo.Shadow.Pool.pull();
119 if(this.el.dom.nextSibling != target.dom){
120 this.el.insertBefore(target);
123 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
125 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
128 target.getLeft(true),
133 this.el.dom.style.display = "block";
137 * Returns true if the shadow is visible, else false
139 isVisible : function(){
140 return this.el ? true : false;
144 * Direct alignment when values are already available. Show must be called at least once before
145 * calling this method to ensure it is initialized.
146 * @param {Number} left The target element left position
147 * @param {Number} top The target element top position
148 * @param {Number} width The target element width
149 * @param {Number} height The target element height
151 realign : function(l, t, w, h){
155 var a = this.adjusts, d = this.el.dom, s = d.style;
157 s.left = (l+a.l)+"px";
158 s.top = (t+a.t)+"px";
159 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
161 if(s.width != sws || s.height != shs){
165 var cn = d.childNodes;
166 var sww = Math.max(0, (sw-12))+"px";
167 cn[0].childNodes[1].style.width = sww;
168 cn[1].childNodes[1].style.width = sww;
169 cn[2].childNodes[1].style.width = sww;
170 cn[1].style.height = Math.max(0, (sh-12))+"px";
180 this.el.dom.style.display = "none";
181 Roo.Shadow.Pool.push(this.el);
187 * Adjust the z-index of this shadow
188 * @param {Number} zindex The new z-index
190 setZIndex : function(z){
193 this.el.setStyle("z-index", z);
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
201 var markup = Roo.isIE ?
202 '<div class="x-ie-shadow"></div>' :
203 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
208 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209 sh.autoBoxAdjust = false;
221 * base class for bootstrap elements.
225 Roo.bootstrap = Roo.bootstrap || {};
227 * @class Roo.bootstrap.Component
228 * @extends Roo.Component
230 * @children Roo.bootstrap.Component
231 * Bootstrap Component base class
232 * @cfg {String} cls css class
233 * @cfg {String} style any extra css
234 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
236 * @cfg {string} dataId cutomer id
237 * @cfg {string} name Specifies name attribute
238 * @cfg {string} tooltip Text for the tooltip
239 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
240 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
243 * Do not use directly - it does not do anything..
244 * @param {Object} config The config object
249 Roo.bootstrap.Component = function(config){
250 Roo.bootstrap.Component.superclass.constructor.call(this, config);
254 * @event childrenrendered
255 * Fires when the children have been rendered..
256 * @param {Roo.bootstrap.Component} this
258 "childrenrendered" : true
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
270 allowDomMove : false, // to stop relocations in parent onRender...
280 * Initialize Events for the element
282 initEvents : function() { },
288 can_build_overlaid : true,
290 container_method : false,
297 // returns the parent component..
298 return Roo.ComponentMgr.get(this.parentId)
304 onRender : function(ct, position)
306 // Roo.log("Call onRender: " + this.xtype);
308 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311 if (this.el.attr('xtype')) {
312 this.el.attr('xtypex', this.el.attr('xtype'));
313 this.el.dom.removeAttribute('xtype');
323 var cfg = Roo.apply({}, this.getAutoCreate());
325 cfg.id = this.id || Roo.id();
327 // fill in the extra attributes
328 if (this.xattr && typeof(this.xattr) =='object') {
329 for (var i in this.xattr) {
330 cfg[i] = this.xattr[i];
335 cfg.dataId = this.dataId;
339 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342 if (this.style) { // fixme needs to support more complex style data.
343 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347 cfg.name = this.name;
350 this.el = ct.createChild(cfg, position);
353 this.tooltipEl().attr('tooltip', this.tooltip);
356 if(this.tabIndex !== undefined){
357 this.el.dom.setAttribute('tabIndex', this.tabIndex);
364 * Fetch the element to add children to
365 * @return {Roo.Element} defaults to this.el
367 getChildContainer : function()
371 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373 return Roo.get(document.body);
377 * Fetch the element to display the tooltip on.
378 * @return {Roo.Element} defaults to this.el
380 tooltipEl : function()
385 addxtype : function(tree,cntr)
389 cn = Roo.factory(tree);
390 //Roo.log(['addxtype', cn]);
392 cn.parentType = this.xtype; //??
393 cn.parentId = this.id;
395 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396 if (typeof(cn.container_method) == 'string') {
397 cntr = cn.container_method;
401 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
403 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
405 var build_from_html = Roo.XComponent.build_from_html;
407 var is_body = (tree.xtype == 'Body') ;
409 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411 var self_cntr_el = Roo.get(this[cntr](false));
413 // do not try and build conditional elements
414 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420 return this.addxtypeChild(tree,cntr, is_body);
423 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429 Roo.log('skipping render');
435 if (!build_from_html) {
439 // this i think handles overlaying multiple children of the same type
440 // with the sam eelement.. - which might be buggy..
442 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
448 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459 addxtypeChild : function (tree, cntr, is_body)
461 Roo.debug && Roo.log('addxtypeChild:' + cntr);
463 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467 (typeof(tree['flexy:foreach']) != 'undefined');
471 skip_children = false;
472 // render the element if it's not BODY.
475 // if parent was disabled, then do not try and create the children..
476 if(!this[cntr](true)){
481 cn = Roo.factory(tree);
483 cn.parentType = this.xtype; //??
484 cn.parentId = this.id;
486 var build_from_html = Roo.XComponent.build_from_html;
489 // does the container contain child eleemnts with 'xtype' attributes.
490 // that match this xtype..
491 // note - when we render we create these as well..
492 // so we should check to see if body has xtype set.
493 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495 var self_cntr_el = Roo.get(this[cntr](false));
496 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498 //Roo.log(Roo.XComponent.build_from_html);
499 //Roo.log("got echild:");
502 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503 // and are not displayed -this causes this to use up the wrong element when matching.
504 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
514 //echild.dom.removeAttribute('xtype');
516 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517 Roo.debug && Roo.log(self_cntr_el);
518 Roo.debug && Roo.log(echild);
519 Roo.debug && Roo.log(cn);
525 // if object has flexy:if - then it may or may not be rendered.
526 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
527 // skip a flexy if element.
528 Roo.debug && Roo.log('skipping render');
529 Roo.debug && Roo.log(tree);
531 Roo.debug && Roo.log('skipping all children');
532 skip_children = true;
537 // actually if flexy:foreach is found, we really want to create
538 // multiple copies here...
540 //Roo.log(this[cntr]());
541 // some elements do not have render methods.. like the layouts...
543 if(this[cntr](true) === false){
548 cn.render && cn.render(this[cntr](true));
551 // then add the element..
558 if (typeof (tree.menu) != 'undefined') {
559 tree.menu.parentType = cn.xtype;
560 tree.menu.triggerEl = cn.el;
561 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565 if (!tree.items || !tree.items.length) {
567 //Roo.log(["no children", this]);
572 var items = tree.items;
575 //Roo.log(items.length);
577 if (!skip_children) {
578 for(var i =0;i < items.length;i++) {
579 // Roo.log(['add child', items[i]]);
580 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
586 //Roo.log("fire childrenrendered");
588 cn.fireEvent('childrenrendered', this);
594 * Set the element that will be used to show or hide
596 setVisibilityEl : function(el)
598 this.visibilityEl = el;
602 * Get the element that will be used to show or hide
604 getVisibilityEl : function()
606 if (typeof(this.visibilityEl) == 'object') {
607 return this.visibilityEl;
610 if (typeof(this.visibilityEl) == 'string') {
611 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
618 * Show a component - removes 'hidden' class
622 if(!this.getVisibilityEl()){
626 this.getVisibilityEl().removeClass(['hidden','d-none']);
628 this.fireEvent('show', this);
633 * Hide a component - adds 'hidden' class
637 if(!this.getVisibilityEl()){
641 this.getVisibilityEl().addClass(['hidden','d-none']);
643 this.fireEvent('hide', this);
656 * @class Roo.bootstrap.Element
657 * @extends Roo.bootstrap.Component
658 * @children Roo.bootstrap.Component
659 * Bootstrap Element class (basically a DIV used to make random stuff )
661 * @cfg {String} html contents of the element
662 * @cfg {String} tag tag of the element
663 * @cfg {String} cls class of the element
664 * @cfg {Boolean} preventDefault (true|false) default false
665 * @cfg {Boolean} clickable (true|false) default false
666 * @cfg {String} role default blank - set to button to force cursor pointer
670 * Create a new Element
671 * @param {Object} config The config object
674 Roo.bootstrap.Element = function(config){
675 Roo.bootstrap.Element.superclass.constructor.call(this, config);
681 * When a element is chick
682 * @param {Roo.bootstrap.Element} this
683 * @param {Roo.EventObject} e
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
696 preventDefault: false,
701 getAutoCreate : function(){
705 // cls: this.cls, double assign in parent class Component.js :: onRender
708 if (this.role !== false) {
709 cfg.role = this.role;
715 initEvents: function()
717 Roo.bootstrap.Element.superclass.initEvents.call(this);
720 this.el.on('click', this.onClick, this);
726 onClick : function(e)
728 if(this.preventDefault){
732 this.fireEvent('click', this, e); // why was this double click before?
740 getValue : function()
742 return this.el.dom.innerHTML;
745 setValue : function(value)
747 this.el.dom.innerHTML = value;
762 * @class Roo.bootstrap.DropTarget
763 * @extends Roo.bootstrap.Element
764 * Bootstrap DropTarget class
766 * @cfg {string} name dropable name
769 * Create a new Dropable Area
770 * @param {Object} config The config object
773 Roo.bootstrap.DropTarget = function(config){
774 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
780 * When a element is chick
781 * @param {Roo.bootstrap.Element} this
782 * @param {Roo.EventObject} e
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
791 getAutoCreate : function(){
796 initEvents: function()
798 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802 drop : this.dragDrop.createDelegate(this),
803 enter : this.dragEnter.createDelegate(this),
804 out : this.dragOut.createDelegate(this),
805 over : this.dragOver.createDelegate(this)
809 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812 dragDrop : function(source,e,data)
814 // user has to decide how to impliment this.
817 //this.fireEvent('drop', this, source, e ,data);
821 dragEnter : function(n, dd, e, data)
823 // probably want to resize the element to match the dropped element..
825 this.originalSize = this.el.getSize();
826 this.el.setSize( n.el.getSize());
827 this.dropZone.DDM.refreshCache(this.name);
828 Roo.log([n, dd, e, data]);
831 dragOut : function(value)
833 // resize back to normal
835 this.el.setSize(this.originalSize);
836 this.dropZone.resetConstraints();
839 dragOver : function()
856 * @class Roo.bootstrap.Body
857 * @extends Roo.bootstrap.Component
858 * @children Roo.bootstrap.Component
859 * @parent none builder
860 * Bootstrap Body class
864 * @param {Object} config The config object
867 Roo.bootstrap.Body = function(config){
869 config = config || {};
871 Roo.bootstrap.Body.superclass.constructor.call(this, config);
872 this.el = Roo.get(config.el ? config.el : document.body );
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
880 is_body : true,// just to make sure it's constructed?
885 onRender : function(ct, position)
887 /* Roo.log("Roo.bootstrap.Body - onRender");
888 if (this.cls && this.cls.length) {
889 Roo.get(document.body).addClass(this.cls);
908 * @class Roo.bootstrap.ButtonGroup
909 * @extends Roo.bootstrap.Component
910 * Bootstrap ButtonGroup class
911 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913 * @cfg {String} size lg | sm | xs (default empty normal)
914 * @cfg {String} align vertical | justified (default none)
915 * @cfg {String} direction up | down (default down)
916 * @cfg {Boolean} toolbar false | true
917 * @cfg {Boolean} btn true | false
922 * @param {Object} config The config object
925 Roo.bootstrap.ButtonGroup = function(config){
926 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
937 getAutoCreate : function(){
943 cfg.html = this.html || cfg.html;
954 if (['vertical','justified'].indexOf(this.align)!==-1) {
955 cfg.cls = 'btn-group-' + this.align;
957 if (this.align == 'justified') {
958 console.log(this.items);
962 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963 cfg.cls += ' btn-group-' + this.size;
966 if (this.direction == 'up') {
967 cfg.cls += ' dropup' ;
973 * Add a button to the group (similar to NavItem API.)
975 addItem : function(cfg)
977 var cn = new Roo.bootstrap.Button(cfg);
979 cn.parentId = this.id;
980 cn.onRender(this.el, null);
994 * @class Roo.bootstrap.Button
995 * @extends Roo.bootstrap.Component
996 * Bootstrap Button class
997 * @cfg {String} html The button content
998 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001 * @cfg {String} size (lg|sm|xs)
1002 * @cfg {String} tag (a|input|submit)
1003 * @cfg {String} href empty or href
1004 * @cfg {Boolean} disabled default false;
1005 * @cfg {Boolean} isClose default false;
1006 * @cfg {String} glyphicon depricated - use fa
1007 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008 * @cfg {String} badge text for badge
1009 * @cfg {String} theme (default|glow)
1010 * @cfg {Boolean} inverse dark themed version
1011 * @cfg {Boolean} toggle is it a slidy toggle button
1012 * @cfg {Boolean} pressed default null - if the button ahs active state
1013 * @cfg {String} ontext text for on slidy toggle state
1014 * @cfg {String} offtext text for off slidy toggle state
1015 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1016 * @cfg {Boolean} removeClass remove the standard class..
1017 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1018 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1022 * Create a new button
1023 * @param {Object} config The config object
1027 Roo.bootstrap.Button = function(config){
1028 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1034 * When a button is pressed
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * When a button is double clicked
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1048 * After the button has been toggles
1049 * @param {Roo.bootstrap.Button} btn
1050 * @param {Roo.EventObject} e
1051 * @param {boolean} pressed (also available as button.pressed)
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1078 preventDefault: true,
1087 getAutoCreate : function(){
1095 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097 this.tag = 'button';
1101 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103 if (this.toggle == true) {
1106 cls: 'slider-frame roo-button',
1110 'data-on-text':'ON',
1111 'data-off-text':'OFF',
1112 cls: 'slider-button',
1117 // why are we validating the weights?
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119 cfg.cls += ' ' + this.weight;
1126 cfg.cls += ' close';
1128 cfg["aria-hidden"] = true;
1130 cfg.html = "×";
1136 if (this.theme==='default') {
1137 cfg.cls = 'btn roo-button';
1139 //if (this.parentType != 'Navbar') {
1140 this.weight = this.weight.length ? this.weight : 'default';
1142 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146 cfg.cls += ' btn-' + outline + weight;
1147 if (this.weight == 'default') {
1149 cfg.cls += ' btn-' + this.weight;
1152 } else if (this.theme==='glow') {
1155 cfg.cls = 'btn-glow roo-button';
1157 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159 cfg.cls += ' ' + this.weight;
1165 this.cls += ' inverse';
1169 if (this.active || this.pressed === true) {
1170 cfg.cls += ' active';
1173 if (this.disabled) {
1174 cfg.disabled = 'disabled';
1178 Roo.log('changing to ul' );
1180 this.glyphicon = 'caret';
1181 if (Roo.bootstrap.version == 4) {
1182 this.fa = 'caret-down';
1187 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189 //gsRoo.log(this.parentType);
1190 if (this.parentType === 'Navbar' && !this.parent().bar) {
1191 Roo.log('changing to li?');
1200 href : this.href || '#'
1203 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1204 cfg.cls += ' dropdown';
1211 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1213 if (this.glyphicon) {
1214 cfg.html = ' ' + cfg.html;
1219 cls: 'glyphicon glyphicon-' + this.glyphicon
1224 cfg.html = ' ' + cfg.html;
1229 cls: 'fa fas fa-' + this.fa
1239 // cfg.cls='btn roo-button';
1243 var value = cfg.html;
1248 cls: 'glyphicon glyphicon-' + this.glyphicon,
1255 cls: 'fa fas fa-' + this.fa,
1260 var bw = this.badge_weight.length ? this.badge_weight :
1261 (this.weight.length ? this.weight : 'secondary');
1262 bw = bw == 'default' ? 'secondary' : bw;
1268 cls: 'badge badge-' + bw,
1277 cfg.cls += ' dropdown';
1278 cfg.html = typeof(cfg.html) != 'undefined' ?
1279 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282 if (cfg.tag !== 'a' && this.href !== '') {
1283 throw "Tag must be a to set href.";
1284 } else if (this.href.length > 0) {
1285 cfg.href = this.href;
1288 if(this.removeClass){
1293 cfg.target = this.target;
1298 initEvents: function() {
1299 // Roo.log('init events?');
1300 // Roo.log(this.el.dom);
1303 if (typeof (this.menu) != 'undefined') {
1304 this.menu.parentType = this.xtype;
1305 this.menu.triggerEl = this.el;
1306 this.addxtype(Roo.apply({}, this.menu));
1310 if (this.el.hasClass('roo-button')) {
1311 this.el.on('click', this.onClick, this);
1312 this.el.on('dblclick', this.onDblClick, this);
1314 this.el.select('.roo-button').on('click', this.onClick, this);
1315 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319 if(this.removeClass){
1320 this.el.on('click', this.onClick, this);
1323 if (this.group === true) {
1324 if (this.pressed === false || this.pressed === true) {
1327 this.pressed = false;
1328 this.setActive(this.pressed);
1333 this.el.enableDisplayMode();
1336 onClick : function(e)
1338 if (this.disabled) {
1342 Roo.log('button on click ');
1343 if(this.href === '' || this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2943 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2951 initEvents : function()
2954 Roo.bootstrap.Button.prototype.initEvents.call(this);
2960 this.urlAPI = (window.createObjectURL && window) ||
2961 (window.URL && URL.revokeObjectURL && URL) ||
2962 (window.webkitURL && webkitURL);
2967 cls : 'd-none roo-card-upload-selector'
2970 if (this.multiple) {
2971 im.multiple = 'multiple';
2973 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2975 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * @children Roo.bootstrap.Component
3384 * Bootstrap Header class
3387 * @cfg {String} html content of header
3388 * @cfg {Number} level (1|2|3|4|5|6) default 1
3391 * Create a new Header
3392 * @param {Object} config The config object
3396 Roo.bootstrap.Header = function(config){
3397 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3408 getAutoCreate : function(){
3413 tag: 'h' + (1 *this.level),
3414 html: this.html || ''
3425 * @class Roo.bootstrap.MenuMgr
3427 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430 Roo.bootstrap.menu.Manager = function(){
3431 var menus, active, groups = {}, attached = false, lastShow = new Date();
3433 // private - called when first menu is created
3436 active = new Roo.util.MixedCollection();
3437 Roo.get(document).addKeyListener(27, function(){
3438 if(active.length > 0){
3446 if(active && active.length > 0){
3447 var c = active.clone();
3457 if(active.length < 1){
3458 Roo.get(document).un("mouseup", onMouseDown);
3466 var last = active.last();
3467 lastShow = new Date();
3470 Roo.get(document).on("mouseup", onMouseDown);
3475 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476 m.parentMenu.activeChild = m;
3477 }else if(last && last.isVisible()){
3478 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3483 function onBeforeHide(m){
3485 m.activeChild.hide();
3487 if(m.autoHideTimer){
3488 clearTimeout(m.autoHideTimer);
3489 delete m.autoHideTimer;
3494 function onBeforeShow(m){
3495 var pm = m.parentMenu;
3496 if(!pm && !m.allowOtherMenus){
3498 }else if(pm && pm.activeChild && active != m){
3499 pm.activeChild.hide();
3503 // private this should really trigger on mouseup..
3504 function onMouseDown(e){
3505 Roo.log("on Mouse Up");
3507 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508 Roo.log("MenuManager hideAll");
3517 function onBeforeCheck(mi, state){
3519 var g = groups[mi.group];
3520 for(var i = 0, l = g.length; i < l; i++){
3522 g[i].setChecked(false);
3531 * Hides all menus that are currently visible
3533 hideAll : function(){
3538 register : function(menu){
3542 menus[menu.id] = menu;
3543 menu.on("beforehide", onBeforeHide);
3544 menu.on("hide", onHide);
3545 menu.on("beforeshow", onBeforeShow);
3546 menu.on("show", onShow);
3548 if(g && menu.events["checkchange"]){
3552 groups[g].push(menu);
3553 menu.on("checkchange", onCheck);
3558 * Returns a {@link Roo.menu.Menu} object
3559 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560 * be used to generate and return a new Menu instance.
3562 get : function(menu){
3563 if(typeof menu == "string"){ // menu id
3565 }else if(menu.events){ // menu instance
3568 /*else if(typeof menu.length == 'number'){ // array of menu items?
3569 return new Roo.bootstrap.Menu({items:menu});
3570 }else{ // otherwise, must be a config
3571 return new Roo.bootstrap.Menu(menu);
3578 unregister : function(menu){
3579 delete menus[menu.id];
3580 menu.un("beforehide", onBeforeHide);
3581 menu.un("hide", onHide);
3582 menu.un("beforeshow", onBeforeShow);
3583 menu.un("show", onShow);
3585 if(g && menu.events["checkchange"]){
3586 groups[g].remove(menu);
3587 menu.un("checkchange", onCheck);
3592 registerCheckable : function(menuItem){
3593 var g = menuItem.group;
3598 groups[g].push(menuItem);
3599 menuItem.on("beforecheckchange", onBeforeCheck);
3604 unregisterCheckable : function(menuItem){
3605 var g = menuItem.group;
3607 groups[g].remove(menuItem);
3608 menuItem.un("beforecheckchange", onBeforeCheck);
3614 * @class Roo.bootstrap.menu.Menu
3615 * @extends Roo.bootstrap.Component
3617 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3619 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3621 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622 * @cfg {bool} hidden if the menu should be hidden when rendered.
3623 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3624 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3626 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3630 * @param {Object} config The config objectQ
3634 Roo.bootstrap.menu.Menu = function(config){
3636 if (config.type == 'treeview') {
3637 // normally menu's are drawn attached to the document to handle layering etc..
3638 // however treeview (used by the docs menu is drawn into the parent element)
3639 this.container_method = 'getChildContainer';
3642 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643 if (this.registerMenu && this.type != 'treeview') {
3644 Roo.bootstrap.menu.Manager.register(this);
3651 * Fires before this menu is displayed (return false to block)
3652 * @param {Roo.menu.Menu} this
3657 * Fires before this menu is hidden (return false to block)
3658 * @param {Roo.menu.Menu} this
3663 * Fires after this menu is displayed
3664 * @param {Roo.menu.Menu} this
3669 * Fires after this menu is hidden
3670 * @param {Roo.menu.Menu} this
3675 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678 * @param {Roo.EventObject} e
3683 * Fires when the mouse is hovering over this menu
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.EventObject} e
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691 * Fires when the mouse exits this menu
3692 * @param {Roo.menu.Menu} this
3693 * @param {Roo.EventObject} e
3694 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699 * Fires when a menu item contained in this menu is clicked
3700 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701 * @param {Roo.EventObject} e
3705 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3712 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3715 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717 registerMenu : true,
3719 menuItems :false, // stores the menu items..
3729 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731 hideTrigger : false,
3736 getChildContainer : function() {
3740 getAutoCreate : function(){
3742 //if (['right'].indexOf(this.align)!==-1) {
3743 // cfg.cn[1].cls += ' pull-right'
3748 cls : 'dropdown-menu shadow' ,
3749 style : 'z-index:1000'
3753 if (this.type === 'submenu') {
3754 cfg.cls = 'submenu active';
3756 if (this.type === 'treeview') {
3757 cfg.cls = 'treeview-menu';
3762 initEvents : function() {
3764 // Roo.log("ADD event");
3765 // Roo.log(this.triggerEl.dom);
3766 if (this.triggerEl) {
3768 this.triggerEl.on('click', this.onTriggerClick, this);
3770 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772 if (!this.hideTrigger) {
3773 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774 // dropdown toggle on the 'a' in BS4?
3775 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777 this.triggerEl.addClass('dropdown-toggle');
3783 this.el.on('touchstart' , this.onTouch, this);
3785 this.el.on('click' , this.onClick, this);
3787 this.el.on("mouseover", this.onMouseOver, this);
3788 this.el.on("mouseout", this.onMouseOut, this);
3792 findTargetItem : function(e)
3794 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3798 //Roo.log(t); Roo.log(t.id);
3800 //Roo.log(this.menuitems);
3801 return this.menuitems.get(t.id);
3803 //return this.items.get(t.menuItemId);
3809 onTouch : function(e)
3811 Roo.log("menu.onTouch");
3812 //e.stopEvent(); this make the user popdown broken
3816 onClick : function(e)
3818 Roo.log("menu.onClick");
3820 var t = this.findTargetItem(e);
3821 if(!t || t.isContainer){
3826 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3827 if(t == this.activeItem && t.shouldDeactivate(e)){
3828 this.activeItem.deactivate();
3829 delete this.activeItem;
3833 this.setActiveItem(t, true);
3841 Roo.log('pass click event');
3845 this.fireEvent("click", this, t, e);
3849 if(!t.href.length || t.href == '#'){
3850 (function() { _this.hide(); }).defer(100);
3855 onMouseOver : function(e){
3856 var t = this.findTargetItem(e);
3859 // if(t.canActivate && !t.disabled){
3860 // this.setActiveItem(t, true);
3864 this.fireEvent("mouseover", this, e, t);
3866 isVisible : function(){
3867 return !this.hidden;
3869 onMouseOut : function(e){
3870 var t = this.findTargetItem(e);
3873 // if(t == this.activeItem && t.shouldDeactivate(e)){
3874 // this.activeItem.deactivate();
3875 // delete this.activeItem;
3878 this.fireEvent("mouseout", this, e, t);
3883 * Displays this menu relative to another element
3884 * @param {String/HTMLElement/Roo.Element} element The element to align to
3885 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886 * the element (defaults to this.defaultAlign)
3887 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889 show : function(el, pos, parentMenu)
3891 if (false === this.fireEvent("beforeshow", this)) {
3892 Roo.log("show canceled");
3895 this.parentMenu = parentMenu;
3899 this.el.addClass('show'); // show otherwise we do not know how big we are..
3901 var xy = this.el.getAlignToXY(el, pos);
3903 // bl-tl << left align below
3904 // tl-bl << left align
3906 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907 // if it goes to far to the right.. -> align left.
3908 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911 // was left align - go right?
3912 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915 // goes down the bottom
3916 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918 var a = this.align.replace('?', '').split('-');
3919 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3923 this.showAt( xy , parentMenu, false);
3926 * Displays this menu at a specific xy position
3927 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930 showAt : function(xy, parentMenu, /* private: */_e){
3931 this.parentMenu = parentMenu;
3936 this.fireEvent("beforeshow", this);
3937 //xy = this.el.adjustForConstraints(xy);
3941 this.hideMenuItems();
3942 this.hidden = false;
3943 if (this.triggerEl) {
3944 this.triggerEl.addClass('open');
3947 this.el.addClass('show');
3951 // reassign x when hitting right
3953 // reassign y when hitting bottom
3955 // but the list may align on trigger left or trigger top... should it be a properity?
3957 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3962 this.fireEvent("show", this);
3968 this.doFocus.defer(50, this);
3972 doFocus : function(){
3974 this.focusEl.focus();
3979 * Hides this menu and optionally all parent menus
3980 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982 hide : function(deep)
3984 if (false === this.fireEvent("beforehide", this)) {
3985 Roo.log("hide canceled");
3988 this.hideMenuItems();
3989 if(this.el && this.isVisible()){
3991 if(this.activeItem){
3992 this.activeItem.deactivate();
3993 this.activeItem = null;
3995 if (this.triggerEl) {
3996 this.triggerEl.removeClass('open');
3999 this.el.removeClass('show');
4001 this.fireEvent("hide", this);
4003 if(deep === true && this.parentMenu){
4004 this.parentMenu.hide(true);
4008 onTriggerClick : function(e)
4010 Roo.log('trigger click');
4012 var target = e.getTarget();
4014 Roo.log(target.nodeName.toLowerCase());
4016 if(target.nodeName.toLowerCase() === 'i'){
4022 onTriggerPress : function(e)
4024 Roo.log('trigger press');
4025 //Roo.log(e.getTarget());
4026 // Roo.log(this.triggerEl.dom);
4028 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029 var pel = Roo.get(e.getTarget());
4030 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031 Roo.log('is treeview or dropdown?');
4035 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039 if (this.isVisible()) {
4045 this.show(this.triggerEl, this.align, false);
4048 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4055 hideMenuItems : function()
4057 Roo.log("hide Menu Items");
4062 this.el.select('.open',true).each(function(aa) {
4064 aa.removeClass('open');
4068 addxtypeChild : function (tree, cntr) {
4069 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071 this.menuitems.add(comp);
4083 this.getEl().dom.innerHTML = '';
4084 this.menuitems.clear();
4090 * @class Roo.bootstrap.menu.Item
4091 * @extends Roo.bootstrap.Component
4092 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093 * @parent Roo.bootstrap.menu.Menu
4095 * Bootstrap MenuItem class
4097 * @cfg {String} html the menu label
4098 * @cfg {String} href the link
4099 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101 * @cfg {Boolean} active used on sidebars to highlight active itesm
4102 * @cfg {String} fa favicon to show on left of menu item.
4103 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107 * Create a new MenuItem
4108 * @param {Object} config The config object
4112 Roo.bootstrap.menu.Item = function(config){
4113 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4118 * The raw click event for the entire grid.
4119 * @param {Roo.bootstrap.menu.Item} this
4120 * @param {Roo.EventObject} e
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4130 preventDefault: false,
4131 isContainer : false,
4135 getAutoCreate : function(){
4137 if(this.isContainer){
4140 cls: 'dropdown-menu-item '
4150 cls : 'dropdown-item',
4155 if (this.fa !== false) {
4158 cls : 'fa fa-' + this.fa
4167 cls: 'dropdown-menu-item',
4170 if (this.parent().type == 'treeview') {
4171 cfg.cls = 'treeview-menu';
4174 cfg.cls += ' active';
4179 anc.href = this.href || cfg.cn[0].href ;
4180 ctag.html = this.html || cfg.cn[0].html ;
4184 initEvents: function()
4186 if (this.parent().type == 'treeview') {
4187 this.el.select('a').on('click', this.onClick, this);
4191 this.menu.parentType = this.xtype;
4192 this.menu.triggerEl = this.el;
4193 this.menu = this.addxtype(Roo.apply({}, this.menu));
4197 onClick : function(e)
4199 //Roo.log('item on click ');
4201 if(this.href === false || this.preventDefault){
4204 //this.parent().hideMenuItems();
4206 this.fireEvent('click', this, e);
4220 * @class Roo.bootstrap.menu.Separator
4221 * @extends Roo.bootstrap.Component
4223 * @parent Roo.bootstrap.menu.Menu
4224 * Bootstrap Separator class
4227 * Create a new Separator
4228 * @param {Object} config The config object
4232 Roo.bootstrap.menu.Separator = function(config){
4233 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4238 getAutoCreate : function(){
4241 cls: 'dropdown-divider divider'
4257 * @class Roo.bootstrap.Modal
4258 * @extends Roo.bootstrap.Component
4259 * @parent none builder
4260 * @children Roo.bootstrap.Component
4261 * Bootstrap Modal class
4262 * @cfg {String} title Title of dialog
4263 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4265 * @cfg {Boolean} specificTitle default false
4266 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268 * @cfg {Boolean} animate default true
4269 * @cfg {Boolean} allow_close default true
4270 * @cfg {Boolean} fitwindow default false
4271 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274 * @cfg {String} size (sm|lg|xl) default empty
4275 * @cfg {Number} max_width set the max width of modal
4276 * @cfg {Boolean} editableTitle can the title be edited
4281 * Create a new Modal Dialog
4282 * @param {Object} config The config object
4285 Roo.bootstrap.Modal = function(config){
4286 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291 * The raw btnclick event for the button
4292 * @param {Roo.EventObject} e
4297 * Fire when dialog resize
4298 * @param {Roo.bootstrap.Modal} this
4299 * @param {Roo.EventObject} e
4303 * @event titlechanged
4304 * Fire when the editable title has been changed
4305 * @param {Roo.bootstrap.Modal} this
4306 * @param {Roo.EventObject} value
4308 "titlechanged" : true
4311 this.buttons = this.buttons || [];
4314 this.tmpl = Roo.factory(this.tmpl);
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4321 title : 'test dialog',
4331 specificTitle: false,
4333 buttonPosition: 'right',
4355 editableTitle : false,
4357 onRender : function(ct, position)
4359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362 var cfg = Roo.apply({}, this.getAutoCreate());
4365 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367 //if (!cfg.name.length) {
4371 cfg.cls += ' ' + this.cls;
4374 cfg.style = this.style;
4376 this.el = Roo.get(document.body).createChild(cfg, position);
4378 //var type = this.el.dom.type;
4381 if(this.tabIndex !== undefined){
4382 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385 this.dialogEl = this.el.select('.modal-dialog',true).first();
4386 this.bodyEl = this.el.select('.modal-body',true).first();
4387 this.closeEl = this.el.select('.modal-header .close', true).first();
4388 this.headerEl = this.el.select('.modal-header',true).first();
4389 this.titleEl = this.el.select('.modal-title',true).first();
4390 this.footerEl = this.el.select('.modal-footer',true).first();
4392 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394 //this.el.addClass("x-dlg-modal");
4396 if (this.buttons.length) {
4397 Roo.each(this.buttons, function(bb) {
4398 var b = Roo.apply({}, bb);
4399 b.xns = b.xns || Roo.bootstrap;
4400 b.xtype = b.xtype || 'Button';
4401 if (typeof(b.listeners) == 'undefined') {
4402 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4405 var btn = Roo.factory(b);
4407 btn.render(this.getButtonContainer());
4411 // render the children.
4414 if(typeof(this.items) != 'undefined'){
4415 var items = this.items;
4418 for(var i =0;i < items.length;i++) {
4419 // we force children not to montor widnow resize - as we do that for them.
4420 items[i].monitorWindowResize = false;
4421 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4548 initEvents : function()
4550 if (this.allow_close) {
4551 this.closeEl.on('click', this.hide, this);
4553 Roo.EventManager.onWindowResize(this.resize, this, true);
4554 if (this.editableTitle) {
4555 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4556 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557 this.headerEditEl.on('keyup', function(e) {
4558 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559 this.toggleHeaderInput(false)
4562 this.headerEditEl.on('blur', function(e) {
4563 this.toggleHeaderInput(false)
4572 this.maskEl.setSize(
4573 Roo.lib.Dom.getViewWidth(true),
4574 Roo.lib.Dom.getViewHeight(true)
4577 if (this.fitwindow) {
4579 this.dialogEl.setStyle( { 'max-width' : '100%' });
4581 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4587 if(this.max_width !== 0) {
4589 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592 this.setSize(w, this.height);
4596 if(this.max_height) {
4597 this.setSize(w,Math.min(
4599 Roo.lib.Dom.getViewportHeight(true) - 60
4605 if(!this.fit_content) {
4606 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4610 this.setSize(w, Math.min(
4612 this.headerEl.getHeight() +
4613 this.footerEl.getHeight() +
4614 this.getChildHeight(this.bodyEl.dom.childNodes),
4615 Roo.lib.Dom.getViewportHeight(true) - 60)
4621 setSize : function(w,h)
4628 // any layout/border etc.. resize..
4630 this.items.forEach( function(e) {
4631 e.layout ? e.layout() : false;
4640 if (!this.rendered) {
4643 this.toggleHeaderInput(false);
4644 //this.el.setStyle('display', 'block');
4645 this.el.removeClass('hideing');
4646 this.el.dom.style.display='block';
4648 Roo.get(document.body).addClass('modal-open');
4650 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4653 this.el.addClass('show');
4654 this.el.addClass('in');
4657 this.el.addClass('show');
4658 this.el.addClass('in');
4661 // not sure how we can show data in here..
4663 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4666 Roo.get(document.body).addClass("x-body-masked");
4668 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4669 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670 this.maskEl.dom.style.display = 'block';
4671 this.maskEl.addClass('show');
4676 this.fireEvent('show', this);
4678 // set zindex here - otherwise it appears to be ignored...
4679 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4682 // this is for children that are... layout.Border
4684 this.items.forEach( function(e) {
4685 e.layout ? e.layout() : false;
4693 if(this.fireEvent("beforehide", this) !== false){
4695 this.maskEl.removeClass('show');
4697 this.maskEl.dom.style.display = '';
4698 Roo.get(document.body).removeClass("x-body-masked");
4699 this.el.removeClass('in');
4700 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4702 if(this.animate){ // why
4703 this.el.addClass('hideing');
4704 this.el.removeClass('show');
4706 if (!this.el.hasClass('hideing')) {
4707 return; // it's been shown again...
4710 this.el.dom.style.display='';
4712 Roo.get(document.body).removeClass('modal-open');
4713 this.el.removeClass('hideing');
4717 this.el.removeClass('show');
4718 this.el.dom.style.display='';
4719 Roo.get(document.body).removeClass('modal-open');
4722 this.fireEvent('hide', this);
4725 isVisible : function()
4728 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4732 addButton : function(str, cb)
4736 var b = Roo.apply({}, { html : str } );
4737 b.xns = b.xns || Roo.bootstrap;
4738 b.xtype = b.xtype || 'Button';
4739 if (typeof(b.listeners) == 'undefined') {
4740 b.listeners = { click : cb.createDelegate(this) };
4743 var btn = Roo.factory(b);
4745 btn.render(this.getButtonContainer());
4751 setDefaultButton : function(btn)
4753 //this.el.select('.modal-footer').()
4756 resizeTo: function(w,h)
4758 this.dialogEl.setWidth(w);
4760 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4762 this.bodyEl.setHeight(h - diff);
4764 this.fireEvent('resize', this);
4767 setContentSize : function(w, h)
4771 onButtonClick: function(btn,e)
4774 this.fireEvent('btnclick', btn.name, e);
4777 * Set the title of the Dialog
4778 * @param {String} str new Title
4780 setTitle: function(str) {
4781 this.titleEl.dom.innerHTML = str;
4785 * Set the body of the Dialog
4786 * @param {String} str new Title
4788 setBody: function(str) {
4789 this.bodyEl.dom.innerHTML = str;
4792 * Set the body of the Dialog using the template
4793 * @param {Obj} data - apply this data to the template and replace the body contents.
4795 applyBody: function(obj)
4798 Roo.log("Error - using apply Body without a template");
4801 this.tmpl.overwrite(this.bodyEl, obj);
4804 getChildHeight : function(child_nodes)
4808 child_nodes.length == 0
4813 var child_height = 0;
4815 for(var i = 0; i < child_nodes.length; i++) {
4818 * for modal with tabs...
4819 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4821 var layout_childs = child_nodes[i].childNodes;
4823 for(var j = 0; j < layout_childs.length; j++) {
4825 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4827 var layout_body_childs = layout_childs[j].childNodes;
4829 for(var k = 0; k < layout_body_childs.length; k++) {
4831 if(layout_body_childs[k].classList.contains('navbar')) {
4832 child_height += layout_body_childs[k].offsetHeight;
4836 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4838 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4840 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4842 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4858 child_height += child_nodes[i].offsetHeight;
4859 // Roo.log(child_nodes[i].offsetHeight);
4862 return child_height;
4864 toggleHeaderInput : function(is_edit)
4866 if (!this.editableTitle) {
4867 return; // not editable.
4869 if (is_edit && this.is_header_editing) {
4870 return; // already editing..
4874 this.headerEditEl.dom.value = this.title;
4875 this.headerEditEl.removeClass('d-none');
4876 this.headerEditEl.dom.focus();
4877 this.titleEl.addClass('d-none');
4879 this.is_header_editing = true;
4882 // flip back to not editing.
4883 this.title = this.headerEditEl.dom.value;
4884 this.headerEditEl.addClass('d-none');
4885 this.titleEl.removeClass('d-none');
4886 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887 this.is_header_editing = false;
4888 this.fireEvent('titlechanged', this, this.title);
4897 Roo.apply(Roo.bootstrap.Modal, {
4899 * Button config that displays a single OK button
4908 * Button config that displays Yes and No buttons
4924 * Button config that displays OK and Cancel buttons
4939 * Button config that displays Yes, No and Cancel buttons
4964 * messagebox - can be used as a replace
4968 * @class Roo.MessageBox
4969 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4978 // process text value...
4982 // Show a dialog using config options:
4984 title:'Save Changes?',
4985 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986 buttons: Roo.Msg.YESNOCANCEL,
4993 Roo.bootstrap.MessageBox = function(){
4994 var dlg, opt, mask, waitTimer;
4995 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996 var buttons, activeTextEl, bwidth;
5000 var handleButton = function(button){
5002 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5006 var handleHide = function(){
5008 dlg.el.removeClass(opt.cls);
5011 // Roo.TaskMgr.stop(waitTimer);
5012 // waitTimer = null;
5017 var updateButtons = function(b){
5020 buttons["ok"].hide();
5021 buttons["cancel"].hide();
5022 buttons["yes"].hide();
5023 buttons["no"].hide();
5024 dlg.footerEl.hide();
5028 dlg.footerEl.show();
5029 for(var k in buttons){
5030 if(typeof buttons[k] != "function"){
5033 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034 width += buttons[k].el.getWidth()+15;
5044 var handleEsc = function(d, k, e){
5045 if(opt && opt.closable !== false){
5055 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056 * @return {Roo.BasicDialog} The BasicDialog element
5058 getDialog : function(){
5060 dlg = new Roo.bootstrap.Modal( {
5063 //constraintoviewport:false,
5065 //collapsible : false,
5070 //buttonAlign:"center",
5071 closeClick : function(){
5072 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5075 handleButton("cancel");
5080 dlg.on("hide", handleHide);
5082 //dlg.addKeyListener(27, handleEsc);
5084 this.buttons = buttons;
5085 var bt = this.buttonText;
5086 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5091 bodyEl = dlg.bodyEl.createChild({
5093 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094 '<textarea class="roo-mb-textarea"></textarea>' +
5095 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5097 msgEl = bodyEl.dom.firstChild;
5098 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099 textboxEl.enableDisplayMode();
5100 textboxEl.addKeyListener([10,13], function(){
5101 if(dlg.isVisible() && opt && opt.buttons){
5104 }else if(opt.buttons.yes){
5105 handleButton("yes");
5109 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110 textareaEl.enableDisplayMode();
5111 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112 progressEl.enableDisplayMode();
5114 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115 var pf = progressEl.dom.firstChild;
5117 pp = Roo.get(pf.firstChild);
5118 pp.setHeight(pf.offsetHeight);
5126 * Updates the message box body text
5127 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128 * the XHTML-compliant non-breaking space character '&#160;')
5129 * @return {Roo.MessageBox} This message box
5131 updateText : function(text)
5133 if(!dlg.isVisible() && !opt.width){
5134 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5137 msgEl.innerHTML = text || ' ';
5139 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5142 Math.min(opt.width || cw , this.maxWidth),
5143 Math.max(opt.minWidth || this.minWidth, bwidth)
5146 activeTextEl.setWidth(w);
5148 if(dlg.isVisible()){
5149 dlg.fixedcenter = false;
5151 // to big, make it scroll. = But as usual stupid IE does not support
5154 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5158 bodyEl.dom.style.height = '';
5159 bodyEl.dom.style.overflowY = '';
5162 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5164 bodyEl.dom.style.overflowX = '';
5167 dlg.setContentSize(w, bodyEl.getHeight());
5168 if(dlg.isVisible()){
5169 dlg.fixedcenter = true;
5175 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5176 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179 * @return {Roo.MessageBox} This message box
5181 updateProgress : function(value, text){
5183 this.updateText(text);
5186 if (pp) { // weird bug on my firefox - for some reason this is not defined
5187 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5194 * Returns true if the message box is currently displayed
5195 * @return {Boolean} True if the message box is visible, else false
5197 isVisible : function(){
5198 return dlg && dlg.isVisible();
5202 * Hides the message box if it is displayed
5205 if(this.isVisible()){
5211 * Displays a new message box, or reinitializes an existing message box, based on the config options
5212 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213 * The following config object properties are supported:
5215 Property Type Description
5216 ---------- --------------- ------------------------------------------------------------------------------------
5217 animEl String/Element An id or Element from which the message box should animate as it opens and
5218 closes (defaults to undefined)
5219 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable Boolean False to hide the top-right close button (defaults to true). Note that
5222 progress and wait dialogs will ignore this property and always hide the
5223 close button as they can only be closed programmatically.
5224 cls String A custom CSS class to apply to the message box element
5225 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5226 displayed (defaults to 75)
5227 fn Function A callback function to execute after closing the dialog. The arguments to the
5228 function will be btn (the name of the button that was clicked, if applicable,
5229 e.g. "ok"), and text (the value of the active text field, if applicable).
5230 Progress and wait dialogs will ignore this option since they do not respond to
5231 user actions and can only be closed programmatically, so any required function
5232 should be called by the same code after it closes the dialog.
5233 icon String A CSS class that provides a background image to be used as an icon for
5234 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5236 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5237 modal Boolean False to allow user interaction with the page while the message box is
5238 displayed (defaults to true)
5239 msg String A string that will replace the existing message box body text (defaults
5240 to the XHTML-compliant non-breaking space character ' ')
5241 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5242 progress Boolean True to display a progress bar (defaults to false)
5243 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5246 title String The title text
5247 value String The string value to set into the active textbox element if displayed
5248 wait Boolean True to display a progress bar (defaults to false)
5249 width Number The width of the dialog in pixels
5256 msg: 'Please enter your address:',
5258 buttons: Roo.MessageBox.OKCANCEL,
5261 animEl: 'addAddressBtn'
5264 * @param {Object} config Configuration options
5265 * @return {Roo.MessageBox} This message box
5267 show : function(options)
5270 // this causes nightmares if you show one dialog after another
5271 // especially on callbacks..
5273 if(this.isVisible()){
5276 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5278 Roo.log("New Dialog Message:" + options.msg )
5279 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5283 var d = this.getDialog();
5285 d.setTitle(opt.title || " ");
5286 d.closeEl.setDisplayed(opt.closable !== false);
5287 activeTextEl = textboxEl;
5288 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5293 textareaEl.setHeight(typeof opt.multiline == "number" ?
5294 opt.multiline : this.defaultTextHeight);
5295 activeTextEl = textareaEl;
5304 progressEl.setDisplayed(opt.progress === true);
5306 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5308 this.updateProgress(0);
5309 activeTextEl.dom.value = opt.value || "";
5311 dlg.setDefaultButton(activeTextEl);
5313 var bs = opt.buttons;
5317 }else if(bs && bs.yes){
5318 db = buttons["yes"];
5320 dlg.setDefaultButton(db);
5322 bwidth = updateButtons(opt.buttons);
5323 this.updateText(opt.msg);
5325 d.el.addClass(opt.cls);
5327 d.proxyDrag = opt.proxyDrag === true;
5328 d.modal = opt.modal !== false;
5329 d.mask = opt.modal !== false ? mask : false;
5331 // force it to the end of the z-index stack so it gets a cursor in FF
5332 document.body.appendChild(dlg.el.dom);
5333 d.animateTarget = null;
5334 d.show(options.animEl);
5340 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5341 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342 * and closing the message box when the process is complete.
5343 * @param {String} title The title bar text
5344 * @param {String} msg The message box body text
5345 * @return {Roo.MessageBox} This message box
5347 progress : function(title, msg){
5354 minWidth: this.minProgressWidth,
5361 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362 * If a callback function is passed it will be called after the user clicks the button, and the
5363 * id of the button that was clicked will be passed as the only parameter to the callback
5364 * (could also be the top-right close button).
5365 * @param {String} title The title bar text
5366 * @param {String} msg The message box body text
5367 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368 * @param {Object} scope (optional) The scope of the callback function
5369 * @return {Roo.MessageBox} This message box
5371 alert : function(title, msg, fn, scope)
5386 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5387 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388 * You are responsible for closing the message box when the process is complete.
5389 * @param {String} msg The message box body text
5390 * @param {String} title (optional) The title bar text
5391 * @return {Roo.MessageBox} This message box
5393 wait : function(msg, title){
5404 waitTimer = Roo.TaskMgr.start({
5406 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5414 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417 * @param {String} title The title bar text
5418 * @param {String} msg The message box body text
5419 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420 * @param {Object} scope (optional) The scope of the callback function
5421 * @return {Roo.MessageBox} This message box
5423 confirm : function(title, msg, fn, scope){
5427 buttons: this.YESNO,
5436 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5438 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439 * (could also be the top-right close button) and the text that was entered will be passed as the two
5440 * parameters to the callback.
5441 * @param {String} title The title bar text
5442 * @param {String} msg The message box body text
5443 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444 * @param {Object} scope (optional) The scope of the callback function
5445 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447 * @return {Roo.MessageBox} This message box
5449 prompt : function(title, msg, fn, scope, multiline){
5453 buttons: this.OKCANCEL,
5458 multiline: multiline,
5465 * Button config that displays a single OK button
5470 * Button config that displays Yes and No buttons
5473 YESNO : {yes:true, no:true},
5475 * Button config that displays OK and Cancel buttons
5478 OKCANCEL : {ok:true, cancel:true},
5480 * Button config that displays Yes, No and Cancel buttons
5483 YESNOCANCEL : {yes:true, no:true, cancel:true},
5486 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5489 defaultTextHeight : 75,
5491 * The maximum width in pixels of the message box (defaults to 600)
5496 * The minimum width in pixels of the message box (defaults to 100)
5501 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5502 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5505 minProgressWidth : 250,
5507 * An object containing the default button text strings that can be overriden for localized language support.
5508 * Supported properties are: ok, cancel, yes and no.
5509 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5522 * Shorthand for {@link Roo.MessageBox}
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5534 * @class Roo.bootstrap.nav.Bar
5535 * @extends Roo.bootstrap.Component
5537 * Bootstrap Navbar class
5540 * Create a new Navbar
5541 * @param {Object} config The config object
5545 Roo.bootstrap.nav.Bar = function(config){
5546 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5550 * @event beforetoggle
5551 * Fire before toggle the menu
5552 * @param {Roo.EventObject} e
5554 "beforetoggle" : true
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5567 getAutoCreate : function(){
5570 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5574 initEvents :function ()
5576 //Roo.log(this.el.select('.navbar-toggle',true));
5577 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5584 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5586 var size = this.el.getSize();
5587 this.maskEl.setSize(size.width, size.height);
5588 this.maskEl.enableDisplayMode("block");
5597 getChildContainer : function()
5599 if (this.el && this.el.select('.collapse').getCount()) {
5600 return this.el.select('.collapse',true).first();
5615 onToggle : function()
5618 if(this.fireEvent('beforetoggle', this) === false){
5621 var ce = this.el.select('.navbar-collapse',true).first();
5623 if (!ce.hasClass('show')) {
5633 * Expand the navbar pulldown
5635 expand : function ()
5638 var ce = this.el.select('.navbar-collapse',true).first();
5639 if (ce.hasClass('collapsing')) {
5642 ce.dom.style.height = '';
5644 ce.addClass('in'); // old...
5645 ce.removeClass('collapse');
5646 ce.addClass('show');
5647 var h = ce.getHeight();
5649 ce.removeClass('show');
5650 // at this point we should be able to see it..
5651 ce.addClass('collapsing');
5653 ce.setHeight(0); // resize it ...
5654 ce.on('transitionend', function() {
5655 //Roo.log('done transition');
5656 ce.removeClass('collapsing');
5657 ce.addClass('show');
5658 ce.removeClass('collapse');
5660 ce.dom.style.height = '';
5661 }, this, { single: true} );
5663 ce.dom.scrollTop = 0;
5666 * Collapse the navbar pulldown
5668 collapse : function()
5670 var ce = this.el.select('.navbar-collapse',true).first();
5672 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673 // it's collapsed or collapsing..
5676 ce.removeClass('in'); // old...
5677 ce.setHeight(ce.getHeight());
5678 ce.removeClass('show');
5679 ce.addClass('collapsing');
5681 ce.on('transitionend', function() {
5682 ce.dom.style.height = '';
5683 ce.removeClass('collapsing');
5684 ce.addClass('collapse');
5685 }, this, { single: true} );
5705 * @class Roo.bootstrap.nav.Simplebar
5706 * @extends Roo.bootstrap.nav.Bar
5707 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708 * Bootstrap Sidebar class
5710 * @cfg {Boolean} inverse is inverted color
5712 * @cfg {String} type (nav | pills | tabs)
5713 * @cfg {Boolean} arrangement stacked | justified
5714 * @cfg {String} align (left | right) alignment
5716 * @cfg {Boolean} main (true|false) main nav bar? default false
5717 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5719 * @cfg {String} tag (header|footer|nav|div) default is nav
5721 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5725 * Create a new Sidebar
5726 * @param {Object} config The config object
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5750 getAutoCreate : function(){
5754 tag : this.tag || 'div',
5755 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5757 if (['light','white'].indexOf(this.weight) > -1) {
5758 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5760 cfg.cls += ' bg-' + this.weight;
5763 cfg.cls += ' navbar-inverse';
5767 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5769 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5778 cls: 'nav nav-' + this.xtype,
5784 this.type = this.type || 'nav';
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cn[0].cls += ' nav-' + this.type
5790 if (this.type!=='nav') {
5791 Roo.log('nav type must be nav/tabs/pills')
5793 cfg.cn[0].cls += ' navbar-nav'
5799 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800 cfg.cn[0].cls += ' nav-' + this.arrangement;
5804 if (this.align === 'right') {
5805 cfg.cn[0].cls += ' navbar-right';
5830 * navbar-expand-md fixed-top
5834 * @class Roo.bootstrap.nav.Headerbar
5835 * @extends Roo.bootstrap.nav.Simplebar
5836 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837 * Bootstrap Sidebar class
5839 * @cfg {String} brand what is brand
5840 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841 * @cfg {String} brand_href href of the brand
5842 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5843 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5848 * Create a new Sidebar
5849 * @param {Object} config The config object
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5865 desktopCenter : false,
5868 getAutoCreate : function(){
5871 tag: this.nav || 'nav',
5872 cls: 'navbar navbar-expand-md',
5878 if (this.desktopCenter) {
5879 cn.push({cls : 'container', cn : []});
5887 cls: 'navbar-toggle navbar-toggler',
5888 'data-toggle': 'collapse',
5893 html: 'Toggle navigation'
5897 cls: 'icon-bar navbar-toggler-icon'
5910 cn.push( Roo.bootstrap.version == 4 ? btn : {
5912 cls: 'navbar-header',
5921 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5925 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5927 if (['light','white'].indexOf(this.weight) > -1) {
5928 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5930 cfg.cls += ' bg-' + this.weight;
5933 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5936 // tag can override this..
5938 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5941 if (this.brand !== '') {
5942 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5945 href: this.brand_href ? this.brand_href : '#',
5946 cls: 'navbar-brand',
5954 cfg.cls += ' main-nav';
5962 getHeaderChildContainer : function()
5964 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965 return this.el.select('.navbar-header',true).first();
5968 return this.getChildContainer();
5971 getChildContainer : function()
5974 return this.el.select('.roo-navbar-collapse',true).first();
5979 initEvents : function()
5981 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5983 if (this.autohide) {
5988 Roo.get(document).on('scroll',function(e) {
5989 var ns = Roo.get(document).getScroll().top;
5990 var os = prevScroll;
5994 ft.removeClass('slideDown');
5995 ft.addClass('slideUp');
5998 ft.removeClass('slideUp');
5999 ft.addClass('slideDown');
6020 * @class Roo.bootstrap.nav.Sidebar
6021 * @extends Roo.bootstrap.nav.Bar
6022 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023 * Bootstrap Sidebar class
6026 * Create a new Sidebar
6027 * @param {Object} config The config object
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6037 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6039 getAutoCreate : function(){
6044 cls: 'sidebar sidebar-nav'
6066 * @class Roo.bootstrap.nav.Group
6067 * @extends Roo.bootstrap.Component
6068 * @children Roo.bootstrap.nav.Item
6069 * Bootstrap NavGroup class
6070 * @cfg {String} align (left|right)
6071 * @cfg {Boolean} inverse
6072 * @cfg {String} type (nav|pills|tab) default nav
6073 * @cfg {String} navId - reference Id for navbar.
6074 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6077 * Create a new nav group
6078 * @param {Object} config The config object
6081 Roo.bootstrap.nav.Group = function(config){
6082 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6085 Roo.bootstrap.nav.Group.register(this);
6089 * Fires when the active item changes
6090 * @param {Roo.bootstrap.nav.Group} this
6091 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6111 getAutoCreate : function()
6113 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6119 if (Roo.bootstrap.version == 4) {
6120 if (['tabs','pills'].indexOf(this.type) != -1) {
6121 cfg.cls += ' nav-' + this.type;
6123 // trying to remove so header bar can right align top?
6124 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125 // do not use on header bar...
6126 cfg.cls += ' navbar-nav';
6131 if (['tabs','pills'].indexOf(this.type) != -1) {
6132 cfg.cls += ' nav-' + this.type
6134 if (this.type !== 'nav') {
6135 Roo.log('nav type must be nav/tabs/pills')
6137 cfg.cls += ' navbar-nav'
6141 if (this.parent() && this.parent().sidebar) {
6144 cls: 'dashboard-menu sidebar-menu'
6150 if (this.form === true) {
6153 cls: 'navbar-form form-inline'
6155 //nav navbar-right ml-md-auto
6156 if (this.align === 'right') {
6157 cfg.cls += ' navbar-right ml-md-auto';
6159 cfg.cls += ' navbar-left';
6163 if (this.align === 'right') {
6164 cfg.cls += ' navbar-right ml-md-auto';
6166 cfg.cls += ' mr-auto';
6170 cfg.cls += ' navbar-inverse';
6178 * sets the active Navigation item
6179 * @param {Roo.bootstrap.nav.Item} the new current navitem
6181 setActiveItem : function(item)
6184 Roo.each(this.navItems, function(v){
6189 v.setActive(false, true);
6196 item.setActive(true, true);
6197 this.fireEvent('changed', this, item, prev);
6202 * gets the active Navigation item
6203 * @return {Roo.bootstrap.nav.Item} the current navitem
6205 getActive : function()
6209 Roo.each(this.navItems, function(v){
6220 indexOfNav : function()
6224 Roo.each(this.navItems, function(v,i){
6235 * adds a Navigation item
6236 * @param {Roo.bootstrap.nav.Item} the navitem to add
6238 addItem : function(cfg)
6240 if (this.form && Roo.bootstrap.version == 4) {
6243 var cn = new Roo.bootstrap.nav.Item(cfg);
6245 cn.parentId = this.id;
6246 cn.onRender(this.el, null);
6250 * register a Navigation item
6251 * @param {Roo.bootstrap.nav.Item} the navitem to add
6253 register : function(item)
6255 this.navItems.push( item);
6256 item.navId = this.navId;
6261 * clear all the Navigation item
6264 clearAll : function()
6267 this.el.dom.innerHTML = '';
6270 getNavItem: function(tabId)
6273 Roo.each(this.navItems, function(e) {
6274 if (e.tabId == tabId) {
6284 setActiveNext : function()
6286 var i = this.indexOfNav(this.getActive());
6287 if (i > this.navItems.length) {
6290 this.setActiveItem(this.navItems[i+1]);
6292 setActivePrev : function()
6294 var i = this.indexOfNav(this.getActive());
6298 this.setActiveItem(this.navItems[i-1]);
6300 clearWasActive : function(except) {
6301 Roo.each(this.navItems, function(e) {
6302 if (e.tabId != except.tabId && e.was_active) {
6303 e.was_active = false;
6310 getWasActive : function ()
6313 Roo.each(this.navItems, function(e) {
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6332 * register a Navigation Group
6333 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6335 register : function(navgrp)
6337 this.groups[navgrp.navId] = navgrp;
6341 * fetch a Navigation Group based on the navigation ID
6342 * @param {string} the navgroup to add
6343 * @returns {Roo.bootstrap.nav.Group} the navgroup
6345 get: function(navId) {
6346 if (typeof(this.groups[navId]) == 'undefined') {
6348 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6350 return this.groups[navId] ;
6358 * @class Roo.bootstrap.nav.Item
6359 * @extends Roo.bootstrap.Component
6360 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361 * @parent Roo.bootstrap.nav.Group
6363 * Bootstrap Navbar.NavItem class
6365 * @cfg {String} href link to
6366 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367 * @cfg {Boolean} button_outline show and outlined button
6368 * @cfg {String} html content of button
6369 * @cfg {String} badge text inside badge
6370 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371 * @cfg {String} glyphicon DEPRICATED - use fa
6372 * @cfg {String} icon DEPRICATED - use fa
6373 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374 * @cfg {Boolean} active Is item active
6375 * @cfg {Boolean} disabled Is item disabled
6376 * @cfg {String} linkcls Link Class
6377 * @cfg {Boolean} preventDefault (true | false) default false
6378 * @cfg {String} tabId the tab that this item activates.
6379 * @cfg {String} tagtype (a|span) render as a href or span?
6380 * @cfg {Boolean} animateRef (true|false) link to element default false
6381 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6384 * Create a new Navbar Item
6385 * @param {Object} config The config object
6387 Roo.bootstrap.nav.Item = function(config){
6388 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6393 * The raw click event for the entire grid.
6394 * @param {Roo.EventObject} e
6399 * Fires when the active item active state changes
6400 * @param {Roo.bootstrap.nav.Item} this
6401 * @param {boolean} state the new state
6407 * Fires when scroll to element
6408 * @param {Roo.bootstrap.nav.Item} this
6409 * @param {Object} options
6410 * @param {Roo.EventObject} e
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6427 preventDefault : false,
6435 button_outline : false,
6439 getAutoCreate : function(){
6446 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6449 cfg.cls += ' active' ;
6451 if (this.disabled) {
6452 cfg.cls += ' disabled';
6456 if (this.button_weight.length) {
6457 cfg.tag = this.href ? 'a' : 'button';
6458 cfg.html = this.html || '';
6459 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6461 cfg.href = this.href;
6464 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6466 cfg.cls += " nav-html";
6469 // menu .. should add dropdown-menu class - so no need for carat..
6471 if (this.badge !== '') {
6473 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6478 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6482 href : this.href || "#",
6483 html: this.html || '',
6487 if (this.tagtype == 'a') {
6488 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6492 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493 } else if (this.fa) {
6494 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495 } else if(this.glyphicon) {
6496 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6498 cfg.cn[0].cls += " nav-html";
6502 cfg.cn[0].html += " <span class='caret'></span>";
6506 if (this.badge !== '') {
6507 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6515 onRender : function(ct, position)
6517 // Roo.log("Call onRender: " + this.xtype);
6518 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6522 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523 this.navLink = this.el.select('.nav-link',true).first();
6524 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6529 initEvents: function()
6531 if (typeof (this.menu) != 'undefined') {
6532 this.menu.parentType = this.xtype;
6533 this.menu.triggerEl = this.el;
6534 this.menu = this.addxtype(Roo.apply({}, this.menu));
6537 this.el.on('click', this.onClick, this);
6539 //if(this.tagtype == 'span'){
6540 // this.el.select('span',true).on('click', this.onClick, this);
6543 // at this point parent should be available..
6544 this.parent().register(this);
6547 onClick : function(e)
6549 if (e.getTarget('.dropdown-menu-item')) {
6550 // did you click on a menu itemm.... - then don't trigger onclick..
6555 this.preventDefault ||
6556 this.href === false ||
6559 //Roo.log("NavItem - prevent Default?");
6563 if (this.disabled) {
6567 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568 if (tg && tg.transition) {
6569 Roo.log("waiting for the transitionend");
6575 //Roo.log("fire event clicked");
6576 if(this.fireEvent('click', this, e) === false){
6580 if(this.tagtype == 'span'){
6584 //Roo.log(this.href);
6585 var ael = this.el.select('a',true).first();
6588 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591 return; // ignore... - it's a 'hash' to another page.
6593 Roo.log("NavItem - prevent Default?");
6595 this.scrollToElement(e);
6599 var p = this.parent();
6601 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602 if (typeof(p.setActiveItem) !== 'undefined') {
6603 p.setActiveItem(this);
6607 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609 // remove the collapsed menu expand...
6610 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6614 isActive: function () {
6617 setActive : function(state, fire, is_was_active)
6619 if (this.active && !state && this.navId) {
6620 this.was_active = true;
6621 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6623 nv.clearWasActive(this);
6627 this.active = state;
6630 this.el.removeClass('active');
6631 this.navLink ? this.navLink.removeClass('active') : false;
6632 } else if (!this.el.hasClass('active')) {
6634 this.el.addClass('active');
6635 if (Roo.bootstrap.version == 4 && this.navLink ) {
6636 this.navLink.addClass('active');
6641 this.fireEvent('changed', this, state);
6644 // show a panel if it's registered and related..
6646 if (!this.navId || !this.tabId || !state || is_was_active) {
6650 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6654 var pan = tg.getPanelByName(this.tabId);
6658 // if we can not flip to new panel - go back to old nav highlight..
6659 if (false == tg.showPanel(pan)) {
6660 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6662 var onav = nv.getWasActive();
6664 onav.setActive(true, false, true);
6673 // this should not be here...
6674 setDisabled : function(state)
6676 this.disabled = state;
6678 this.el.removeClass('disabled');
6679 } else if (!this.el.hasClass('disabled')) {
6680 this.el.addClass('disabled');
6686 * Fetch the element to display the tooltip on.
6687 * @return {Roo.Element} defaults to this.el
6689 tooltipEl : function()
6691 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6694 scrollToElement : function(e)
6696 var c = document.body;
6699 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6701 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702 c = document.documentElement;
6705 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6711 var o = target.calcOffsetsTo(c);
6718 this.fireEvent('scrollto', this, options, e);
6720 Roo.get(c).scrollTo('top', options.value, true);
6725 * Set the HTML (text content) of the item
6726 * @param {string} html content for the nav item
6728 setHtml : function(html)
6731 this.htmlEl.dom.innerHTML = html;
6743 * <span> icon </span>
6744 * <span> text </span>
6745 * <span>badge </span>
6749 * @class Roo.bootstrap.nav.SidebarItem
6750 * @extends Roo.bootstrap.nav.Item
6751 * Bootstrap Navbar.NavSidebarItem class
6753 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754 * {Boolean} open is the menu open
6755 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757 * {String} buttonSize (sm|md|lg)the extra classes for the button
6758 * {Boolean} showArrow show arrow next to the text (default true)
6760 * Create a new Navbar Button
6761 * @param {Object} config The config object
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6769 * The raw click event for the entire grid.
6770 * @param {Roo.EventObject} e
6775 * Fires when the active item active state changes
6776 * @param {Roo.bootstrap.nav.SidebarItem} this
6777 * @param {boolean} state the new state
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6787 badgeWeight : 'default',
6793 buttonWeight : 'default',
6799 getAutoCreate : function(){
6804 href : this.href || '#',
6810 if(this.buttonView){
6813 href : this.href || '#',
6814 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6827 cfg.cls += ' active';
6830 if (this.disabled) {
6831 cfg.cls += ' disabled';
6834 cfg.cls += ' open x-open';
6837 if (this.glyphicon || this.icon) {
6838 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6839 a.cn.push({ tag : 'i', cls : c }) ;
6842 if(!this.buttonView){
6845 html : this.html || ''
6852 if (this.badge !== '') {
6853 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6859 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6862 a.cls += ' dropdown-toggle treeview' ;
6868 initEvents : function()
6870 if (typeof (this.menu) != 'undefined') {
6871 this.menu.parentType = this.xtype;
6872 this.menu.triggerEl = this.el;
6873 this.menu = this.addxtype(Roo.apply({}, this.menu));
6876 this.el.on('click', this.onClick, this);
6878 if(this.badge !== ''){
6879 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6884 onClick : function(e)
6891 if(this.preventDefault){
6895 this.fireEvent('click', this, e);
6898 disable : function()
6900 this.setDisabled(true);
6905 this.setDisabled(false);
6908 setDisabled : function(state)
6910 if(this.disabled == state){
6914 this.disabled = state;
6917 this.el.addClass('disabled');
6921 this.el.removeClass('disabled');
6926 setActive : function(state)
6928 if(this.active == state){
6932 this.active = state;
6935 this.el.addClass('active');
6939 this.el.removeClass('active');
6944 isActive: function ()
6949 setBadge : function(str)
6955 this.badgeEl.dom.innerHTML = str;
6972 * @class Roo.bootstrap.nav.ProgressBar
6973 * @extends Roo.bootstrap.Component
6974 * @children Roo.bootstrap.nav.ProgressBarItem
6975 * Bootstrap NavProgressBar class
6978 * Create a new nav progress bar - a bar indicating step along a process
6979 * @param {Object} config The config object
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6985 this.bullets = this.bullets || [];
6987 // Roo.bootstrap.nav.ProgressBar.register(this);
6991 * Fires when the active item changes
6992 * @param {Roo.bootstrap.nav.ProgressBar} this
6993 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7003 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004 * Bullets for the Nav Progress bar for the toolbar
7009 getAutoCreate : function()
7011 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7015 cls : 'roo-navigation-bar-group',
7019 cls : 'roo-navigation-top-bar'
7023 cls : 'roo-navigation-bullets-bar',
7027 cls : 'roo-navigation-bar'
7034 cls : 'roo-navigation-bottom-bar'
7044 initEvents: function()
7049 onRender : function(ct, position)
7051 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7053 if(this.bullets.length){
7054 Roo.each(this.bullets, function(b){
7063 addItem : function(cfg)
7065 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7067 item.parentId = this.id;
7068 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7071 var top = new Roo.bootstrap.Element({
7073 cls : 'roo-navigation-bar-text'
7076 var bottom = new Roo.bootstrap.Element({
7078 cls : 'roo-navigation-bar-text'
7081 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7084 var topText = new Roo.bootstrap.Element({
7086 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7089 var bottomText = new Roo.bootstrap.Element({
7091 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7094 topText.onRender(top.el, null);
7095 bottomText.onRender(bottom.el, null);
7098 item.bottomEl = bottom;
7101 this.barItems.push(item);
7106 getActive : function()
7110 Roo.each(this.barItems, function(v){
7112 if (!v.isActive()) {
7124 setActiveItem : function(item)
7128 Roo.each(this.barItems, function(v){
7129 if (v.rid == item.rid) {
7139 item.setActive(true);
7141 this.fireEvent('changed', this, item, prev);
7144 getBarItem: function(rid)
7148 Roo.each(this.barItems, function(e) {
7160 indexOfItem : function(item)
7164 Roo.each(this.barItems, function(v, i){
7166 if (v.rid != item.rid) {
7177 setActiveNext : function()
7179 var i = this.indexOfItem(this.getActive());
7181 if (i > this.barItems.length) {
7185 this.setActiveItem(this.barItems[i+1]);
7188 setActivePrev : function()
7190 var i = this.indexOfItem(this.getActive());
7196 this.setActiveItem(this.barItems[i-1]);
7201 if(!this.barItems.length){
7205 var width = 100 / this.barItems.length;
7207 Roo.each(this.barItems, function(i){
7208 i.el.setStyle('width', width + '%');
7209 i.topEl.el.setStyle('width', width + '%');
7210 i.bottomEl.el.setStyle('width', width + '%');
7224 * @class Roo.bootstrap.nav.ProgressBarItem
7225 * @extends Roo.bootstrap.Component
7226 * Bootstrap NavProgressBarItem class
7227 * @cfg {String} rid the reference id
7228 * @cfg {Boolean} active (true|false) Is item active default false
7229 * @cfg {Boolean} disabled (true|false) Is item active default false
7230 * @cfg {String} html
7231 * @cfg {String} position (top|bottom) text position default bottom
7232 * @cfg {String} icon show icon instead of number
7235 * Create a new NavProgressBarItem
7236 * @param {Object} config The config object
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7244 * The raw click event for the entire grid.
7245 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246 * @param {Roo.EventObject} e
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7259 position : 'bottom',
7262 getAutoCreate : function()
7264 var iconCls = 'roo-navigation-bar-item-icon';
7266 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7270 cls: 'roo-navigation-bar-item',
7280 cfg.cls += ' active';
7283 cfg.cls += ' disabled';
7289 disable : function()
7291 this.setDisabled(true);
7296 this.setDisabled(false);
7299 initEvents: function()
7301 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7303 this.iconEl.on('click', this.onClick, this);
7306 onClick : function(e)
7314 if(this.fireEvent('click', this, e) === false){
7318 this.parent().setActiveItem(this);
7321 isActive: function ()
7326 setActive : function(state)
7328 if(this.active == state){
7332 this.active = state;
7335 this.el.addClass('active');
7339 this.el.removeClass('active');
7344 setDisabled : function(state)
7346 if(this.disabled == state){
7350 this.disabled = state;
7353 this.el.addClass('disabled');
7357 this.el.removeClass('disabled');
7360 tooltipEl : function()
7362 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7377 * @class Roo.bootstrap.breadcrumb.Nav
7378 * @extends Roo.bootstrap.Component
7379 * Bootstrap Breadcrumb Nav Class
7381 * @children Roo.bootstrap.breadcrumb.Item
7384 * Create a new breadcrumb.Nav
7385 * @param {Object} config The config object
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7397 getAutoCreate : function()
7414 initEvents: function()
7416 this.olEl = this.el.select('ol',true).first();
7418 getChildContainer : function()
7434 * @class Roo.bootstrap.breadcrumb.Nav
7435 * @extends Roo.bootstrap.Component
7436 * @children Roo.bootstrap.Component
7437 * @parent Roo.bootstrap.breadcrumb.Nav
7438 * Bootstrap Breadcrumb Nav Class
7441 * @cfg {String} html the content of the link.
7442 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443 * @cfg {Boolean} active is it active
7447 * Create a new breadcrumb.Nav
7448 * @param {Object} config The config object
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7457 * The img click event for the img.
7458 * @param {Roo.EventObject} e
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7470 getAutoCreate : function()
7475 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7477 if (this.href !== false) {
7484 cfg.html = this.html;
7490 initEvents: function()
7493 this.el.select('a', true).first().on('click',this.onClick, this)
7497 onClick : function(e)
7500 this.fireEvent('click',this, e);
7513 * @class Roo.bootstrap.Row
7514 * @extends Roo.bootstrap.Component
7515 * @children Roo.bootstrap.Component
7516 * Bootstrap Row class (contains columns...)
7520 * @param {Object} config The config object
7523 Roo.bootstrap.Row = function(config){
7524 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7529 getAutoCreate : function(){
7548 * @class Roo.bootstrap.Pagination
7549 * @extends Roo.bootstrap.Component
7550 * @children Roo.bootstrap.Pagination
7551 * Bootstrap Pagination class
7553 * @cfg {String} size (xs|sm|md|lg|xl)
7554 * @cfg {Boolean} inverse
7557 * Create a new Pagination
7558 * @param {Object} config The config object
7561 Roo.bootstrap.Pagination = function(config){
7562 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7571 getAutoCreate : function(){
7577 cfg.cls += ' inverse';
7583 cfg.cls += " " + this.cls;
7601 * @class Roo.bootstrap.PaginationItem
7602 * @extends Roo.bootstrap.Component
7603 * Bootstrap PaginationItem class
7604 * @cfg {String} html text
7605 * @cfg {String} href the link
7606 * @cfg {Boolean} preventDefault (true | false) default true
7607 * @cfg {Boolean} active (true | false) default false
7608 * @cfg {Boolean} disabled default false
7612 * Create a new PaginationItem
7613 * @param {Object} config The config object
7617 Roo.bootstrap.PaginationItem = function(config){
7618 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7623 * The raw click event for the entire grid.
7624 * @param {Roo.EventObject} e
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7634 preventDefault: true,
7639 getAutoCreate : function(){
7645 href : this.href ? this.href : '#',
7646 html : this.html ? this.html : ''
7656 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7660 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7666 initEvents: function() {
7668 this.el.on('click', this.onClick, this);
7671 onClick : function(e)
7673 Roo.log('PaginationItem on click ');
7674 if(this.preventDefault){
7682 this.fireEvent('click', this, e);
7698 * @class Roo.bootstrap.Slider
7699 * @extends Roo.bootstrap.Component
7700 * Bootstrap Slider class
7703 * Create a new Slider
7704 * @param {Object} config The config object
7707 Roo.bootstrap.Slider = function(config){
7708 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7713 getAutoCreate : function(){
7717 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7721 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7733 * Ext JS Library 1.1.1
7734 * Copyright(c) 2006-2007, Ext JS, LLC.
7736 * Originally Released Under LGPL - original licence link has changed is not relivant.
7739 * <script type="text/javascript">
7742 * @extends Roo.dd.DDProxy
7743 * @class Roo.grid.SplitDragZone
7744 * Support for Column Header resizing
7746 * @param {Object} config
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7752 this.view = grid.getView();
7753 this.proxy = this.view.resizeProxy;
7754 Roo.grid.SplitDragZone.superclass.constructor.call(
7757 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7759 dragElId : Roo.id(this.proxy.dom),
7764 this.setHandleElId(Roo.id(hd));
7765 if (hd2 !== false) {
7766 this.setOuterHandleElId(Roo.id(hd2));
7769 this.scroll = false;
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772 fly: Roo.Element.fly,
7774 b4StartDrag : function(x, y){
7775 this.view.headersDisabled = true;
7776 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7779 this.proxy.setHeight(h);
7781 // for old system colWidth really stored the actual width?
7782 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783 // which in reality did not work.. - it worked only for fixed sizes
7784 // for resizable we need to use actual sizes.
7785 var w = this.cm.getColumnWidth(this.cellIndex);
7786 if (!this.view.mainWrap) {
7788 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7793 // this was w-this.grid.minColumnWidth;
7794 // doesnt really make sense? - w = thie curren width or the rendered one?
7795 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796 this.resetConstraints();
7797 this.setXConstraint(minw, 1000);
7798 this.setYConstraint(0, 0);
7799 this.minX = x - minw;
7800 this.maxX = x + 1000;
7802 if (!this.view.mainWrap) { // this is Bootstrap code..
7803 this.getDragEl().style.display='block';
7806 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7810 handleMouseDown : function(e){
7811 ev = Roo.EventObject.setEvent(e);
7812 var t = this.fly(ev.getTarget());
7813 if(t.hasClass("x-grid-split")){
7814 this.cellIndex = this.view.getCellIndex(t.dom);
7816 this.cm = this.grid.colModel;
7817 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7823 endDrag : function(e){
7824 this.view.headersDisabled = false;
7825 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826 var diff = endX - this.startPos;
7828 var w = this.cm.getColumnWidth(this.cellIndex);
7829 if (!this.view.mainWrap) {
7832 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7835 autoOffset : function(){
7840 * Ext JS Library 1.1.1
7841 * Copyright(c) 2006-2007, Ext JS, LLC.
7843 * Originally Released Under LGPL - original licence link has changed is not relivant.
7846 * <script type="text/javascript">
7850 * @class Roo.grid.AbstractSelectionModel
7851 * @extends Roo.util.Observable
7853 * Abstract base class for grid SelectionModels. It provides the interface that should be
7854 * implemented by descendant classes. This class should not be directly instantiated.
7857 Roo.grid.AbstractSelectionModel = function(){
7858 this.locked = false;
7859 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7863 /** @ignore Called by the grid automatically. Do not call directly. */
7864 init : function(grid){
7870 * Locks the selections.
7877 * Unlocks the selections.
7879 unlock : function(){
7880 this.locked = false;
7884 * Returns true if the selections are locked.
7887 isLocked : function(){
7892 * Ext JS Library 1.1.1
7893 * Copyright(c) 2006-2007, Ext JS, LLC.
7895 * Originally Released Under LGPL - original licence link has changed is not relivant.
7898 * <script type="text/javascript">
7901 * @extends Roo.grid.AbstractSelectionModel
7902 * @class Roo.grid.RowSelectionModel
7903 * The default SelectionModel used by {@link Roo.grid.Grid}.
7904 * It supports multiple selections and keyboard selection/navigation.
7906 * @param {Object} config
7908 Roo.grid.RowSelectionModel = function(config){
7909 Roo.apply(this, config);
7910 this.selections = new Roo.util.MixedCollection(false, function(o){
7915 this.lastActive = false;
7919 * @event selectionchange
7920 * Fires when the selection changes
7921 * @param {SelectionModel} this
7923 "selectionchange" : true,
7925 * @event afterselectionchange
7926 * Fires after the selection changes (eg. by key press or clicking)
7927 * @param {SelectionModel} this
7929 "afterselectionchange" : true,
7931 * @event beforerowselect
7932 * Fires when a row is selected being selected, return false to cancel.
7933 * @param {SelectionModel} this
7934 * @param {Number} rowIndex The selected index
7935 * @param {Boolean} keepExisting False if other selections will be cleared
7937 "beforerowselect" : true,
7940 * Fires when a row is selected.
7941 * @param {SelectionModel} this
7942 * @param {Number} rowIndex The selected index
7943 * @param {Roo.data.Record} r The record
7947 * @event rowdeselect
7948 * Fires when a row is deselected.
7949 * @param {SelectionModel} this
7950 * @param {Number} rowIndex The selected index
7952 "rowdeselect" : true
7954 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955 this.locked = false;
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7960 * @cfg {Boolean} singleSelect
7961 * True to allow selection of only one row at a time (defaults to false)
7963 singleSelect : false,
7966 initEvents : function(){
7968 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969 this.grid.on("mousedown", this.handleMouseDown, this);
7970 }else{ // allow click to work like normal
7971 this.grid.on("rowclick", this.handleDragableRowClick, this);
7973 // bootstrap does not have a view..
7974 var view = this.grid.view ? this.grid.view : this.grid;
7975 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7978 this.selectPrevious(e.shiftKey);
7979 }else if(this.last !== false && this.lastActive !== false){
7980 var last = this.last;
7981 this.selectRange(this.last, this.lastActive-1);
7982 view.focusRow(this.lastActive);
7987 this.selectFirstRow();
7989 this.fireEvent("afterselectionchange", this);
7991 "down" : function(e){
7993 this.selectNext(e.shiftKey);
7994 }else if(this.last !== false && this.lastActive !== false){
7995 var last = this.last;
7996 this.selectRange(this.last, this.lastActive+1);
7997 view.focusRow(this.lastActive);
8002 this.selectFirstRow();
8004 this.fireEvent("afterselectionchange", this);
8010 view.on("refresh", this.onRefresh, this);
8011 view.on("rowupdated", this.onRowUpdated, this);
8012 view.on("rowremoved", this.onRemove, this);
8016 onRefresh : function(){
8017 var ds = this.grid.ds, i, v = this.grid.view;
8018 var s = this.selections;
8020 if((i = ds.indexOfId(r.id)) != -1){
8022 s.add(ds.getAt(i)); // updating the selection relate data
8030 onRemove : function(v, index, r){
8031 this.selections.remove(r);
8035 onRowUpdated : function(v, index, r){
8036 if(this.isSelected(r)){
8037 v.onRowSelect(index);
8043 * @param {Array} records The records to select
8044 * @param {Boolean} keepExisting (optional) True to keep existing selections
8046 selectRecords : function(records, keepExisting){
8048 this.clearSelections();
8050 var ds = this.grid.ds;
8051 for(var i = 0, len = records.length; i < len; i++){
8052 this.selectRow(ds.indexOf(records[i]), true);
8057 * Gets the number of selected rows.
8060 getCount : function(){
8061 return this.selections.length;
8065 * Selects the first row in the grid.
8067 selectFirstRow : function(){
8072 * Select the last row.
8073 * @param {Boolean} keepExisting (optional) True to keep existing selections
8075 selectLastRow : function(keepExisting){
8076 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8080 * Selects the row immediately following the last selected row.
8081 * @param {Boolean} keepExisting (optional) True to keep existing selections
8083 selectNext : function(keepExisting){
8084 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085 this.selectRow(this.last+1, keepExisting);
8086 var view = this.grid.view ? this.grid.view : this.grid;
8087 view.focusRow(this.last);
8092 * Selects the row that precedes the last selected row.
8093 * @param {Boolean} keepExisting (optional) True to keep existing selections
8095 selectPrevious : function(keepExisting){
8097 this.selectRow(this.last-1, keepExisting);
8098 var view = this.grid.view ? this.grid.view : this.grid;
8099 view.focusRow(this.last);
8104 * Returns the selected records
8105 * @return {Array} Array of selected records
8107 getSelections : function(){
8108 return [].concat(this.selections.items);
8112 * Returns the first selected record.
8115 getSelected : function(){
8116 return this.selections.itemAt(0);
8121 * Clears all selections.
8123 clearSelections : function(fast){
8128 var ds = this.grid.ds;
8129 var s = this.selections;
8131 this.deselectRow(ds.indexOfId(r.id));
8135 this.selections.clear();
8144 selectAll : function(){
8148 this.selections.clear();
8149 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150 this.selectRow(i, true);
8155 * Returns True if there is a selection.
8158 hasSelection : function(){
8159 return this.selections.length > 0;
8163 * Returns True if the specified row is selected.
8164 * @param {Number/Record} record The record or index of the record to check
8167 isSelected : function(index){
8168 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169 return (r && this.selections.key(r.id) ? true : false);
8173 * Returns True if the specified record id is selected.
8174 * @param {String} id The id of record to check
8177 isIdSelected : function(id){
8178 return (this.selections.key(id) ? true : false);
8182 handleMouseDown : function(e, t)
8184 var view = this.grid.view ? this.grid.view : this.grid;
8186 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8189 if(e.shiftKey && this.last !== false){
8190 var last = this.last;
8191 this.selectRange(last, rowIndex, e.ctrlKey);
8192 this.last = last; // reset the last
8193 view.focusRow(rowIndex);
8195 var isSelected = this.isSelected(rowIndex);
8196 if(e.button !== 0 && isSelected){
8197 view.focusRow(rowIndex);
8198 }else if(e.ctrlKey && isSelected){
8199 this.deselectRow(rowIndex);
8200 }else if(!isSelected){
8201 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202 view.focusRow(rowIndex);
8205 this.fireEvent("afterselectionchange", this);
8208 handleDragableRowClick : function(grid, rowIndex, e)
8210 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211 this.selectRow(rowIndex, false);
8212 var view = this.grid.view ? this.grid.view : this.grid;
8213 view.focusRow(rowIndex);
8214 this.fireEvent("afterselectionchange", this);
8219 * Selects multiple rows.
8220 * @param {Array} rows Array of the indexes of the row to select
8221 * @param {Boolean} keepExisting (optional) True to keep existing selections
8223 selectRows : function(rows, keepExisting){
8225 this.clearSelections();
8227 for(var i = 0, len = rows.length; i < len; i++){
8228 this.selectRow(rows[i], true);
8233 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234 * @param {Number} startRow The index of the first row in the range
8235 * @param {Number} endRow The index of the last row in the range
8236 * @param {Boolean} keepExisting (optional) True to retain existing selections
8238 selectRange : function(startRow, endRow, keepExisting){
8243 this.clearSelections();
8245 if(startRow <= endRow){
8246 for(var i = startRow; i <= endRow; i++){
8247 this.selectRow(i, true);
8250 for(var i = startRow; i >= endRow; i--){
8251 this.selectRow(i, true);
8257 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258 * @param {Number} startRow The index of the first row in the range
8259 * @param {Number} endRow The index of the last row in the range
8261 deselectRange : function(startRow, endRow, preventViewNotify){
8265 for(var i = startRow; i <= endRow; i++){
8266 this.deselectRow(i, preventViewNotify);
8272 * @param {Number} row The index of the row to select
8273 * @param {Boolean} keepExisting (optional) True to keep existing selections
8275 selectRow : function(index, keepExisting, preventViewNotify){
8276 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8279 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280 if(!keepExisting || this.singleSelect){
8281 this.clearSelections();
8283 var r = this.grid.ds.getAt(index);
8284 this.selections.add(r);
8285 this.last = this.lastActive = index;
8286 if(!preventViewNotify){
8287 var view = this.grid.view ? this.grid.view : this.grid;
8288 view.onRowSelect(index);
8290 this.fireEvent("rowselect", this, index, r);
8291 this.fireEvent("selectionchange", this);
8297 * @param {Number} row The index of the row to deselect
8299 deselectRow : function(index, preventViewNotify){
8303 if(this.last == index){
8306 if(this.lastActive == index){
8307 this.lastActive = false;
8309 var r = this.grid.ds.getAt(index);
8310 this.selections.remove(r);
8311 if(!preventViewNotify){
8312 var view = this.grid.view ? this.grid.view : this.grid;
8313 view.onRowDeselect(index);
8315 this.fireEvent("rowdeselect", this, index);
8316 this.fireEvent("selectionchange", this);
8320 restoreLast : function(){
8322 this.last = this._last;
8327 acceptsNav : function(row, col, cm){
8328 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8332 onEditorKey : function(field, e){
8333 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8338 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8340 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8342 }else if(k == e.ENTER && !e.ctrlKey){
8346 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8348 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8350 }else if(k == e.ESC){
8354 g.startEditing(newCell[0], newCell[1]);
8359 * Ext JS Library 1.1.1
8360 * Copyright(c) 2006-2007, Ext JS, LLC.
8362 * Originally Released Under LGPL - original licence link has changed is not relivant.
8365 * <script type="text/javascript">
8370 * @class Roo.grid.ColumnModel
8371 * @extends Roo.util.Observable
8372 * This is the default implementation of a ColumnModel used by the Grid. It defines
8373 * the columns in the grid.
8376 var colModel = new Roo.grid.ColumnModel([
8377 {header: "Ticker", width: 60, sortable: true, locked: true},
8378 {header: "Company Name", width: 150, sortable: true},
8379 {header: "Market Cap.", width: 100, sortable: true},
8380 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381 {header: "Employees", width: 100, sortable: true, resizable: false}
8386 * The config options listed for this class are options which may appear in each
8387 * individual column definition.
8388 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8390 * @param {Object} config An Array of column config objects. See this class's
8391 * config objects for details.
8393 Roo.grid.ColumnModel = function(config){
8395 * The config passed into the constructor
8397 this.config = []; //config;
8400 // if no id, create one
8401 // if the column does not have a dataIndex mapping,
8402 // map it to the order it is in the config
8403 for(var i = 0, len = config.length; i < len; i++){
8404 this.addColumn(config[i]);
8409 * The width of columns which have no width specified (defaults to 100)
8412 this.defaultWidth = 100;
8415 * Default sortable of columns which have no sortable specified (defaults to false)
8418 this.defaultSortable = false;
8422 * @event widthchange
8423 * Fires when the width of a column changes.
8424 * @param {ColumnModel} this
8425 * @param {Number} columnIndex The column index
8426 * @param {Number} newWidth The new width
8428 "widthchange": true,
8430 * @event headerchange
8431 * Fires when the text of a header changes.
8432 * @param {ColumnModel} this
8433 * @param {Number} columnIndex The column index
8434 * @param {Number} newText The new header text
8436 "headerchange": true,
8438 * @event hiddenchange
8439 * Fires when a column is hidden or "unhidden".
8440 * @param {ColumnModel} this
8441 * @param {Number} columnIndex The column index
8442 * @param {Boolean} hidden true if hidden, false otherwise
8444 "hiddenchange": true,
8446 * @event columnmoved
8447 * Fires when a column is moved.
8448 * @param {ColumnModel} this
8449 * @param {Number} oldIndex
8450 * @param {Number} newIndex
8452 "columnmoved" : true,
8454 * @event columlockchange
8455 * Fires when a column's locked state is changed
8456 * @param {ColumnModel} this
8457 * @param {Number} colIndex
8458 * @param {Boolean} locked true if locked
8460 "columnlockchange" : true
8462 Roo.grid.ColumnModel.superclass.constructor.call(this);
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8466 * @cfg {String} header [required] The header text to display in the Grid view.
8469 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8472 * @cfg {String} smHeader Header at Bootsrap Small width
8475 * @cfg {String} mdHeader Header at Bootsrap Medium width
8478 * @cfg {String} lgHeader Header at Bootsrap Large width
8481 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8484 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8485 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486 * specified, the column's index is used as an index into the Record's data Array.
8489 * @cfg {Number} width The initial width in pixels of the column. Using this
8490 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8493 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494 * Defaults to the value of the {@link #defaultSortable} property.
8495 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8498 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8501 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8504 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8507 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8510 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8516 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8519 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8522 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8525 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8528 * @cfg {String} tooltip mouse over tooltip text
8531 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8534 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8537 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8540 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8546 * Returns the id of the column at the specified index.
8547 * @param {Number} index The column index
8548 * @return {String} the id
8550 getColumnId : function(index){
8551 return this.config[index].id;
8555 * Returns the column for a specified id.
8556 * @param {String} id The column id
8557 * @return {Object} the column
8559 getColumnById : function(id){
8560 return this.lookup[id];
8565 * Returns the column Object for a specified dataIndex.
8566 * @param {String} dataIndex The column dataIndex
8567 * @return {Object|Boolean} the column or false if not found
8569 getColumnByDataIndex: function(dataIndex){
8570 var index = this.findColumnIndex(dataIndex);
8571 return index > -1 ? this.config[index] : false;
8575 * Returns the index for a specified column id.
8576 * @param {String} id The column id
8577 * @return {Number} the index, or -1 if not found
8579 getIndexById : function(id){
8580 for(var i = 0, len = this.config.length; i < len; i++){
8581 if(this.config[i].id == id){
8589 * Returns the index for a specified column dataIndex.
8590 * @param {String} dataIndex The column dataIndex
8591 * @return {Number} the index, or -1 if not found
8594 findColumnIndex : function(dataIndex){
8595 for(var i = 0, len = this.config.length; i < len; i++){
8596 if(this.config[i].dataIndex == dataIndex){
8604 moveColumn : function(oldIndex, newIndex){
8605 var c = this.config[oldIndex];
8606 this.config.splice(oldIndex, 1);
8607 this.config.splice(newIndex, 0, c);
8608 this.dataMap = null;
8609 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8612 isLocked : function(colIndex){
8613 return this.config[colIndex].locked === true;
8616 setLocked : function(colIndex, value, suppressEvent){
8617 if(this.isLocked(colIndex) == value){
8620 this.config[colIndex].locked = value;
8622 this.fireEvent("columnlockchange", this, colIndex, value);
8626 getTotalLockedWidth : function(){
8628 for(var i = 0; i < this.config.length; i++){
8629 if(this.isLocked(i) && !this.isHidden(i)){
8630 this.totalWidth += this.getColumnWidth(i);
8636 getLockedCount : function(){
8637 for(var i = 0, len = this.config.length; i < len; i++){
8638 if(!this.isLocked(i)){
8643 return this.config.length;
8647 * Returns the number of columns.
8650 getColumnCount : function(visibleOnly){
8651 if(visibleOnly === true){
8653 for(var i = 0, len = this.config.length; i < len; i++){
8654 if(!this.isHidden(i)){
8660 return this.config.length;
8664 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665 * @param {Function} fn
8666 * @param {Object} scope (optional)
8667 * @return {Array} result
8669 getColumnsBy : function(fn, scope){
8671 for(var i = 0, len = this.config.length; i < len; i++){
8672 var c = this.config[i];
8673 if(fn.call(scope||this, c, i) === true){
8681 * Returns true if the specified column is sortable.
8682 * @param {Number} col The column index
8685 isSortable : function(col){
8686 if(typeof this.config[col].sortable == "undefined"){
8687 return this.defaultSortable;
8689 return this.config[col].sortable;
8693 * Returns the rendering (formatting) function defined for the column.
8694 * @param {Number} col The column index.
8695 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8697 getRenderer : function(col){
8698 if(!this.config[col].renderer){
8699 return Roo.grid.ColumnModel.defaultRenderer;
8701 return this.config[col].renderer;
8705 * Sets the rendering (formatting) function for a column.
8706 * @param {Number} col The column index
8707 * @param {Function} fn The function to use to process the cell's raw data
8708 * to return HTML markup for the grid view. The render function is called with
8709 * the following parameters:<ul>
8710 * <li>Data value.</li>
8711 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712 * <li>css A CSS style string to apply to the table cell.</li>
8713 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715 * <li>Row index</li>
8716 * <li>Column index</li>
8717 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8719 setRenderer : function(col, fn){
8720 this.config[col].renderer = fn;
8724 * Returns the width for the specified column.
8725 * @param {Number} col The column index
8726 * @param (optional) {String} gridSize bootstrap width size.
8729 getColumnWidth : function(col, gridSize)
8731 var cfg = this.config[col];
8733 if (typeof(gridSize) == 'undefined') {
8734 return cfg.width * 1 || this.defaultWidth;
8736 if (gridSize === false) { // if we set it..
8737 return cfg.width || false;
8739 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8741 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8745 return cfg[ sizes[i] ];
8752 * Sets the width for a column.
8753 * @param {Number} col The column index
8754 * @param {Number} width The new width
8756 setColumnWidth : function(col, width, suppressEvent){
8757 this.config[col].width = width;
8758 this.totalWidth = null;
8760 this.fireEvent("widthchange", this, col, width);
8765 * Returns the total width of all columns.
8766 * @param {Boolean} includeHidden True to include hidden column widths
8769 getTotalWidth : function(includeHidden){
8770 if(!this.totalWidth){
8771 this.totalWidth = 0;
8772 for(var i = 0, len = this.config.length; i < len; i++){
8773 if(includeHidden || !this.isHidden(i)){
8774 this.totalWidth += this.getColumnWidth(i);
8778 return this.totalWidth;
8782 * Returns the header for the specified column.
8783 * @param {Number} col The column index
8786 getColumnHeader : function(col){
8787 return this.config[col].header;
8791 * Sets the header for a column.
8792 * @param {Number} col The column index
8793 * @param {String} header The new header
8795 setColumnHeader : function(col, header){
8796 this.config[col].header = header;
8797 this.fireEvent("headerchange", this, col, header);
8801 * Returns the tooltip for the specified column.
8802 * @param {Number} col The column index
8805 getColumnTooltip : function(col){
8806 return this.config[col].tooltip;
8809 * Sets the tooltip for a column.
8810 * @param {Number} col The column index
8811 * @param {String} tooltip The new tooltip
8813 setColumnTooltip : function(col, tooltip){
8814 this.config[col].tooltip = tooltip;
8818 * Returns the dataIndex for the specified column.
8819 * @param {Number} col The column index
8822 getDataIndex : function(col){
8823 return this.config[col].dataIndex;
8827 * Sets the dataIndex for a column.
8828 * @param {Number} col The column index
8829 * @param {Number} dataIndex The new dataIndex
8831 setDataIndex : function(col, dataIndex){
8832 this.config[col].dataIndex = dataIndex;
8838 * Returns true if the cell is editable.
8839 * @param {Number} colIndex The column index
8840 * @param {Number} rowIndex The row index - this is nto actually used..?
8843 isCellEditable : function(colIndex, rowIndex){
8844 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8848 * Returns the editor defined for the cell/column.
8849 * return false or null to disable editing.
8850 * @param {Number} colIndex The column index
8851 * @param {Number} rowIndex The row index
8854 getCellEditor : function(colIndex, rowIndex){
8855 return this.config[colIndex].editor;
8859 * Sets if a column is editable.
8860 * @param {Number} col The column index
8861 * @param {Boolean} editable True if the column is editable
8863 setEditable : function(col, editable){
8864 this.config[col].editable = editable;
8869 * Returns true if the column is hidden.
8870 * @param {Number} colIndex The column index
8873 isHidden : function(colIndex){
8874 return this.config[colIndex].hidden;
8879 * Returns true if the column width cannot be changed
8881 isFixed : function(colIndex){
8882 return this.config[colIndex].fixed;
8886 * Returns true if the column can be resized
8889 isResizable : function(colIndex){
8890 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8893 * Sets if a column is hidden.
8894 * @param {Number} colIndex The column index
8895 * @param {Boolean} hidden True if the column is hidden
8897 setHidden : function(colIndex, hidden){
8898 this.config[colIndex].hidden = hidden;
8899 this.totalWidth = null;
8900 this.fireEvent("hiddenchange", this, colIndex, hidden);
8904 * Sets the editor for a column.
8905 * @param {Number} col The column index
8906 * @param {Object} editor The editor object
8908 setEditor : function(col, editor){
8909 this.config[col].editor = editor;
8912 * Add a column (experimental...) - defaults to adding to the end..
8913 * @param {Object} config
8915 addColumn : function(c)
8918 var i = this.config.length;
8921 if(typeof c.dataIndex == "undefined"){
8924 if(typeof c.renderer == "string"){
8925 c.renderer = Roo.util.Format[c.renderer];
8927 if(typeof c.id == "undefined"){
8930 if(c.editor && c.editor.xtype){
8931 c.editor = Roo.factory(c.editor, Roo.grid);
8933 if(c.editor && c.editor.isFormField){
8934 c.editor = new Roo.grid.GridEditor(c.editor);
8936 this.lookup[c.id] = c;
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8943 if(typeof value == "object") {
8946 if(typeof value == "string" && value.length < 1){
8950 return String.format("{0}", value);
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8957 * Ext JS Library 1.1.1
8958 * Copyright(c) 2006-2007, Ext JS, LLC.
8960 * Originally Released Under LGPL - original licence link has changed is not relivant.
8963 * <script type="text/javascript">
8967 * @class Roo.LoadMask
8968 * A simple utility class for generically masking elements while loading data. If the element being masked has
8969 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8971 * element's UpdateManager load indicator and will be destroyed after the initial load.
8973 * Create a new LoadMask
8974 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975 * @param {Object} config The config object
8977 Roo.LoadMask = function(el, config){
8978 this.el = Roo.get(el);
8979 Roo.apply(this, config);
8981 this.store.on('beforeload', this.onBeforeLoad, this);
8982 this.store.on('load', this.onLoad, this);
8983 this.store.on('loadexception', this.onLoadException, this);
8984 this.removeMask = false;
8986 var um = this.el.getUpdateManager();
8987 um.showLoadIndicator = false; // disable the default indicator
8988 um.on('beforeupdate', this.onBeforeLoad, this);
8989 um.on('update', this.onLoad, this);
8990 um.on('failure', this.onLoad, this);
8991 this.removeMask = true;
8995 Roo.LoadMask.prototype = {
8997 * @cfg {Boolean} removeMask
8998 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9004 * The text to display in a centered loading message box (defaults to 'Loading...')
9008 * @cfg {String} msgCls
9009 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9011 msgCls : 'x-mask-loading',
9014 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9020 * Disables the mask to prevent it from being displayed
9022 disable : function(){
9023 this.disabled = true;
9027 * Enables the mask so that it can be displayed
9029 enable : function(){
9030 this.disabled = false;
9033 onLoadException : function()
9037 if (typeof(arguments[3]) != 'undefined') {
9038 Roo.MessageBox.alert("Error loading",arguments[3]);
9042 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9050 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9055 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9059 onBeforeLoad : function(){
9061 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9066 destroy : function(){
9068 this.store.un('beforeload', this.onBeforeLoad, this);
9069 this.store.un('load', this.onLoad, this);
9070 this.store.un('loadexception', this.onLoadException, this);
9072 var um = this.el.getUpdateManager();
9073 um.un('beforeupdate', this.onBeforeLoad, this);
9074 um.un('update', this.onLoad, this);
9075 um.un('failure', this.onLoad, this);
9079 * @class Roo.bootstrap.Table
9081 * @extends Roo.bootstrap.Component
9082 * @children Roo.bootstrap.TableBody
9083 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9084 * Similar to Roo.grid.Grid
9086 var table = Roo.factory({
9088 xns : Roo.bootstrap,
9089 autoSizeColumns: true,
9096 sortInfo : { direction : 'ASC', field: 'name' },
9098 xtype : 'HttpProxy',
9101 url : 'https://example.com/some.data.url.json'
9104 xtype : 'JsonReader',
9106 fields : [ 'id', 'name', whatever' ],
9113 xtype : 'ColumnModel',
9117 dataIndex : 'is_in_group',
9120 renderer : function(v, x , r) {
9122 return String.format("{0}", v)
9128 xtype : 'RowSelectionModel',
9129 xns : Roo.bootstrap.Table
9130 // you can add listeners to catch selection change here....
9136 grid.render(Roo.get("some-div"));
9139 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9144 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145 * @cfg {Roo.data.Store} store The data store to use
9146 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9148 * @cfg {String} cls table class
9151 * @cfg {string} empty_results Text to display for no results
9152 * @cfg {boolean} striped Should the rows be alternative striped
9153 * @cfg {boolean} bordered Add borders to the table
9154 * @cfg {boolean} hover Add hover highlighting
9155 * @cfg {boolean} condensed Format condensed
9156 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157 * also adds table-responsive (see bootstrap docs for details)
9158 * @cfg {Boolean} loadMask (true|false) default false
9159 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9161 * @cfg {Boolean} rowSelection (true|false) default false
9162 * @cfg {Boolean} cellSelection (true|false) default false
9163 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9164 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9165 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9166 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9167 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9168 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9171 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9174 * Create a new Table
9175 * @param {Object} config The config object
9178 Roo.bootstrap.Table = function(config)
9180 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9183 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188 this.view = this; // compat with grid.
9190 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192 this.sm.grid = this;
9193 this.selModel = Roo.factory(this.sm, Roo.grid);
9194 this.sm = this.selModel;
9195 this.sm.xmodule = this.xmodule || false;
9198 if (this.cm && typeof(this.cm.config) == 'undefined') {
9199 this.colModel = new Roo.grid.ColumnModel(this.cm);
9200 this.cm = this.colModel;
9201 this.cm.xmodule = this.xmodule || false;
9204 this.store= Roo.factory(this.store, Roo.data);
9205 this.ds = this.store;
9206 this.ds.xmodule = this.xmodule || false;
9209 if (this.footer && this.store) {
9210 this.footer.dataSource = this.ds;
9211 this.footer = Roo.factory(this.footer);
9218 * Fires when a cell is clicked
9219 * @param {Roo.bootstrap.Table} this
9220 * @param {Roo.Element} el
9221 * @param {Number} rowIndex
9222 * @param {Number} columnIndex
9223 * @param {Roo.EventObject} e
9227 * @event celldblclick
9228 * Fires when a cell is double clicked
9229 * @param {Roo.bootstrap.Table} this
9230 * @param {Roo.Element} el
9231 * @param {Number} rowIndex
9232 * @param {Number} columnIndex
9233 * @param {Roo.EventObject} e
9235 "celldblclick" : true,
9238 * Fires when a row is clicked
9239 * @param {Roo.bootstrap.Table} this
9240 * @param {Roo.Element} el
9241 * @param {Number} rowIndex
9242 * @param {Roo.EventObject} e
9246 * @event rowdblclick
9247 * Fires when a row is double clicked
9248 * @param {Roo.bootstrap.Table} this
9249 * @param {Roo.Element} el
9250 * @param {Number} rowIndex
9251 * @param {Roo.EventObject} e
9253 "rowdblclick" : true,
9256 * Fires when a mouseover occur
9257 * @param {Roo.bootstrap.Table} this
9258 * @param {Roo.Element} el
9259 * @param {Number} rowIndex
9260 * @param {Number} columnIndex
9261 * @param {Roo.EventObject} e
9266 * Fires when a mouseout occur
9267 * @param {Roo.bootstrap.Table} this
9268 * @param {Roo.Element} el
9269 * @param {Number} rowIndex
9270 * @param {Number} columnIndex
9271 * @param {Roo.EventObject} e
9276 * Fires when a row is rendered, so you can change add a style to it.
9277 * @param {Roo.bootstrap.Table} this
9278 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9282 * @event rowsrendered
9283 * Fires when all the rows have been rendered
9284 * @param {Roo.bootstrap.Table} this
9286 'rowsrendered' : true,
9288 * @event contextmenu
9289 * The raw contextmenu event for the entire grid.
9290 * @param {Roo.EventObject} e
9292 "contextmenu" : true,
9294 * @event rowcontextmenu
9295 * Fires when a row is right clicked
9296 * @param {Roo.bootstrap.Table} this
9297 * @param {Number} rowIndex
9298 * @param {Roo.EventObject} e
9300 "rowcontextmenu" : true,
9302 * @event cellcontextmenu
9303 * Fires when a cell is right clicked
9304 * @param {Roo.bootstrap.Table} this
9305 * @param {Number} rowIndex
9306 * @param {Number} cellIndex
9307 * @param {Roo.EventObject} e
9309 "cellcontextmenu" : true,
9311 * @event headercontextmenu
9312 * Fires when a header is right clicked
9313 * @param {Roo.bootstrap.Table} this
9314 * @param {Number} columnIndex
9315 * @param {Roo.EventObject} e
9317 "headercontextmenu" : true,
9320 * The raw mousedown event for the entire grid.
9321 * @param {Roo.EventObject} e
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9345 enableColumnResize: true,
9346 disableAutoSize: false,
9348 rowSelection : false,
9349 cellSelection : false,
9352 minColumnWidth : 50,
9354 // Roo.Element - the tbody
9355 bodyEl: false, // <tbody> Roo.Element - thead element
9356 headEl: false, // <thead> Roo.Element - thead element
9357 resizeProxy : false, // proxy element for dragging?
9361 container: false, // used by gridpanel...
9367 auto_hide_footer : false,
9369 view: false, // actually points to this..
9371 getAutoCreate : function()
9373 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9380 // this get's auto added by panel.Grid
9381 if (this.scrollBody) {
9382 cfg.cls += ' table-body-fixed';
9385 cfg.cls += ' table-striped';
9389 cfg.cls += ' table-hover';
9391 if (this.bordered) {
9392 cfg.cls += ' table-bordered';
9394 if (this.condensed) {
9395 cfg.cls += ' table-condensed';
9398 if (this.responsive) {
9399 cfg.cls += ' table-responsive';
9403 cfg.cls+= ' ' +this.cls;
9409 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9412 if(this.store || this.cm){
9413 if(this.headerShow){
9414 cfg.cn.push(this.renderHeader());
9417 cfg.cn.push(this.renderBody());
9419 if(this.footerShow){
9420 cfg.cn.push(this.renderFooter());
9422 // where does this come from?
9423 //cfg.cls+= ' TableGrid';
9426 return { cn : [ cfg ] };
9429 initEvents : function()
9431 if(!this.store || !this.cm){
9434 if (this.selModel) {
9435 this.selModel.initEvents();
9439 //Roo.log('initEvents with ds!!!!');
9441 this.bodyEl = this.el.select('tbody', true).first();
9442 this.headEl = this.el.select('thead', true).first();
9443 this.mainFoot = this.el.select('tfoot', true).first();
9448 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9449 e.on('click', this.sort, this);
9453 // why is this done????? = it breaks dialogs??
9454 //this.parent().el.setStyle('position', 'relative');
9458 this.footer.parentId = this.id;
9459 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9462 this.el.select('tfoot tr td').first().addClass('hide');
9467 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9470 this.store.on('load', this.onLoad, this);
9471 this.store.on('beforeload', this.onBeforeLoad, this);
9472 this.store.on('update', this.onUpdate, this);
9473 this.store.on('add', this.onAdd, this);
9474 this.store.on("clear", this.clear, this);
9476 this.el.on("contextmenu", this.onContextMenu, this);
9479 this.cm.on("headerchange", this.onHeaderChange, this);
9480 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9482 //?? does bodyEl get replaced on render?
9483 this.bodyEl.on("click", this.onClick, this);
9484 this.bodyEl.on("dblclick", this.onDblClick, this);
9485 this.bodyEl.on('scroll', this.onBodyScroll, this);
9487 // guessing mainbody will work - this relays usually caught by selmodel at present.
9488 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9491 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9494 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9495 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9500 // Compatibility with grid - we implement all the view features at present.
9501 getView : function()
9506 initCSS : function()
9508 if(this.disableAutoSize) {
9512 var cm = this.cm, styles = [];
9513 this.CSS.removeStyleSheet(this.id + '-cssrules');
9514 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9515 // we can honour xs/sm/md/xl as widths...
9516 // we first have to decide what widht we are currently at...
9517 var sz = Roo.getGridSize();
9521 var cols = []; // visable cols.
9523 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9524 var w = cm.getColumnWidth(i, false);
9526 cols.push( { rel : false, abs : 0 });
9530 cols.push( { rel : false, abs : w });
9532 last = i; // not really..
9535 var w = cm.getColumnWidth(i, sz);
9540 cols.push( { rel : w, abs : false });
9543 var avail = this.bodyEl.dom.clientWidth - total_abs;
9545 var unitWidth = Math.floor(avail / total);
9546 var rem = avail - (unitWidth * total);
9548 var hidden, width, pos = 0 , splithide , left;
9549 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9551 hidden = 'display:none;';
9553 width = 'width:0px;';
9555 if(!cm.isHidden(i)){
9559 // we can honour xs/sm/md/xl ?
9560 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9562 hidden = 'display:none;';
9564 // width should return a small number...
9566 w+=rem; // add the remaining with..
9569 left = "left:" + (pos -4) + "px;";
9570 width = "width:" + w+ "px;";
9573 if (this.responsive) {
9576 hidden = cm.isHidden(i) ? 'display:none;' : '';
9577 splithide = 'display: none;';
9580 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9583 splithide = 'display:none;';
9586 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9587 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9588 // this is the popover version..
9589 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9594 //Roo.log(styles.join(''));
9595 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9601 onContextMenu : function(e, t)
9603 this.processEvent("contextmenu", e);
9606 processEvent : function(name, e)
9608 if (name != 'touchstart' ) {
9609 this.fireEvent(name, e);
9612 var t = e.getTarget();
9614 var cell = Roo.get(t);
9620 if(cell.findParent('tfoot', false, true)){
9624 if(cell.findParent('thead', false, true)){
9626 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9627 cell = Roo.get(t).findParent('th', false, true);
9629 Roo.log("failed to find th in thead?");
9630 Roo.log(e.getTarget());
9635 var cellIndex = cell.dom.cellIndex;
9637 var ename = name == 'touchstart' ? 'click' : name;
9638 this.fireEvent("header" + ename, this, cellIndex, e);
9643 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9644 cell = Roo.get(t).findParent('td', false, true);
9646 Roo.log("failed to find th in tbody?");
9647 Roo.log(e.getTarget());
9652 var row = cell.findParent('tr', false, true);
9653 var cellIndex = cell.dom.cellIndex;
9654 var rowIndex = row.dom.rowIndex - 1;
9658 this.fireEvent("row" + name, this, rowIndex, e);
9662 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9668 onMouseover : function(e, el)
9670 var cell = Roo.get(el);
9676 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9677 cell = cell.findParent('td', false, true);
9680 var row = cell.findParent('tr', false, true);
9681 var cellIndex = cell.dom.cellIndex;
9682 var rowIndex = row.dom.rowIndex - 1; // start from 0
9684 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9688 onMouseout : function(e, el)
9690 var cell = Roo.get(el);
9696 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9697 cell = cell.findParent('td', false, true);
9700 var row = cell.findParent('tr', false, true);
9701 var cellIndex = cell.dom.cellIndex;
9702 var rowIndex = row.dom.rowIndex - 1; // start from 0
9704 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9708 onClick : function(e, el)
9710 var cell = Roo.get(el);
9712 if(!cell || (!this.cellSelection && !this.rowSelection)){
9716 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9717 cell = cell.findParent('td', false, true);
9720 if(!cell || typeof(cell) == 'undefined'){
9724 var row = cell.findParent('tr', false, true);
9726 if(!row || typeof(row) == 'undefined'){
9730 var cellIndex = cell.dom.cellIndex;
9731 var rowIndex = this.getRowIndex(row);
9733 // why??? - should these not be based on SelectionModel?
9734 //if(this.cellSelection){
9735 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9738 //if(this.rowSelection){
9739 this.fireEvent('rowclick', this, row, rowIndex, e);
9744 onDblClick : function(e,el)
9746 var cell = Roo.get(el);
9748 if(!cell || (!this.cellSelection && !this.rowSelection)){
9752 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9753 cell = cell.findParent('td', false, true);
9756 if(!cell || typeof(cell) == 'undefined'){
9760 var row = cell.findParent('tr', false, true);
9762 if(!row || typeof(row) == 'undefined'){
9766 var cellIndex = cell.dom.cellIndex;
9767 var rowIndex = this.getRowIndex(row);
9769 if(this.cellSelection){
9770 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9773 if(this.rowSelection){
9774 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9777 findRowIndex : function(el)
9779 var cell = Roo.get(el);
9783 var row = cell.findParent('tr', false, true);
9785 if(!row || typeof(row) == 'undefined'){
9788 return this.getRowIndex(row);
9790 sort : function(e,el)
9792 var col = Roo.get(el);
9794 if(!col.hasClass('sortable')){
9798 var sort = col.attr('sort');
9801 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9805 this.store.sortInfo = {field : sort, direction : dir};
9808 Roo.log("calling footer first");
9809 this.footer.onClick('first');
9812 this.store.load({ params : { start : 0 } });
9816 renderHeader : function()
9824 this.totalWidth = 0;
9826 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9828 var config = cm.config[i];
9832 cls : 'x-hcol-' + i,
9835 html: cm.getColumnHeader(i)
9838 var tooltip = cm.getColumnTooltip(i);
9840 c.tooltip = tooltip;
9846 if(typeof(config.sortable) != 'undefined' && config.sortable){
9847 c.cls += ' sortable';
9848 c.html = '<i class="fa"></i>' + c.html;
9851 // could use BS4 hidden-..-down
9853 if(typeof(config.lgHeader) != 'undefined'){
9854 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9857 if(typeof(config.mdHeader) != 'undefined'){
9858 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9861 if(typeof(config.smHeader) != 'undefined'){
9862 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9865 if(typeof(config.xsHeader) != 'undefined'){
9866 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9873 if(typeof(config.tooltip) != 'undefined'){
9874 c.tooltip = config.tooltip;
9877 if(typeof(config.colspan) != 'undefined'){
9878 c.colspan = config.colspan;
9881 // hidden is handled by CSS now
9883 if(typeof(config.dataIndex) != 'undefined'){
9884 c.sort = config.dataIndex;
9889 if(typeof(config.align) != 'undefined' && config.align.length){
9890 c.style += ' text-align:' + config.align + ';';
9893 /* width is done in CSS
9894 *if(typeof(config.width) != 'undefined'){
9895 c.style += ' width:' + config.width + 'px;';
9896 this.totalWidth += config.width;
9898 this.totalWidth += 100; // assume minimum of 100 per column?
9902 if(typeof(config.cls) != 'undefined'){
9903 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9905 // this is the bit that doesnt reall work at all...
9907 if (this.responsive) {
9910 ['xs','sm','md','lg'].map(function(size){
9912 if(typeof(config[size]) == 'undefined'){
9916 if (!config[size]) { // 0 = hidden
9917 // BS 4 '0' is treated as hide that column and below.
9918 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9922 c.cls += ' col-' + size + '-' + config[size] + (
9923 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9931 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9942 renderBody : function()
9952 colspan : this.cm.getColumnCount()
9962 renderFooter : function()
9972 colspan : this.cm.getColumnCount()
9986 // Roo.log('ds onload');
9991 var ds = this.store;
9993 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9994 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9995 if (_this.store.sortInfo) {
9997 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9998 e.select('i', true).addClass(['fa-arrow-up']);
10001 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10002 e.select('i', true).addClass(['fa-arrow-down']);
10007 var tbody = this.bodyEl;
10009 if(ds.getCount() > 0){
10010 ds.data.each(function(d,rowIndex){
10011 var row = this.renderRow(cm, ds, rowIndex);
10013 tbody.createChild(row);
10017 if(row.cellObjects.length){
10018 Roo.each(row.cellObjects, function(r){
10019 _this.renderCellObject(r);
10024 } else if (this.empty_results.length) {
10025 this.el.mask(this.empty_results, 'no-spinner');
10028 var tfoot = this.el.select('tfoot', true).first();
10030 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10032 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10034 var total = this.ds.getTotalCount();
10036 if(this.footer.pageSize < total){
10037 this.mainFoot.show();
10041 Roo.each(this.el.select('tbody td', true).elements, function(e){
10042 e.on('mouseover', _this.onMouseover, _this);
10045 Roo.each(this.el.select('tbody td', true).elements, function(e){
10046 e.on('mouseout', _this.onMouseout, _this);
10048 this.fireEvent('rowsrendered', this);
10052 this.initCSS(); /// resize cols
10058 onUpdate : function(ds,record)
10060 this.refreshRow(record);
10064 onRemove : function(ds, record, index, isUpdate){
10065 if(isUpdate !== true){
10066 this.fireEvent("beforerowremoved", this, index, record);
10068 var bt = this.bodyEl.dom;
10070 var rows = this.el.select('tbody > tr', true).elements;
10072 if(typeof(rows[index]) != 'undefined'){
10073 bt.removeChild(rows[index].dom);
10076 // if(bt.rows[index]){
10077 // bt.removeChild(bt.rows[index]);
10080 if(isUpdate !== true){
10081 //this.stripeRows(index);
10082 //this.syncRowHeights(index, index);
10084 this.fireEvent("rowremoved", this, index, record);
10088 onAdd : function(ds, records, rowIndex)
10090 //Roo.log('on Add called');
10091 // - note this does not handle multiple adding very well..
10092 var bt = this.bodyEl.dom;
10093 for (var i =0 ; i < records.length;i++) {
10094 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10095 //Roo.log(records[i]);
10096 //Roo.log(this.store.getAt(rowIndex+i));
10097 this.insertRow(this.store, rowIndex + i, false);
10104 refreshRow : function(record){
10105 var ds = this.store, index;
10106 if(typeof record == 'number'){
10108 record = ds.getAt(index);
10110 index = ds.indexOf(record);
10112 return; // should not happen - but seems to
10115 this.insertRow(ds, index, true);
10117 this.onRemove(ds, record, index+1, true);
10119 //this.syncRowHeights(index, index);
10121 this.fireEvent("rowupdated", this, index, record);
10123 // private - called by RowSelection
10124 onRowSelect : function(rowIndex){
10125 var row = this.getRowDom(rowIndex);
10126 row.addClass(['bg-info','info']);
10128 // private - called by RowSelection
10129 onRowDeselect : function(rowIndex)
10131 if (rowIndex < 0) {
10134 var row = this.getRowDom(rowIndex);
10135 row.removeClass(['bg-info','info']);
10138 * Focuses the specified row.
10139 * @param {Number} row The row index
10141 focusRow : function(row)
10143 //Roo.log('GridView.focusRow');
10144 var x = this.bodyEl.dom.scrollLeft;
10145 this.focusCell(row, 0, false);
10146 this.bodyEl.dom.scrollLeft = x;
10150 * Focuses the specified cell.
10151 * @param {Number} row The row index
10152 * @param {Number} col The column index
10153 * @param {Boolean} hscroll false to disable horizontal scrolling
10155 focusCell : function(row, col, hscroll)
10157 //Roo.log('GridView.focusCell');
10158 var el = this.ensureVisible(row, col, hscroll);
10159 // not sure what focusEL achives = it's a <a> pos relative
10160 //this.focusEl.alignTo(el, "tl-tl");
10162 // this.focusEl.focus();
10164 // this.focusEl.focus.defer(1, this.focusEl);
10169 * Scrolls the specified cell into view
10170 * @param {Number} row The row index
10171 * @param {Number} col The column index
10172 * @param {Boolean} hscroll false to disable horizontal scrolling
10174 ensureVisible : function(row, col, hscroll)
10176 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10177 //return null; //disable for testing.
10178 if(typeof row != "number"){
10179 row = row.rowIndex;
10181 if(row < 0 && row >= this.ds.getCount()){
10184 col = (col !== undefined ? col : 0);
10186 while(cm.isHidden(col)){
10190 var el = this.getCellDom(row, col);
10194 var c = this.bodyEl.dom;
10196 var ctop = parseInt(el.offsetTop, 10);
10197 var cleft = parseInt(el.offsetLeft, 10);
10198 var cbot = ctop + el.offsetHeight;
10199 var cright = cleft + el.offsetWidth;
10201 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10202 var ch = 0; //?? header is not withing the area?
10203 var stop = parseInt(c.scrollTop, 10);
10204 var sleft = parseInt(c.scrollLeft, 10);
10205 var sbot = stop + ch;
10206 var sright = sleft + c.clientWidth;
10208 Roo.log('GridView.ensureVisible:' +
10210 ' c.clientHeight:' + c.clientHeight +
10211 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10219 c.scrollTop = ctop;
10220 //Roo.log("set scrolltop to ctop DISABLE?");
10221 }else if(cbot > sbot){
10222 //Roo.log("set scrolltop to cbot-ch");
10223 c.scrollTop = cbot-ch;
10226 if(hscroll !== false){
10228 c.scrollLeft = cleft;
10229 }else if(cright > sright){
10230 c.scrollLeft = cright-c.clientWidth;
10238 insertRow : function(dm, rowIndex, isUpdate){
10241 this.fireEvent("beforerowsinserted", this, rowIndex);
10243 //var s = this.getScrollState();
10244 var row = this.renderRow(this.cm, this.store, rowIndex);
10245 // insert before rowIndex..
10246 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10250 if(row.cellObjects.length){
10251 Roo.each(row.cellObjects, function(r){
10252 _this.renderCellObject(r);
10257 this.fireEvent("rowsinserted", this, rowIndex);
10258 //this.syncRowHeights(firstRow, lastRow);
10259 //this.stripeRows(firstRow);
10266 getRowDom : function(rowIndex)
10268 var rows = this.el.select('tbody > tr', true).elements;
10270 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10273 getCellDom : function(rowIndex, colIndex)
10275 var row = this.getRowDom(rowIndex);
10276 if (row === false) {
10279 var cols = row.select('td', true).elements;
10280 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10284 // returns the object tree for a tr..
10287 renderRow : function(cm, ds, rowIndex)
10289 var d = ds.getAt(rowIndex);
10293 cls : 'x-row-' + rowIndex,
10297 var cellObjects = [];
10299 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10300 var config = cm.config[i];
10302 var renderer = cm.getRenderer(i);
10306 if(typeof(renderer) !== 'undefined'){
10307 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10309 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10310 // and are rendered into the cells after the row is rendered - using the id for the element.
10312 if(typeof(value) === 'object'){
10322 rowIndex : rowIndex,
10327 this.fireEvent('rowclass', this, rowcfg);
10331 // this might end up displaying HTML?
10332 // this is too messy... - better to only do it on columsn you know are going to be too long
10333 //tooltip : (typeof(value) === 'object') ? '' : value,
10334 cls : rowcfg.rowClass + ' x-col-' + i,
10336 html: (typeof(value) === 'object') ? '' : value
10343 if(typeof(config.colspan) != 'undefined'){
10344 td.colspan = config.colspan;
10349 if(typeof(config.align) != 'undefined' && config.align.length){
10350 td.style += ' text-align:' + config.align + ';';
10352 if(typeof(config.valign) != 'undefined' && config.valign.length){
10353 td.style += ' vertical-align:' + config.valign + ';';
10356 if(typeof(config.width) != 'undefined'){
10357 td.style += ' width:' + config.width + 'px;';
10361 if(typeof(config.cursor) != 'undefined'){
10362 td.style += ' cursor:' + config.cursor + ';';
10365 if(typeof(config.cls) != 'undefined'){
10366 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10368 if (this.responsive) {
10369 ['xs','sm','md','lg'].map(function(size){
10371 if(typeof(config[size]) == 'undefined'){
10377 if (!config[size]) { // 0 = hidden
10378 // BS 4 '0' is treated as hide that column and below.
10379 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10383 td.cls += ' col-' + size + '-' + config[size] + (
10384 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10394 row.cellObjects = cellObjects;
10402 onBeforeLoad : function()
10404 this.el.unmask(); // if needed.
10411 this.el.select('tbody', true).first().dom.innerHTML = '';
10414 * Show or hide a row.
10415 * @param {Number} rowIndex to show or hide
10416 * @param {Boolean} state hide
10418 setRowVisibility : function(rowIndex, state)
10420 var bt = this.bodyEl.dom;
10422 var rows = this.el.select('tbody > tr', true).elements;
10424 if(typeof(rows[rowIndex]) == 'undefined'){
10427 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10432 getSelectionModel : function(){
10433 if(!this.selModel){
10434 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10436 return this.selModel;
10439 * Render the Roo.bootstrap object from renderder
10441 renderCellObject : function(r)
10445 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10447 var t = r.cfg.render(r.container);
10450 Roo.each(r.cfg.cn, function(c){
10452 container: t.getChildContainer(),
10455 _this.renderCellObject(child);
10460 * get the Row Index from a dom element.
10461 * @param {Roo.Element} row The row to look for
10462 * @returns {Number} the row
10464 getRowIndex : function(row)
10468 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10479 * get the header TH element for columnIndex
10480 * @param {Number} columnIndex
10481 * @returns {Roo.Element}
10483 getHeaderIndex: function(colIndex)
10485 var cols = this.headEl.select('th', true).elements;
10486 return cols[colIndex];
10489 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10490 * @param {domElement} cell to look for
10491 * @returns {Number} the column
10493 getCellIndex : function(cell)
10495 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10497 return parseInt(id[1], 10);
10502 * Returns the grid's underlying element = used by panel.Grid
10503 * @return {Element} The element
10505 getGridEl : function(){
10509 * Forces a resize - used by panel.Grid
10510 * @return {Element} The element
10512 autoSize : function()
10514 if(this.disableAutoSize) {
10517 //var ctr = Roo.get(this.container.dom.parentElement);
10518 var ctr = Roo.get(this.el.dom);
10520 var thd = this.getGridEl().select('thead',true).first();
10521 var tbd = this.getGridEl().select('tbody', true).first();
10522 var tfd = this.getGridEl().select('tfoot', true).first();
10524 var cw = ctr.getWidth();
10525 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10529 tbd.setWidth(ctr.getWidth());
10530 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10531 // this needs fixing for various usage - currently only hydra job advers I think..
10533 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10535 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10538 cw = Math.max(cw, this.totalWidth);
10539 this.getGridEl().select('tbody tr',true).setWidth(cw);
10542 // resize 'expandable coloumn?
10544 return; // we doe not have a view in this design..
10547 onBodyScroll: function()
10549 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10551 this.headEl.setStyle({
10552 'position' : 'relative',
10553 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10559 var scrollHeight = this.bodyEl.dom.scrollHeight;
10561 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10563 var height = this.bodyEl.getHeight();
10565 if(scrollHeight - height == scrollTop) {
10567 var total = this.ds.getTotalCount();
10569 if(this.footer.cursor + this.footer.pageSize < total){
10571 this.footer.ds.load({
10573 start : this.footer.cursor + this.footer.pageSize,
10574 limit : this.footer.pageSize
10583 onColumnSplitterMoved : function(i, diff)
10585 this.userResized = true;
10587 var cm = this.colModel;
10589 var w = this.getHeaderIndex(i).getWidth() + diff;
10592 cm.setColumnWidth(i, w, true);
10594 //var cid = cm.getColumnId(i); << not used in this version?
10595 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10597 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10598 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10599 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10601 //this.updateSplitters();
10602 //this.layout(); << ??
10603 this.fireEvent("columnresize", i, w);
10605 onHeaderChange : function()
10607 var header = this.renderHeader();
10608 var table = this.el.select('table', true).first();
10610 this.headEl.remove();
10611 this.headEl = table.createChild(header, this.bodyEl, false);
10613 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10614 e.on('click', this.sort, this);
10617 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10618 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10623 onHiddenChange : function(colModel, colIndex, hidden)
10626 this.cm.setHidden()
10627 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10628 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10630 this.CSS.updateRule(thSelector, "display", "");
10631 this.CSS.updateRule(tdSelector, "display", "");
10634 this.CSS.updateRule(thSelector, "display", "none");
10635 this.CSS.updateRule(tdSelector, "display", "none");
10638 // onload calls initCSS()
10639 this.onHeaderChange();
10643 setColumnWidth: function(col_index, width)
10645 // width = "md-2 xs-2..."
10646 if(!this.colModel.config[col_index]) {
10650 var w = width.split(" ");
10652 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10654 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10657 for(var j = 0; j < w.length; j++) {
10663 var size_cls = w[j].split("-");
10665 if(!Number.isInteger(size_cls[1] * 1)) {
10669 if(!this.colModel.config[col_index][size_cls[0]]) {
10673 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10677 h_row[0].classList.replace(
10678 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10679 "col-"+size_cls[0]+"-"+size_cls[1]
10682 for(var i = 0; i < rows.length; i++) {
10684 var size_cls = w[j].split("-");
10686 if(!Number.isInteger(size_cls[1] * 1)) {
10690 if(!this.colModel.config[col_index][size_cls[0]]) {
10694 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10698 rows[i].classList.replace(
10699 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10700 "col-"+size_cls[0]+"-"+size_cls[1]
10704 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10709 // currently only used to find the split on drag..
10710 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10715 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10716 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10725 * @class Roo.bootstrap.TableCell
10726 * @extends Roo.bootstrap.Component
10727 * @children Roo.bootstrap.Component
10728 * @parent Roo.bootstrap.TableRow
10729 * Bootstrap TableCell class
10731 * @cfg {String} html cell contain text
10732 * @cfg {String} cls cell class
10733 * @cfg {String} tag cell tag (td|th) default td
10734 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10735 * @cfg {String} align Aligns the content in a cell
10736 * @cfg {String} axis Categorizes cells
10737 * @cfg {String} bgcolor Specifies the background color of a cell
10738 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10739 * @cfg {Number} colspan Specifies the number of columns a cell should span
10740 * @cfg {String} headers Specifies one or more header cells a cell is related to
10741 * @cfg {Number} height Sets the height of a cell
10742 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10743 * @cfg {Number} rowspan Sets the number of rows a cell should span
10744 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10745 * @cfg {String} valign Vertical aligns the content in a cell
10746 * @cfg {Number} width Specifies the width of a cell
10749 * Create a new TableCell
10750 * @param {Object} config The config object
10753 Roo.bootstrap.TableCell = function(config){
10754 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10757 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10777 getAutoCreate : function(){
10778 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10785 cfg.tag = this.tag;
10798 cfg.align=this.align
10803 if (this.bgcolor) {
10804 cfg.bgcolor=this.bgcolor
10806 if (this.charoff) {
10807 cfg.charoff=this.charoff
10809 if (this.colspan) {
10810 cfg.colspan=this.colspan
10812 if (this.headers) {
10813 cfg.headers=this.headers
10816 cfg.height=this.height
10819 cfg.nowrap=this.nowrap
10821 if (this.rowspan) {
10822 cfg.rowspan=this.rowspan
10825 cfg.scope=this.scope
10828 cfg.valign=this.valign
10831 cfg.width=this.width
10850 * @class Roo.bootstrap.TableRow
10851 * @extends Roo.bootstrap.Component
10852 * @children Roo.bootstrap.TableCell
10853 * @parent Roo.bootstrap.TableBody
10854 * Bootstrap TableRow class
10855 * @cfg {String} cls row class
10856 * @cfg {String} align Aligns the content in a table row
10857 * @cfg {String} bgcolor Specifies a background color for a table row
10858 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10859 * @cfg {String} valign Vertical aligns the content in a table row
10862 * Create a new TableRow
10863 * @param {Object} config The config object
10866 Roo.bootstrap.TableRow = function(config){
10867 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10870 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10878 getAutoCreate : function(){
10879 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10886 cfg.cls = this.cls;
10889 cfg.align = this.align;
10892 cfg.bgcolor = this.bgcolor;
10895 cfg.charoff = this.charoff;
10898 cfg.valign = this.valign;
10916 * @class Roo.bootstrap.TableBody
10917 * @extends Roo.bootstrap.Component
10918 * @children Roo.bootstrap.TableRow
10919 * @parent Roo.bootstrap.Table
10920 * Bootstrap TableBody class
10921 * @cfg {String} cls element class
10922 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10923 * @cfg {String} align Aligns the content inside the element
10924 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10925 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10928 * Create a new TableBody
10929 * @param {Object} config The config object
10932 Roo.bootstrap.TableBody = function(config){
10933 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10936 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10944 getAutoCreate : function(){
10945 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10955 cfg.tag = this.tag;
10959 cfg.align = this.align;
10962 cfg.charoff = this.charoff;
10965 cfg.valign = this.valign;
10972 // initEvents : function()
10975 // if(!this.store){
10979 // this.store = Roo.factory(this.store, Roo.data);
10980 // this.store.on('load', this.onLoad, this);
10982 // this.store.load();
10986 // onLoad: function ()
10988 // this.fireEvent('load', this);
10998 * Ext JS Library 1.1.1
10999 * Copyright(c) 2006-2007, Ext JS, LLC.
11001 * Originally Released Under LGPL - original licence link has changed is not relivant.
11004 * <script type="text/javascript">
11007 // as we use this in bootstrap.
11008 Roo.namespace('Roo.form');
11010 * @class Roo.form.Action
11011 * Internal Class used to handle form actions
11013 * @param {Roo.form.BasicForm} el The form element or its id
11014 * @param {Object} config Configuration options
11019 // define the action interface
11020 Roo.form.Action = function(form, options){
11022 this.options = options || {};
11025 * Client Validation Failed
11028 Roo.form.Action.CLIENT_INVALID = 'client';
11030 * Server Validation Failed
11033 Roo.form.Action.SERVER_INVALID = 'server';
11035 * Connect to Server Failed
11038 Roo.form.Action.CONNECT_FAILURE = 'connect';
11040 * Reading Data from Server Failed
11043 Roo.form.Action.LOAD_FAILURE = 'load';
11045 Roo.form.Action.prototype = {
11047 failureType : undefined,
11048 response : undefined,
11049 result : undefined,
11051 // interface method
11052 run : function(options){
11056 // interface method
11057 success : function(response){
11061 // interface method
11062 handleResponse : function(response){
11066 // default connection failure
11067 failure : function(response){
11069 this.response = response;
11070 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11071 this.form.afterAction(this, false);
11074 processResponse : function(response){
11075 this.response = response;
11076 if(!response.responseText){
11079 this.result = this.handleResponse(response);
11080 return this.result;
11083 // utility functions used internally
11084 getUrl : function(appendParams){
11085 var url = this.options.url || this.form.url || this.form.el.dom.action;
11087 var p = this.getParams();
11089 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11095 getMethod : function(){
11096 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11099 getParams : function(){
11100 var bp = this.form.baseParams;
11101 var p = this.options.params;
11103 if(typeof p == "object"){
11104 p = Roo.urlEncode(Roo.applyIf(p, bp));
11105 }else if(typeof p == 'string' && bp){
11106 p += '&' + Roo.urlEncode(bp);
11109 p = Roo.urlEncode(bp);
11114 createCallback : function(){
11116 success: this.success,
11117 failure: this.failure,
11119 timeout: (this.form.timeout*1000),
11120 upload: this.form.fileUpload ? this.success : undefined
11125 Roo.form.Action.Submit = function(form, options){
11126 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11129 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11132 haveProgress : false,
11133 uploadComplete : false,
11135 // uploadProgress indicator.
11136 uploadProgress : function()
11138 if (!this.form.progressUrl) {
11142 if (!this.haveProgress) {
11143 Roo.MessageBox.progress("Uploading", "Uploading");
11145 if (this.uploadComplete) {
11146 Roo.MessageBox.hide();
11150 this.haveProgress = true;
11152 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11154 var c = new Roo.data.Connection();
11156 url : this.form.progressUrl,
11161 success : function(req){
11162 //console.log(data);
11166 rdata = Roo.decode(req.responseText)
11168 Roo.log("Invalid data from server..");
11172 if (!rdata || !rdata.success) {
11174 Roo.MessageBox.alert(Roo.encode(rdata));
11177 var data = rdata.data;
11179 if (this.uploadComplete) {
11180 Roo.MessageBox.hide();
11185 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11186 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11189 this.uploadProgress.defer(2000,this);
11192 failure: function(data) {
11193 Roo.log('progress url failed ');
11204 // run get Values on the form, so it syncs any secondary forms.
11205 this.form.getValues();
11207 var o = this.options;
11208 var method = this.getMethod();
11209 var isPost = method == 'POST';
11210 if(o.clientValidation === false || this.form.isValid()){
11212 if (this.form.progressUrl) {
11213 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11214 (new Date() * 1) + '' + Math.random());
11219 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11220 form:this.form.el.dom,
11221 url:this.getUrl(!isPost),
11223 params:isPost ? this.getParams() : null,
11224 isUpload: this.form.fileUpload,
11225 formData : this.form.formData
11228 this.uploadProgress();
11230 }else if (o.clientValidation !== false){ // client validation failed
11231 this.failureType = Roo.form.Action.CLIENT_INVALID;
11232 this.form.afterAction(this, false);
11236 success : function(response)
11238 this.uploadComplete= true;
11239 if (this.haveProgress) {
11240 Roo.MessageBox.hide();
11244 var result = this.processResponse(response);
11245 if(result === true || result.success){
11246 this.form.afterAction(this, true);
11250 this.form.markInvalid(result.errors);
11251 this.failureType = Roo.form.Action.SERVER_INVALID;
11253 this.form.afterAction(this, false);
11255 failure : function(response)
11257 this.uploadComplete= true;
11258 if (this.haveProgress) {
11259 Roo.MessageBox.hide();
11262 this.response = response;
11263 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11264 this.form.afterAction(this, false);
11267 handleResponse : function(response){
11268 if(this.form.errorReader){
11269 var rs = this.form.errorReader.read(response);
11272 for(var i = 0, len = rs.records.length; i < len; i++) {
11273 var r = rs.records[i];
11274 errors[i] = r.data;
11277 if(errors.length < 1){
11281 success : rs.success,
11287 var rt = response.responseText;
11288 if (rt.match(/^\<!--\[CDATA\[/)) {
11289 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11290 rt = rt.replace(/\]\]--\>$/,'');
11293 ret = Roo.decode(rt);
11297 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11307 Roo.form.Action.Load = function(form, options){
11308 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11309 this.reader = this.form.reader;
11312 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11317 Roo.Ajax.request(Roo.apply(
11318 this.createCallback(), {
11319 method:this.getMethod(),
11320 url:this.getUrl(false),
11321 params:this.getParams()
11325 success : function(response){
11327 var result = this.processResponse(response);
11328 if(result === true || !result.success || !result.data){
11329 this.failureType = Roo.form.Action.LOAD_FAILURE;
11330 this.form.afterAction(this, false);
11333 this.form.clearInvalid();
11334 this.form.setValues(result.data);
11335 this.form.afterAction(this, true);
11338 handleResponse : function(response){
11339 if(this.form.reader){
11340 var rs = this.form.reader.read(response);
11341 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11343 success : rs.success,
11347 return Roo.decode(response.responseText);
11351 Roo.form.Action.ACTION_TYPES = {
11352 'load' : Roo.form.Action.Load,
11353 'submit' : Roo.form.Action.Submit
11362 * @class Roo.bootstrap.form.Form
11363 * @extends Roo.bootstrap.Component
11364 * @children Roo.bootstrap.Component
11365 * Bootstrap Form class
11366 * @cfg {String} method GET | POST (default POST)
11367 * @cfg {String} labelAlign top | left (default top)
11368 * @cfg {String} align left | right - for navbars
11369 * @cfg {Boolean} loadMask load mask when submit (default true)
11373 * Create a new Form
11374 * @param {Object} config The config object
11378 Roo.bootstrap.form.Form = function(config){
11380 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11382 Roo.bootstrap.form.Form.popover.apply();
11386 * @event clientvalidation
11387 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11388 * @param {Form} this
11389 * @param {Boolean} valid true if the form has passed client-side validation
11391 clientvalidation: true,
11393 * @event beforeaction
11394 * Fires before any action is performed. Return false to cancel the action.
11395 * @param {Form} this
11396 * @param {Action} action The action to be performed
11398 beforeaction: true,
11400 * @event actionfailed
11401 * Fires when an action fails.
11402 * @param {Form} this
11403 * @param {Action} action The action that failed
11405 actionfailed : true,
11407 * @event actioncomplete
11408 * Fires when an action is completed.
11409 * @param {Form} this
11410 * @param {Action} action The action that completed
11412 actioncomplete : true
11416 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11419 * @cfg {String} method
11420 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11424 * @cfg {String} url
11425 * The URL to use for form actions if one isn't supplied in the action options.
11428 * @cfg {Boolean} fileUpload
11429 * Set to true if this form is a file upload.
11433 * @cfg {Object} baseParams
11434 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11438 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11442 * @cfg {Sting} align (left|right) for navbar forms
11447 activeAction : null,
11450 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11451 * element by passing it or its id or mask the form itself by passing in true.
11454 waitMsgTarget : false,
11459 * @cfg {Boolean} errorMask (true|false) default false
11464 * @cfg {Number} maskOffset Default 100
11469 * @cfg {Boolean} maskBody
11473 getAutoCreate : function(){
11477 method : this.method || 'POST',
11478 id : this.id || Roo.id(),
11481 if (this.parent().xtype.match(/^Nav/)) {
11482 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11486 if (this.labelAlign == 'left' ) {
11487 cfg.cls += ' form-horizontal';
11493 initEvents : function()
11495 this.el.on('submit', this.onSubmit, this);
11496 // this was added as random key presses on the form where triggering form submit.
11497 this.el.on('keypress', function(e) {
11498 if (e.getCharCode() != 13) {
11501 // we might need to allow it for textareas.. and some other items.
11502 // check e.getTarget().
11504 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11508 Roo.log("keypress blocked");
11510 e.preventDefault();
11516 onSubmit : function(e){
11521 * Returns true if client-side validation on the form is successful.
11524 isValid : function(){
11525 var items = this.getItems();
11527 var target = false;
11529 items.each(function(f){
11535 Roo.log('invalid field: ' + f.name);
11539 if(!target && f.el.isVisible(true)){
11545 if(this.errorMask && !valid){
11546 Roo.bootstrap.form.Form.popover.mask(this, target);
11553 * Returns true if any fields in this form have changed since their original load.
11556 isDirty : function(){
11558 var items = this.getItems();
11559 items.each(function(f){
11569 * Performs a predefined action (submit or load) or custom actions you define on this form.
11570 * @param {String} actionName The name of the action type
11571 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11572 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11573 * accept other config options):
11575 Property Type Description
11576 ---------------- --------------- ----------------------------------------------------------------------------------
11577 url String The url for the action (defaults to the form's url)
11578 method String The form method to use (defaults to the form's method, or POST if not defined)
11579 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11580 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11581 validate the form on the client (defaults to false)
11583 * @return {BasicForm} this
11585 doAction : function(action, options){
11586 if(typeof action == 'string'){
11587 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11589 if(this.fireEvent('beforeaction', this, action) !== false){
11590 this.beforeAction(action);
11591 action.run.defer(100, action);
11597 beforeAction : function(action){
11598 var o = action.options;
11603 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11605 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11608 // not really supported yet.. ??
11610 //if(this.waitMsgTarget === true){
11611 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11612 //}else if(this.waitMsgTarget){
11613 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11614 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11616 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11622 afterAction : function(action, success){
11623 this.activeAction = null;
11624 var o = action.options;
11629 Roo.get(document.body).unmask();
11635 //if(this.waitMsgTarget === true){
11636 // this.el.unmask();
11637 //}else if(this.waitMsgTarget){
11638 // this.waitMsgTarget.unmask();
11640 // Roo.MessageBox.updateProgress(1);
11641 // Roo.MessageBox.hide();
11648 Roo.callback(o.success, o.scope, [this, action]);
11649 this.fireEvent('actioncomplete', this, action);
11653 // failure condition..
11654 // we have a scenario where updates need confirming.
11655 // eg. if a locking scenario exists..
11656 // we look for { errors : { needs_confirm : true }} in the response.
11658 (typeof(action.result) != 'undefined') &&
11659 (typeof(action.result.errors) != 'undefined') &&
11660 (typeof(action.result.errors.needs_confirm) != 'undefined')
11663 Roo.log("not supported yet");
11666 Roo.MessageBox.confirm(
11667 "Change requires confirmation",
11668 action.result.errorMsg,
11673 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11683 Roo.callback(o.failure, o.scope, [this, action]);
11684 // show an error message if no failed handler is set..
11685 if (!this.hasListener('actionfailed')) {
11686 Roo.log("need to add dialog support");
11688 Roo.MessageBox.alert("Error",
11689 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11690 action.result.errorMsg :
11691 "Saving Failed, please check your entries or try again"
11696 this.fireEvent('actionfailed', this, action);
11701 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11702 * @param {String} id The value to search for
11705 findField : function(id){
11706 var items = this.getItems();
11707 var field = items.get(id);
11709 items.each(function(f){
11710 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11717 return field || null;
11720 * Mark fields in this form invalid in bulk.
11721 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11722 * @return {BasicForm} this
11724 markInvalid : function(errors){
11725 if(errors instanceof Array){
11726 for(var i = 0, len = errors.length; i < len; i++){
11727 var fieldError = errors[i];
11728 var f = this.findField(fieldError.id);
11730 f.markInvalid(fieldError.msg);
11736 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11737 field.markInvalid(errors[id]);
11741 //Roo.each(this.childForms || [], function (f) {
11742 // f.markInvalid(errors);
11749 * Set values for fields in this form in bulk.
11750 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11751 * @return {BasicForm} this
11753 setValues : function(values){
11754 if(values instanceof Array){ // array of objects
11755 for(var i = 0, len = values.length; i < len; i++){
11757 var f = this.findField(v.id);
11759 f.setValue(v.value);
11760 if(this.trackResetOnLoad){
11761 f.originalValue = f.getValue();
11765 }else{ // object hash
11768 if(typeof values[id] != 'function' && (field = this.findField(id))){
11770 if (field.setFromData &&
11771 field.valueField &&
11772 field.displayField &&
11773 // combos' with local stores can
11774 // be queried via setValue()
11775 // to set their value..
11776 (field.store && !field.store.isLocal)
11780 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11781 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11782 field.setFromData(sd);
11784 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11786 field.setFromData(values);
11789 field.setValue(values[id]);
11793 if(this.trackResetOnLoad){
11794 field.originalValue = field.getValue();
11800 //Roo.each(this.childForms || [], function (f) {
11801 // f.setValues(values);
11808 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11809 * they are returned as an array.
11810 * @param {Boolean} asString
11813 getValues : function(asString){
11814 //if (this.childForms) {
11815 // copy values from the child forms
11816 // Roo.each(this.childForms, function (f) {
11817 // this.setValues(f.getValues());
11823 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11824 if(asString === true){
11827 return Roo.urlDecode(fs);
11831 * Returns the fields in this form as an object with key/value pairs.
11832 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11835 getFieldValues : function(with_hidden)
11837 var items = this.getItems();
11839 items.each(function(f){
11841 if (!f.getName()) {
11845 var v = f.getValue();
11847 if (f.inputType =='radio') {
11848 if (typeof(ret[f.getName()]) == 'undefined') {
11849 ret[f.getName()] = ''; // empty..
11852 if (!f.el.dom.checked) {
11856 v = f.el.dom.value;
11860 if(f.xtype == 'MoneyField'){
11861 ret[f.currencyName] = f.getCurrency();
11864 // not sure if this supported any more..
11865 if ((typeof(v) == 'object') && f.getRawValue) {
11866 v = f.getRawValue() ; // dates..
11868 // combo boxes where name != hiddenName...
11869 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11870 ret[f.name] = f.getRawValue();
11872 ret[f.getName()] = v;
11879 * Clears all invalid messages in this form.
11880 * @return {BasicForm} this
11882 clearInvalid : function(){
11883 var items = this.getItems();
11885 items.each(function(f){
11893 * Resets this form.
11894 * @return {BasicForm} this
11896 reset : function(){
11897 var items = this.getItems();
11898 items.each(function(f){
11902 Roo.each(this.childForms || [], function (f) {
11910 getItems : function()
11912 var r=new Roo.util.MixedCollection(false, function(o){
11913 return o.id || (o.id = Roo.id());
11915 var iter = function(el) {
11922 Roo.each(el.items,function(e) {
11931 hideFields : function(items)
11933 Roo.each(items, function(i){
11935 var f = this.findField(i);
11946 showFields : function(items)
11948 Roo.each(items, function(i){
11950 var f = this.findField(i);
11963 Roo.apply(Roo.bootstrap.form.Form, {
11979 intervalID : false,
11985 if(this.isApplied){
11990 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11991 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11992 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11993 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11996 this.maskEl.top.enableDisplayMode("block");
11997 this.maskEl.left.enableDisplayMode("block");
11998 this.maskEl.bottom.enableDisplayMode("block");
11999 this.maskEl.right.enableDisplayMode("block");
12001 this.toolTip = new Roo.bootstrap.Tooltip({
12002 cls : 'roo-form-error-popover',
12004 'left' : ['r-l', [-2,0], 'right'],
12005 'right' : ['l-r', [2,0], 'left'],
12006 'bottom' : ['tl-bl', [0,2], 'top'],
12007 'top' : [ 'bl-tl', [0,-2], 'bottom']
12011 this.toolTip.render(Roo.get(document.body));
12013 this.toolTip.el.enableDisplayMode("block");
12015 Roo.get(document.body).on('click', function(){
12019 Roo.get(document.body).on('touchstart', function(){
12023 this.isApplied = true
12026 mask : function(form, target)
12030 this.target = target;
12032 if(!this.form.errorMask || !target.el){
12036 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12038 Roo.log(scrollable);
12040 var ot = this.target.el.calcOffsetsTo(scrollable);
12042 var scrollTo = ot[1] - this.form.maskOffset;
12044 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12046 scrollable.scrollTo('top', scrollTo);
12048 var box = this.target.el.getBox();
12050 var zIndex = Roo.bootstrap.Modal.zIndex++;
12053 this.maskEl.top.setStyle('position', 'absolute');
12054 this.maskEl.top.setStyle('z-index', zIndex);
12055 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12056 this.maskEl.top.setLeft(0);
12057 this.maskEl.top.setTop(0);
12058 this.maskEl.top.show();
12060 this.maskEl.left.setStyle('position', 'absolute');
12061 this.maskEl.left.setStyle('z-index', zIndex);
12062 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12063 this.maskEl.left.setLeft(0);
12064 this.maskEl.left.setTop(box.y - this.padding);
12065 this.maskEl.left.show();
12067 this.maskEl.bottom.setStyle('position', 'absolute');
12068 this.maskEl.bottom.setStyle('z-index', zIndex);
12069 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12070 this.maskEl.bottom.setLeft(0);
12071 this.maskEl.bottom.setTop(box.bottom + this.padding);
12072 this.maskEl.bottom.show();
12074 this.maskEl.right.setStyle('position', 'absolute');
12075 this.maskEl.right.setStyle('z-index', zIndex);
12076 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12077 this.maskEl.right.setLeft(box.right + this.padding);
12078 this.maskEl.right.setTop(box.y - this.padding);
12079 this.maskEl.right.show();
12081 this.toolTip.bindEl = this.target.el;
12083 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12085 var tip = this.target.blankText;
12087 if(this.target.getValue() !== '' ) {
12089 if (this.target.invalidText.length) {
12090 tip = this.target.invalidText;
12091 } else if (this.target.regexText.length){
12092 tip = this.target.regexText;
12096 this.toolTip.show(tip);
12098 this.intervalID = window.setInterval(function() {
12099 Roo.bootstrap.form.Form.popover.unmask();
12102 window.onwheel = function(){ return false;};
12104 (function(){ this.isMasked = true; }).defer(500, this);
12108 unmask : function()
12110 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12114 this.maskEl.top.setStyle('position', 'absolute');
12115 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12116 this.maskEl.top.hide();
12118 this.maskEl.left.setStyle('position', 'absolute');
12119 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12120 this.maskEl.left.hide();
12122 this.maskEl.bottom.setStyle('position', 'absolute');
12123 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12124 this.maskEl.bottom.hide();
12126 this.maskEl.right.setStyle('position', 'absolute');
12127 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12128 this.maskEl.right.hide();
12130 this.toolTip.hide();
12132 this.toolTip.el.hide();
12134 window.onwheel = function(){ return true;};
12136 if(this.intervalID){
12137 window.clearInterval(this.intervalID);
12138 this.intervalID = false;
12141 this.isMasked = false;
12151 * Ext JS Library 1.1.1
12152 * Copyright(c) 2006-2007, Ext JS, LLC.
12154 * Originally Released Under LGPL - original licence link has changed is not relivant.
12157 * <script type="text/javascript">
12160 * @class Roo.form.VTypes
12161 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12164 Roo.form.VTypes = function(){
12165 // closure these in so they are only created once.
12166 var alpha = /^[a-zA-Z_]+$/;
12167 var alphanum = /^[a-zA-Z0-9_]+$/;
12168 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12169 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12171 // All these messages and functions are configurable
12174 * The function used to validate email addresses
12175 * @param {String} value The email address
12177 email : function(v){
12178 return email.test(v);
12181 * The error text to display when the email validation function returns false
12184 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12186 * The keystroke filter mask to be applied on email input
12189 emailMask : /[a-z0-9_\.\-@]/i,
12192 * The function used to validate URLs
12193 * @param {String} value The URL
12196 return url.test(v);
12199 * The error text to display when the url validation function returns false
12202 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12205 * The function used to validate alpha values
12206 * @param {String} value The value
12208 alpha : function(v){
12209 return alpha.test(v);
12212 * The error text to display when the alpha validation function returns false
12215 alphaText : 'This field should only contain letters and _',
12217 * The keystroke filter mask to be applied on alpha input
12220 alphaMask : /[a-z_]/i,
12223 * The function used to validate alphanumeric values
12224 * @param {String} value The value
12226 alphanum : function(v){
12227 return alphanum.test(v);
12230 * The error text to display when the alphanumeric validation function returns false
12233 alphanumText : 'This field should only contain letters, numbers and _',
12235 * The keystroke filter mask to be applied on alphanumeric input
12238 alphanumMask : /[a-z0-9_]/i
12248 * @class Roo.bootstrap.form.Input
12249 * @extends Roo.bootstrap.Component
12250 * Bootstrap Input class
12251 * @cfg {Boolean} disabled is it disabled
12252 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12253 * @cfg {String} name name of the input
12254 * @cfg {string} fieldLabel - the label associated
12255 * @cfg {string} placeholder - placeholder to put in text.
12256 * @cfg {string} before - input group add on before
12257 * @cfg {string} after - input group add on after
12258 * @cfg {string} size - (lg|sm) or leave empty..
12259 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12260 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12261 * @cfg {Number} md colspan out of 12 for computer-sized screens
12262 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12263 * @cfg {string} value default value of the input
12264 * @cfg {Number} labelWidth set the width of label
12265 * @cfg {Number} labellg set the width of label (1-12)
12266 * @cfg {Number} labelmd set the width of label (1-12)
12267 * @cfg {Number} labelsm set the width of label (1-12)
12268 * @cfg {Number} labelxs set the width of label (1-12)
12269 * @cfg {String} labelAlign (top|left)
12270 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12271 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12272 * @cfg {String} indicatorpos (left|right) default left
12273 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12274 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12275 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12276 * @cfg {Roo.bootstrap.Button} before Button to show before
12277 * @cfg {Roo.bootstrap.Button} afterButton to show before
12278 * @cfg {String} align (left|center|right) Default left
12279 * @cfg {Boolean} forceFeedback (true|false) Default false
12282 * Create a new Input
12283 * @param {Object} config The config object
12286 Roo.bootstrap.form.Input = function(config){
12288 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12293 * Fires when this field receives input focus.
12294 * @param {Roo.form.Field} this
12299 * Fires when this field loses input focus.
12300 * @param {Roo.form.Field} this
12304 * @event specialkey
12305 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12306 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12307 * @param {Roo.form.Field} this
12308 * @param {Roo.EventObject} e The event object
12313 * Fires just before the field blurs if the field value has changed.
12314 * @param {Roo.form.Field} this
12315 * @param {Mixed} newValue The new value
12316 * @param {Mixed} oldValue The original value
12321 * Fires after the field has been marked as invalid.
12322 * @param {Roo.form.Field} this
12323 * @param {String} msg The validation message
12328 * Fires after the field has been validated with no errors.
12329 * @param {Roo.form.Field} this
12334 * Fires after the key up
12335 * @param {Roo.form.Field} this
12336 * @param {Roo.EventObject} e The event Object
12341 * Fires after the user pastes into input
12342 * @param {Roo.form.Field} this
12343 * @param {Roo.EventObject} e The event Object
12349 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12351 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12352 automatic validation (defaults to "keyup").
12354 validationEvent : "keyup",
12356 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12358 validateOnBlur : true,
12360 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12362 validationDelay : 250,
12364 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12366 focusClass : "x-form-focus", // not needed???
12370 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12372 invalidClass : "has-warning",
12375 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12377 validClass : "has-success",
12380 * @cfg {Boolean} hasFeedback (true|false) default true
12382 hasFeedback : true,
12385 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12387 invalidFeedbackClass : "glyphicon-warning-sign",
12390 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12392 validFeedbackClass : "glyphicon-ok",
12395 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12397 selectOnFocus : false,
12400 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12404 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12409 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12411 disableKeyFilter : false,
12414 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12418 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12422 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12424 blankText : "Please complete this mandatory field",
12427 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12431 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12433 maxLength : Number.MAX_VALUE,
12435 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12437 minLengthText : "The minimum length for this field is {0}",
12439 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12441 maxLengthText : "The maximum length for this field is {0}",
12445 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12446 * If available, this function will be called only after the basic validators all return true, and will be passed the
12447 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12451 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12452 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12453 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12457 * @cfg {String} regexText -- Depricated - use Invalid Text
12462 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12468 autocomplete: false,
12472 inputType : 'text',
12475 placeholder: false,
12480 preventMark: false,
12481 isFormField : true,
12484 labelAlign : false,
12487 formatedValue : false,
12488 forceFeedback : false,
12490 indicatorpos : 'left',
12500 parentLabelAlign : function()
12503 while (parent.parent()) {
12504 parent = parent.parent();
12505 if (typeof(parent.labelAlign) !='undefined') {
12506 return parent.labelAlign;
12513 getAutoCreate : function()
12515 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12521 if(this.inputType != 'hidden'){
12522 cfg.cls = 'form-group' //input-group
12528 type : this.inputType,
12529 value : this.value,
12530 cls : 'form-control',
12531 placeholder : this.placeholder || '',
12532 autocomplete : this.autocomplete || 'new-password'
12534 if (this.inputType == 'file') {
12535 input.style = 'overflow:hidden'; // why not in CSS?
12538 if(this.capture.length){
12539 input.capture = this.capture;
12542 if(this.accept.length){
12543 input.accept = this.accept + "/*";
12547 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12550 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12551 input.maxLength = this.maxLength;
12554 if (this.disabled) {
12555 input.disabled=true;
12558 if (this.readOnly) {
12559 input.readonly=true;
12563 input.name = this.name;
12567 input.cls += ' input-' + this.size;
12571 ['xs','sm','md','lg'].map(function(size){
12572 if (settings[size]) {
12573 cfg.cls += ' col-' + size + '-' + settings[size];
12577 var inputblock = input;
12581 cls: 'glyphicon form-control-feedback'
12584 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12587 cls : 'has-feedback',
12595 if (this.before || this.after) {
12598 cls : 'input-group',
12602 if (this.before && typeof(this.before) == 'string') {
12604 inputblock.cn.push({
12606 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12610 if (this.before && typeof(this.before) == 'object') {
12611 this.before = Roo.factory(this.before);
12613 inputblock.cn.push({
12615 cls : 'roo-input-before input-group-prepend input-group-' +
12616 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12620 inputblock.cn.push(input);
12622 if (this.after && typeof(this.after) == 'string') {
12623 inputblock.cn.push({
12625 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12629 if (this.after && typeof(this.after) == 'object') {
12630 this.after = Roo.factory(this.after);
12632 inputblock.cn.push({
12634 cls : 'roo-input-after input-group-append input-group-' +
12635 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12639 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12640 inputblock.cls += ' has-feedback';
12641 inputblock.cn.push(feedback);
12646 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12647 tooltip : 'This field is required'
12649 if (this.allowBlank ) {
12650 indicator.style = this.allowBlank ? ' display:none' : '';
12652 if (align ==='left' && this.fieldLabel.length) {
12654 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12661 cls : 'control-label col-form-label',
12662 html : this.fieldLabel
12673 var labelCfg = cfg.cn[1];
12674 var contentCfg = cfg.cn[2];
12676 if(this.indicatorpos == 'right'){
12681 cls : 'control-label col-form-label',
12685 html : this.fieldLabel
12699 labelCfg = cfg.cn[0];
12700 contentCfg = cfg.cn[1];
12704 if(this.labelWidth > 12){
12705 labelCfg.style = "width: " + this.labelWidth + 'px';
12708 if(this.labelWidth < 13 && this.labelmd == 0){
12709 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12712 if(this.labellg > 0){
12713 labelCfg.cls += ' col-lg-' + this.labellg;
12714 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12717 if(this.labelmd > 0){
12718 labelCfg.cls += ' col-md-' + this.labelmd;
12719 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12722 if(this.labelsm > 0){
12723 labelCfg.cls += ' col-sm-' + this.labelsm;
12724 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12727 if(this.labelxs > 0){
12728 labelCfg.cls += ' col-xs-' + this.labelxs;
12729 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12733 } else if ( this.fieldLabel.length) {
12740 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12741 tooltip : 'This field is required',
12742 style : this.allowBlank ? ' display:none' : ''
12746 //cls : 'input-group-addon',
12747 html : this.fieldLabel
12755 if(this.indicatorpos == 'right'){
12760 //cls : 'input-group-addon',
12761 html : this.fieldLabel
12766 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12767 tooltip : 'This field is required',
12768 style : this.allowBlank ? ' display:none' : ''
12788 if (this.parentType === 'Navbar' && this.parent().bar) {
12789 cfg.cls += ' navbar-form';
12792 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12793 // on BS4 we do this only if not form
12794 cfg.cls += ' navbar-form';
12802 * return the real input element.
12804 inputEl: function ()
12806 return this.el.select('input.form-control',true).first();
12809 tooltipEl : function()
12811 return this.inputEl();
12814 indicatorEl : function()
12816 if (Roo.bootstrap.version == 4) {
12817 return false; // not enabled in v4 yet.
12820 var indicator = this.el.select('i.roo-required-indicator',true).first();
12830 setDisabled : function(v)
12832 var i = this.inputEl().dom;
12834 i.removeAttribute('disabled');
12838 i.setAttribute('disabled','true');
12840 initEvents : function()
12843 this.inputEl().on("keydown" , this.fireKey, this);
12844 this.inputEl().on("focus", this.onFocus, this);
12845 this.inputEl().on("blur", this.onBlur, this);
12847 this.inputEl().relayEvent('keyup', this);
12848 this.inputEl().relayEvent('paste', this);
12850 this.indicator = this.indicatorEl();
12852 if(this.indicator){
12853 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12856 // reference to original value for reset
12857 this.originalValue = this.getValue();
12858 //Roo.form.TextField.superclass.initEvents.call(this);
12859 if(this.validationEvent == 'keyup'){
12860 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12861 this.inputEl().on('keyup', this.filterValidation, this);
12863 else if(this.validationEvent !== false){
12864 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12867 if(this.selectOnFocus){
12868 this.on("focus", this.preFocus, this);
12871 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12872 this.inputEl().on("keypress", this.filterKeys, this);
12874 this.inputEl().relayEvent('keypress', this);
12877 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12878 this.el.on("click", this.autoSize, this);
12881 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12882 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12885 if (typeof(this.before) == 'object') {
12886 this.before.render(this.el.select('.roo-input-before',true).first());
12888 if (typeof(this.after) == 'object') {
12889 this.after.render(this.el.select('.roo-input-after',true).first());
12892 this.inputEl().on('change', this.onChange, this);
12895 filterValidation : function(e){
12896 if(!e.isNavKeyPress()){
12897 this.validationTask.delay(this.validationDelay);
12901 * Validates the field value
12902 * @return {Boolean} True if the value is valid, else false
12904 validate : function(){
12905 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12906 if(this.disabled || this.validateValue(this.getRawValue())){
12911 this.markInvalid();
12917 * Validates a value according to the field's validation rules and marks the field as invalid
12918 * if the validation fails
12919 * @param {Mixed} value The value to validate
12920 * @return {Boolean} True if the value is valid, else false
12922 validateValue : function(value)
12924 if(this.getVisibilityEl().hasClass('hidden')){
12928 if(value.length < 1) { // if it's blank
12929 if(this.allowBlank){
12935 if(value.length < this.minLength){
12938 if(value.length > this.maxLength){
12942 var vt = Roo.form.VTypes;
12943 if(!vt[this.vtype](value, this)){
12947 if(typeof this.validator == "function"){
12948 var msg = this.validator(value);
12949 if (typeof(msg) == 'string') {
12950 this.invalidText = msg;
12957 if(this.regex && !this.regex.test(value)){
12965 fireKey : function(e){
12966 //Roo.log('field ' + e.getKey());
12967 if(e.isNavKeyPress()){
12968 this.fireEvent("specialkey", this, e);
12971 focus : function (selectText){
12973 this.inputEl().focus();
12974 if(selectText === true){
12975 this.inputEl().dom.select();
12981 onFocus : function(){
12982 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12983 // this.el.addClass(this.focusClass);
12985 if(!this.hasFocus){
12986 this.hasFocus = true;
12987 this.startValue = this.getValue();
12988 this.fireEvent("focus", this);
12992 beforeBlur : Roo.emptyFn,
12996 onBlur : function(){
12998 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12999 //this.el.removeClass(this.focusClass);
13001 this.hasFocus = false;
13002 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13005 var v = this.getValue();
13006 if(String(v) !== String(this.startValue)){
13007 this.fireEvent('change', this, v, this.startValue);
13009 this.fireEvent("blur", this);
13012 onChange : function(e)
13014 var v = this.getValue();
13015 if(String(v) !== String(this.startValue)){
13016 this.fireEvent('change', this, v, this.startValue);
13022 * Resets the current field value to the originally loaded value and clears any validation messages
13024 reset : function(){
13025 this.setValue(this.originalValue);
13029 * Returns the name of the field
13030 * @return {Mixed} name The name field
13032 getName: function(){
13036 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13037 * @return {Mixed} value The field value
13039 getValue : function(){
13041 var v = this.inputEl().getValue();
13046 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13047 * @return {Mixed} value The field value
13049 getRawValue : function(){
13050 var v = this.inputEl().getValue();
13056 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13057 * @param {Mixed} value The value to set
13059 setRawValue : function(v){
13060 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13063 selectText : function(start, end){
13064 var v = this.getRawValue();
13066 start = start === undefined ? 0 : start;
13067 end = end === undefined ? v.length : end;
13068 var d = this.inputEl().dom;
13069 if(d.setSelectionRange){
13070 d.setSelectionRange(start, end);
13071 }else if(d.createTextRange){
13072 var range = d.createTextRange();
13073 range.moveStart("character", start);
13074 range.moveEnd("character", v.length-end);
13081 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13082 * @param {Mixed} value The value to set
13084 setValue : function(v){
13087 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13093 processValue : function(value){
13094 if(this.stripCharsRe){
13095 var newValue = value.replace(this.stripCharsRe, '');
13096 if(newValue !== value){
13097 this.setRawValue(newValue);
13104 preFocus : function(){
13106 if(this.selectOnFocus){
13107 this.inputEl().dom.select();
13110 filterKeys : function(e){
13111 var k = e.getKey();
13112 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13115 var c = e.getCharCode(), cc = String.fromCharCode(c);
13116 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13119 if(!this.maskRe.test(cc)){
13124 * Clear any invalid styles/messages for this field
13126 clearInvalid : function(){
13128 if(!this.el || this.preventMark){ // not rendered
13133 this.el.removeClass([this.invalidClass, 'is-invalid']);
13135 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13137 var feedback = this.el.select('.form-control-feedback', true).first();
13140 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13145 if(this.indicator){
13146 this.indicator.removeClass('visible');
13147 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13150 this.fireEvent('valid', this);
13154 * Mark this field as valid
13156 markValid : function()
13158 if(!this.el || this.preventMark){ // not rendered...
13162 this.el.removeClass([this.invalidClass, this.validClass]);
13163 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13165 var feedback = this.el.select('.form-control-feedback', true).first();
13168 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13171 if(this.indicator){
13172 this.indicator.removeClass('visible');
13173 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13181 if(this.allowBlank && !this.getRawValue().length){
13184 if (Roo.bootstrap.version == 3) {
13185 this.el.addClass(this.validClass);
13187 this.inputEl().addClass('is-valid');
13190 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13192 var feedback = this.el.select('.form-control-feedback', true).first();
13195 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13196 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13201 this.fireEvent('valid', this);
13205 * Mark this field as invalid
13206 * @param {String} msg The validation message
13208 markInvalid : function(msg)
13210 if(!this.el || this.preventMark){ // not rendered
13214 this.el.removeClass([this.invalidClass, this.validClass]);
13215 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13217 var feedback = this.el.select('.form-control-feedback', true).first();
13220 this.el.select('.form-control-feedback', true).first().removeClass(
13221 [this.invalidFeedbackClass, this.validFeedbackClass]);
13228 if(this.allowBlank && !this.getRawValue().length){
13232 if(this.indicator){
13233 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13234 this.indicator.addClass('visible');
13236 if (Roo.bootstrap.version == 3) {
13237 this.el.addClass(this.invalidClass);
13239 this.inputEl().addClass('is-invalid');
13244 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13246 var feedback = this.el.select('.form-control-feedback', true).first();
13249 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13251 if(this.getValue().length || this.forceFeedback){
13252 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13259 this.fireEvent('invalid', this, msg);
13262 SafariOnKeyDown : function(event)
13264 // this is a workaround for a password hang bug on chrome/ webkit.
13265 if (this.inputEl().dom.type != 'password') {
13269 var isSelectAll = false;
13271 if(this.inputEl().dom.selectionEnd > 0){
13272 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13274 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13275 event.preventDefault();
13280 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13282 event.preventDefault();
13283 // this is very hacky as keydown always get's upper case.
13285 var cc = String.fromCharCode(event.getCharCode());
13286 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13290 adjustWidth : function(tag, w){
13291 tag = tag.toLowerCase();
13292 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13293 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13294 if(tag == 'input'){
13297 if(tag == 'textarea'){
13300 }else if(Roo.isOpera){
13301 if(tag == 'input'){
13304 if(tag == 'textarea'){
13312 setFieldLabel : function(v)
13314 if(!this.rendered){
13318 if(this.indicatorEl()){
13319 var ar = this.el.select('label > span',true);
13321 if (ar.elements.length) {
13322 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13323 this.fieldLabel = v;
13327 var br = this.el.select('label',true);
13329 if(br.elements.length) {
13330 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13331 this.fieldLabel = v;
13335 Roo.log('Cannot Found any of label > span || label in input');
13339 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13340 this.fieldLabel = v;
13355 * @class Roo.bootstrap.form.TextArea
13356 * @extends Roo.bootstrap.form.Input
13357 * Bootstrap TextArea class
13358 * @cfg {Number} cols Specifies the visible width of a text area
13359 * @cfg {Number} rows Specifies the visible number of lines in a text area
13360 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13361 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13362 * @cfg {string} html text
13365 * Create a new TextArea
13366 * @param {Object} config The config object
13369 Roo.bootstrap.form.TextArea = function(config){
13370 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13374 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13384 getAutoCreate : function(){
13386 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13392 if(this.inputType != 'hidden'){
13393 cfg.cls = 'form-group' //input-group
13401 value : this.value || '',
13402 html: this.html || '',
13403 cls : 'form-control',
13404 placeholder : this.placeholder || ''
13408 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13409 input.maxLength = this.maxLength;
13413 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13417 input.cols = this.cols;
13420 if (this.readOnly) {
13421 input.readonly = true;
13425 input.name = this.name;
13429 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13433 ['xs','sm','md','lg'].map(function(size){
13434 if (settings[size]) {
13435 cfg.cls += ' col-' + size + '-' + settings[size];
13439 var inputblock = input;
13441 if(this.hasFeedback && !this.allowBlank){
13445 cls: 'glyphicon form-control-feedback'
13449 cls : 'has-feedback',
13458 if (this.before || this.after) {
13461 cls : 'input-group',
13465 inputblock.cn.push({
13467 cls : 'input-group-addon',
13472 inputblock.cn.push(input);
13474 if(this.hasFeedback && !this.allowBlank){
13475 inputblock.cls += ' has-feedback';
13476 inputblock.cn.push(feedback);
13480 inputblock.cn.push({
13482 cls : 'input-group-addon',
13489 if (align ==='left' && this.fieldLabel.length) {
13494 cls : 'control-label',
13495 html : this.fieldLabel
13506 if(this.labelWidth > 12){
13507 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13510 if(this.labelWidth < 13 && this.labelmd == 0){
13511 this.labelmd = this.labelWidth;
13514 if(this.labellg > 0){
13515 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13516 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13519 if(this.labelmd > 0){
13520 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13521 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13524 if(this.labelsm > 0){
13525 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13526 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13529 if(this.labelxs > 0){
13530 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13531 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13534 } else if ( this.fieldLabel.length) {
13539 //cls : 'input-group-addon',
13540 html : this.fieldLabel
13558 if (this.disabled) {
13559 input.disabled=true;
13566 * return the real textarea element.
13568 inputEl: function ()
13570 return this.el.select('textarea.form-control',true).first();
13574 * Clear any invalid styles/messages for this field
13576 clearInvalid : function()
13579 if(!this.el || this.preventMark){ // not rendered
13583 var label = this.el.select('label', true).first();
13584 var icon = this.el.select('i.fa-star', true).first();
13589 this.el.removeClass( this.validClass);
13590 this.inputEl().removeClass('is-invalid');
13592 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13594 var feedback = this.el.select('.form-control-feedback', true).first();
13597 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13602 this.fireEvent('valid', this);
13606 * Mark this field as valid
13608 markValid : function()
13610 if(!this.el || this.preventMark){ // not rendered
13614 this.el.removeClass([this.invalidClass, this.validClass]);
13615 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13617 var feedback = this.el.select('.form-control-feedback', true).first();
13620 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13623 if(this.disabled || this.allowBlank){
13627 var label = this.el.select('label', true).first();
13628 var icon = this.el.select('i.fa-star', true).first();
13633 if (Roo.bootstrap.version == 3) {
13634 this.el.addClass(this.validClass);
13636 this.inputEl().addClass('is-valid');
13640 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13642 var feedback = this.el.select('.form-control-feedback', true).first();
13645 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13646 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13651 this.fireEvent('valid', this);
13655 * Mark this field as invalid
13656 * @param {String} msg The validation message
13658 markInvalid : function(msg)
13660 if(!this.el || this.preventMark){ // not rendered
13664 this.el.removeClass([this.invalidClass, this.validClass]);
13665 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13667 var feedback = this.el.select('.form-control-feedback', true).first();
13670 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13673 if(this.disabled || this.allowBlank){
13677 var label = this.el.select('label', true).first();
13678 var icon = this.el.select('i.fa-star', true).first();
13680 if(!this.getValue().length && label && !icon){
13681 this.el.createChild({
13683 cls : 'text-danger fa fa-lg fa-star',
13684 tooltip : 'This field is required',
13685 style : 'margin-right:5px;'
13689 if (Roo.bootstrap.version == 3) {
13690 this.el.addClass(this.invalidClass);
13692 this.inputEl().addClass('is-invalid');
13695 // fixme ... this may be depricated need to test..
13696 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13698 var feedback = this.el.select('.form-control-feedback', true).first();
13701 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13703 if(this.getValue().length || this.forceFeedback){
13704 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13711 this.fireEvent('invalid', this, msg);
13719 * trigger field - base class for combo..
13724 * @class Roo.bootstrap.form.TriggerField
13725 * @extends Roo.bootstrap.form.Input
13726 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13727 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13728 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13729 * for which you can provide a custom implementation. For example:
13731 var trigger = new Roo.bootstrap.form.TriggerField();
13732 trigger.onTriggerClick = myTriggerFn;
13733 trigger.applyTo('my-field');
13736 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13737 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13738 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13739 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13740 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13743 * Create a new TriggerField.
13744 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13745 * to the base TextField)
13747 Roo.bootstrap.form.TriggerField = function(config){
13748 this.mimicing = false;
13749 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13752 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13754 * @cfg {String} triggerClass A CSS class to apply to the trigger
13757 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13762 * @cfg {Boolean} removable (true|false) special filter default false
13766 /** @cfg {Boolean} grow @hide */
13767 /** @cfg {Number} growMin @hide */
13768 /** @cfg {Number} growMax @hide */
13774 autoSize: Roo.emptyFn,
13778 deferHeight : true,
13781 actionMode : 'wrap',
13786 getAutoCreate : function(){
13788 var align = this.labelAlign || this.parentLabelAlign();
13793 cls: 'form-group' //input-group
13800 type : this.inputType,
13801 cls : 'form-control',
13802 autocomplete: 'new-password',
13803 placeholder : this.placeholder || ''
13807 input.name = this.name;
13810 input.cls += ' input-' + this.size;
13813 if (this.disabled) {
13814 input.disabled=true;
13817 var inputblock = input;
13819 if(this.hasFeedback && !this.allowBlank){
13823 cls: 'glyphicon form-control-feedback'
13826 if(this.removable && !this.editable ){
13828 cls : 'has-feedback',
13834 cls : 'roo-combo-removable-btn close'
13841 cls : 'has-feedback',
13850 if(this.removable && !this.editable ){
13852 cls : 'roo-removable',
13858 cls : 'roo-combo-removable-btn close'
13865 if (this.before || this.after) {
13868 cls : 'input-group',
13872 inputblock.cn.push({
13874 cls : 'input-group-addon input-group-prepend input-group-text',
13879 inputblock.cn.push(input);
13881 if(this.hasFeedback && !this.allowBlank){
13882 inputblock.cls += ' has-feedback';
13883 inputblock.cn.push(feedback);
13887 inputblock.cn.push({
13889 cls : 'input-group-addon input-group-append input-group-text',
13898 var ibwrap = inputblock;
13903 cls: 'roo-select2-choices',
13907 cls: 'roo-select2-search-field',
13919 cls: 'roo-select2-container input-group',
13924 cls: 'form-hidden-field'
13930 if(!this.multiple && this.showToggleBtn){
13936 if (this.caret != false) {
13939 cls: 'fa fa-' + this.caret
13946 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13948 Roo.bootstrap.version == 3 ? caret : '',
13951 cls: 'combobox-clear',
13965 combobox.cls += ' roo-select2-container-multi';
13969 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13970 tooltip : 'This field is required'
13972 if (Roo.bootstrap.version == 4) {
13975 style : 'display:none'
13980 if (align ==='left' && this.fieldLabel.length) {
13982 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13989 cls : 'control-label',
13990 html : this.fieldLabel
14002 var labelCfg = cfg.cn[1];
14003 var contentCfg = cfg.cn[2];
14005 if(this.indicatorpos == 'right'){
14010 cls : 'control-label',
14014 html : this.fieldLabel
14028 labelCfg = cfg.cn[0];
14029 contentCfg = cfg.cn[1];
14032 if(this.labelWidth > 12){
14033 labelCfg.style = "width: " + this.labelWidth + 'px';
14036 if(this.labelWidth < 13 && this.labelmd == 0){
14037 this.labelmd = this.labelWidth;
14040 if(this.labellg > 0){
14041 labelCfg.cls += ' col-lg-' + this.labellg;
14042 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14045 if(this.labelmd > 0){
14046 labelCfg.cls += ' col-md-' + this.labelmd;
14047 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14050 if(this.labelsm > 0){
14051 labelCfg.cls += ' col-sm-' + this.labelsm;
14052 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14055 if(this.labelxs > 0){
14056 labelCfg.cls += ' col-xs-' + this.labelxs;
14057 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14060 } else if ( this.fieldLabel.length) {
14061 // Roo.log(" label");
14066 //cls : 'input-group-addon',
14067 html : this.fieldLabel
14075 if(this.indicatorpos == 'right'){
14083 html : this.fieldLabel
14097 // Roo.log(" no label && no align");
14104 ['xs','sm','md','lg'].map(function(size){
14105 if (settings[size]) {
14106 cfg.cls += ' col-' + size + '-' + settings[size];
14117 onResize : function(w, h){
14118 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14119 // if(typeof w == 'number'){
14120 // var x = w - this.trigger.getWidth();
14121 // this.inputEl().setWidth(this.adjustWidth('input', x));
14122 // this.trigger.setStyle('left', x+'px');
14127 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14130 getResizeEl : function(){
14131 return this.inputEl();
14135 getPositionEl : function(){
14136 return this.inputEl();
14140 alignErrorIcon : function(){
14141 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14145 initEvents : function(){
14149 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14150 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14151 if(!this.multiple && this.showToggleBtn){
14152 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14153 if(this.hideTrigger){
14154 this.trigger.setDisplayed(false);
14156 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14160 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14163 if(this.removable && !this.editable && !this.tickable){
14164 var close = this.closeTriggerEl();
14167 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14168 close.on('click', this.removeBtnClick, this, close);
14172 //this.trigger.addClassOnOver('x-form-trigger-over');
14173 //this.trigger.addClassOnClick('x-form-trigger-click');
14176 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14180 closeTriggerEl : function()
14182 var close = this.el.select('.roo-combo-removable-btn', true).first();
14183 return close ? close : false;
14186 removeBtnClick : function(e, h, el)
14188 e.preventDefault();
14190 if(this.fireEvent("remove", this) !== false){
14192 this.fireEvent("afterremove", this)
14196 createList : function()
14198 this.list = Roo.get(document.body).createChild({
14199 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14200 cls: 'typeahead typeahead-long dropdown-menu shadow',
14201 style: 'display:none'
14204 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14209 initTrigger : function(){
14214 onDestroy : function(){
14216 this.trigger.removeAllListeners();
14217 // this.trigger.remove();
14220 // this.wrap.remove();
14222 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14226 onFocus : function(){
14227 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14229 if(!this.mimicing){
14230 this.wrap.addClass('x-trigger-wrap-focus');
14231 this.mimicing = true;
14232 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14233 if(this.monitorTab){
14234 this.el.on("keydown", this.checkTab, this);
14241 checkTab : function(e){
14242 if(e.getKey() == e.TAB){
14243 this.triggerBlur();
14248 onBlur : function(){
14253 mimicBlur : function(e, t){
14255 if(!this.wrap.contains(t) && this.validateBlur()){
14256 this.triggerBlur();
14262 triggerBlur : function(){
14263 this.mimicing = false;
14264 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14265 if(this.monitorTab){
14266 this.el.un("keydown", this.checkTab, this);
14268 //this.wrap.removeClass('x-trigger-wrap-focus');
14269 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14273 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14274 validateBlur : function(e, t){
14279 onDisable : function(){
14280 this.inputEl().dom.disabled = true;
14281 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14283 // this.wrap.addClass('x-item-disabled');
14288 onEnable : function(){
14289 this.inputEl().dom.disabled = false;
14290 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14292 // this.el.removeClass('x-item-disabled');
14297 onShow : function(){
14298 var ae = this.getActionEl();
14301 ae.dom.style.display = '';
14302 ae.dom.style.visibility = 'visible';
14308 onHide : function(){
14309 var ae = this.getActionEl();
14310 ae.dom.style.display = 'none';
14314 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14315 * by an implementing function.
14317 * @param {EventObject} e
14319 onTriggerClick : Roo.emptyFn
14327 * @class Roo.bootstrap.form.CardUploader
14328 * @extends Roo.bootstrap.Button
14329 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14330 * @cfg {Number} errorTimeout default 3000
14331 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14332 * @cfg {Array} html The button text.
14336 * Create a new CardUploader
14337 * @param {Object} config The config object
14340 Roo.bootstrap.form.CardUploader = function(config){
14344 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14347 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14355 * When a image is clicked on - and needs to display a slideshow or similar..
14356 * @param {Roo.bootstrap.Card} this
14357 * @param {Object} The image information data
14363 * When a the download link is clicked
14364 * @param {Roo.bootstrap.Card} this
14365 * @param {Object} The image information data contains
14372 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14375 errorTimeout : 3000,
14379 fileCollection : false,
14382 getAutoCreate : function()
14386 cls :'form-group' ,
14391 //cls : 'input-group-addon',
14392 html : this.fieldLabel
14400 value : this.value,
14401 cls : 'd-none form-control'
14406 multiple : 'multiple',
14408 cls : 'd-none roo-card-upload-selector'
14412 cls : 'roo-card-uploader-button-container w-100 mb-2'
14415 cls : 'card-columns roo-card-uploader-container'
14425 getChildContainer : function() /// what children are added to.
14427 return this.containerEl;
14430 getButtonContainer : function() /// what children are added to.
14432 return this.el.select(".roo-card-uploader-button-container").first();
14435 initEvents : function()
14438 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14442 xns: Roo.bootstrap,
14445 container_method : 'getButtonContainer' ,
14446 html : this.html, // fix changable?
14449 'click' : function(btn, e) {
14458 this.urlAPI = (window.createObjectURL && window) ||
14459 (window.URL && URL.revokeObjectURL && URL) ||
14460 (window.webkitURL && webkitURL);
14465 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14467 this.selectorEl.on('change', this.onFileSelected, this);
14470 this.images.forEach(function(img) {
14473 this.images = false;
14475 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14481 onClick : function(e)
14483 e.preventDefault();
14485 this.selectorEl.dom.click();
14489 onFileSelected : function(e)
14491 e.preventDefault();
14493 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14497 Roo.each(this.selectorEl.dom.files, function(file){
14498 this.addFile(file);
14507 addFile : function(file)
14510 if(typeof(file) === 'string'){
14511 throw "Add file by name?"; // should not happen
14515 if(!file || !this.urlAPI){
14525 var url = _this.urlAPI.createObjectURL( file);
14528 id : Roo.bootstrap.form.CardUploader.ID--,
14529 is_uploaded : false,
14533 mimetype : file.type,
14541 * addCard - add an Attachment to the uploader
14542 * @param data - the data about the image to upload
14546 title : "Title of file",
14547 is_uploaded : false,
14548 src : "http://.....",
14549 srcfile : { the File upload object },
14550 mimetype : file.type,
14553 .. any other data...
14559 addCard : function (data)
14561 // hidden input element?
14562 // if the file is not an image...
14563 //then we need to use something other that and header_image
14568 xns : Roo.bootstrap,
14569 xtype : 'CardFooter',
14572 xns : Roo.bootstrap,
14578 xns : Roo.bootstrap,
14580 html : String.format("<small>{0}</small>", data.title),
14581 cls : 'col-10 text-left',
14586 click : function() {
14588 t.fireEvent( "download", t, data );
14594 xns : Roo.bootstrap,
14596 style: 'max-height: 28px; ',
14602 click : function() {
14603 t.removeCard(data.id)
14615 var cn = this.addxtype(
14618 xns : Roo.bootstrap,
14621 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14622 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14623 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14628 initEvents : function() {
14629 Roo.bootstrap.Card.prototype.initEvents.call(this);
14631 this.imgEl = this.el.select('.card-img-top').first();
14633 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14634 this.imgEl.set({ 'pointer' : 'cursor' });
14637 this.getCardFooter().addClass('p-1');
14644 // dont' really need ot update items.
14645 // this.items.push(cn);
14646 this.fileCollection.add(cn);
14648 if (!data.srcfile) {
14649 this.updateInput();
14654 var reader = new FileReader();
14655 reader.addEventListener("load", function() {
14656 data.srcdata = reader.result;
14659 reader.readAsDataURL(data.srcfile);
14664 removeCard : function(id)
14667 var card = this.fileCollection.get(id);
14668 card.data.is_deleted = 1;
14669 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14670 //this.fileCollection.remove(card);
14671 //this.items = this.items.filter(function(e) { return e != card });
14672 // dont' really need ot update items.
14673 card.el.dom.parentNode.removeChild(card.el.dom);
14674 this.updateInput();
14680 this.fileCollection.each(function(card) {
14681 if (card.el.dom && card.el.dom.parentNode) {
14682 card.el.dom.parentNode.removeChild(card.el.dom);
14685 this.fileCollection.clear();
14686 this.updateInput();
14689 updateInput : function()
14692 this.fileCollection.each(function(e) {
14696 this.inputEl().dom.value = JSON.stringify(data);
14706 Roo.bootstrap.form.CardUploader.ID = -1;/*
14708 * Ext JS Library 1.1.1
14709 * Copyright(c) 2006-2007, Ext JS, LLC.
14711 * Originally Released Under LGPL - original licence link has changed is not relivant.
14714 * <script type="text/javascript">
14719 * @class Roo.data.SortTypes
14721 * Defines the default sorting (casting?) comparison functions used when sorting data.
14723 Roo.data.SortTypes = {
14725 * Default sort that does nothing
14726 * @param {Mixed} s The value being converted
14727 * @return {Mixed} The comparison value
14729 none : function(s){
14734 * The regular expression used to strip tags
14738 stripTagsRE : /<\/?[^>]+>/gi,
14741 * Strips all HTML tags to sort on text only
14742 * @param {Mixed} s The value being converted
14743 * @return {String} The comparison value
14745 asText : function(s){
14746 return String(s).replace(this.stripTagsRE, "");
14750 * Strips all HTML tags to sort on text only - Case insensitive
14751 * @param {Mixed} s The value being converted
14752 * @return {String} The comparison value
14754 asUCText : function(s){
14755 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14759 * Case insensitive string
14760 * @param {Mixed} s The value being converted
14761 * @return {String} The comparison value
14763 asUCString : function(s) {
14764 return String(s).toUpperCase();
14769 * @param {Mixed} s The value being converted
14770 * @return {Number} The comparison value
14772 asDate : function(s) {
14776 if(s instanceof Date){
14777 return s.getTime();
14779 return Date.parse(String(s));
14784 * @param {Mixed} s The value being converted
14785 * @return {Float} The comparison value
14787 asFloat : function(s) {
14788 var val = parseFloat(String(s).replace(/,/g, ""));
14797 * @param {Mixed} s The value being converted
14798 * @return {Number} The comparison value
14800 asInt : function(s) {
14801 var val = parseInt(String(s).replace(/,/g, ""));
14809 * Ext JS Library 1.1.1
14810 * Copyright(c) 2006-2007, Ext JS, LLC.
14812 * Originally Released Under LGPL - original licence link has changed is not relivant.
14815 * <script type="text/javascript">
14819 * @class Roo.data.Record
14820 * Instances of this class encapsulate both record <em>definition</em> information, and record
14821 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14822 * to access Records cached in an {@link Roo.data.Store} object.<br>
14824 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14825 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14828 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14830 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14831 * {@link #create}. The parameters are the same.
14832 * @param {Array} data An associative Array of data values keyed by the field name.
14833 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14834 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14835 * not specified an integer id is generated.
14837 Roo.data.Record = function(data, id){
14838 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14843 * Generate a constructor for a specific record layout.
14844 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14845 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14846 * Each field definition object may contain the following properties: <ul>
14847 * <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,
14848 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14849 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14850 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14851 * is being used, then this is a string containing the javascript expression to reference the data relative to
14852 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14853 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14854 * this may be omitted.</p></li>
14855 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14856 * <ul><li>auto (Default, implies no conversion)</li>
14861 * <li>date</li></ul></p></li>
14862 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14863 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14864 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14865 * by the Reader into an object that will be stored in the Record. It is passed the
14866 * following parameters:<ul>
14867 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14869 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14871 * <br>usage:<br><pre><code>
14872 var TopicRecord = Roo.data.Record.create(
14873 {name: 'title', mapping: 'topic_title'},
14874 {name: 'author', mapping: 'username'},
14875 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14876 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14877 {name: 'lastPoster', mapping: 'user2'},
14878 {name: 'excerpt', mapping: 'post_text'}
14881 var myNewRecord = new TopicRecord({
14882 title: 'Do my job please',
14885 lastPost: new Date(),
14886 lastPoster: 'Animal',
14887 excerpt: 'No way dude!'
14889 myStore.add(myNewRecord);
14894 Roo.data.Record.create = function(o){
14895 var f = function(){
14896 f.superclass.constructor.apply(this, arguments);
14898 Roo.extend(f, Roo.data.Record);
14899 var p = f.prototype;
14900 p.fields = new Roo.util.MixedCollection(false, function(field){
14903 for(var i = 0, len = o.length; i < len; i++){
14904 p.fields.add(new Roo.data.Field(o[i]));
14906 f.getField = function(name){
14907 return p.fields.get(name);
14912 Roo.data.Record.AUTO_ID = 1000;
14913 Roo.data.Record.EDIT = 'edit';
14914 Roo.data.Record.REJECT = 'reject';
14915 Roo.data.Record.COMMIT = 'commit';
14917 Roo.data.Record.prototype = {
14919 * Readonly flag - true if this record has been modified.
14928 join : function(store){
14929 this.store = store;
14933 * Set the named field to the specified value.
14934 * @param {String} name The name of the field to set.
14935 * @param {Object} value The value to set the field to.
14937 set : function(name, value){
14938 if(this.data[name] == value){
14942 if(!this.modified){
14943 this.modified = {};
14945 if(typeof this.modified[name] == 'undefined'){
14946 this.modified[name] = this.data[name];
14948 this.data[name] = value;
14949 if(!this.editing && this.store){
14950 this.store.afterEdit(this);
14955 * Get the value of the named field.
14956 * @param {String} name The name of the field to get the value of.
14957 * @return {Object} The value of the field.
14959 get : function(name){
14960 return this.data[name];
14964 beginEdit : function(){
14965 this.editing = true;
14966 this.modified = {};
14970 cancelEdit : function(){
14971 this.editing = false;
14972 delete this.modified;
14976 endEdit : function(){
14977 this.editing = false;
14978 if(this.dirty && this.store){
14979 this.store.afterEdit(this);
14984 * Usually called by the {@link Roo.data.Store} which owns the Record.
14985 * Rejects all changes made to the Record since either creation, or the last commit operation.
14986 * Modified fields are reverted to their original values.
14988 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14989 * of reject operations.
14991 reject : function(){
14992 var m = this.modified;
14994 if(typeof m[n] != "function"){
14995 this.data[n] = m[n];
14998 this.dirty = false;
14999 delete this.modified;
15000 this.editing = false;
15002 this.store.afterReject(this);
15007 * Usually called by the {@link Roo.data.Store} which owns the Record.
15008 * Commits all changes made to the Record since either creation, or the last commit operation.
15010 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15011 * of commit operations.
15013 commit : function(){
15014 this.dirty = false;
15015 delete this.modified;
15016 this.editing = false;
15018 this.store.afterCommit(this);
15023 hasError : function(){
15024 return this.error != null;
15028 clearError : function(){
15033 * Creates a copy of this record.
15034 * @param {String} id (optional) A new record id if you don't want to use this record's id
15037 copy : function(newId) {
15038 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15042 * Ext JS Library 1.1.1
15043 * Copyright(c) 2006-2007, Ext JS, LLC.
15045 * Originally Released Under LGPL - original licence link has changed is not relivant.
15048 * <script type="text/javascript">
15054 * @class Roo.data.Store
15055 * @extends Roo.util.Observable
15056 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15057 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15059 * 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
15060 * has no knowledge of the format of the data returned by the Proxy.<br>
15062 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15063 * instances from the data object. These records are cached and made available through accessor functions.
15065 * Creates a new Store.
15066 * @param {Object} config A config object containing the objects needed for the Store to access data,
15067 * and read the data into Records.
15069 Roo.data.Store = function(config){
15070 this.data = new Roo.util.MixedCollection(false);
15071 this.data.getKey = function(o){
15074 this.baseParams = {};
15076 this.paramNames = {
15081 "multisort" : "_multisort"
15084 if(config && config.data){
15085 this.inlineData = config.data;
15086 delete config.data;
15089 Roo.apply(this, config);
15091 if(this.reader){ // reader passed
15092 this.reader = Roo.factory(this.reader, Roo.data);
15093 this.reader.xmodule = this.xmodule || false;
15094 if(!this.recordType){
15095 this.recordType = this.reader.recordType;
15097 if(this.reader.onMetaChange){
15098 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15102 if(this.recordType){
15103 this.fields = this.recordType.prototype.fields;
15105 this.modified = [];
15109 * @event datachanged
15110 * Fires when the data cache has changed, and a widget which is using this Store
15111 * as a Record cache should refresh its view.
15112 * @param {Store} this
15114 datachanged : true,
15116 * @event metachange
15117 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15118 * @param {Store} this
15119 * @param {Object} meta The JSON metadata
15124 * Fires when Records have been added to the Store
15125 * @param {Store} this
15126 * @param {Roo.data.Record[]} records The array of Records added
15127 * @param {Number} index The index at which the record(s) were added
15132 * Fires when a Record has been removed from the Store
15133 * @param {Store} this
15134 * @param {Roo.data.Record} record The Record that was removed
15135 * @param {Number} index The index at which the record was removed
15140 * Fires when a Record has been updated
15141 * @param {Store} this
15142 * @param {Roo.data.Record} record The Record that was updated
15143 * @param {String} operation The update operation being performed. Value may be one of:
15145 Roo.data.Record.EDIT
15146 Roo.data.Record.REJECT
15147 Roo.data.Record.COMMIT
15153 * Fires when the data cache has been cleared.
15154 * @param {Store} this
15158 * @event beforeload
15159 * Fires before a request is made for a new data object. If the beforeload handler returns false
15160 * the load action will be canceled.
15161 * @param {Store} this
15162 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15166 * @event beforeloadadd
15167 * Fires after a new set of Records has been loaded.
15168 * @param {Store} this
15169 * @param {Roo.data.Record[]} records The Records that were loaded
15170 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15172 beforeloadadd : true,
15175 * Fires after a new set of Records has been loaded, before they are added to the store.
15176 * @param {Store} this
15177 * @param {Roo.data.Record[]} records The Records that were loaded
15178 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15179 * @params {Object} return from reader
15183 * @event loadexception
15184 * Fires if an exception occurs in the Proxy during loading.
15185 * Called with the signature of the Proxy's "loadexception" event.
15186 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15189 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15190 * @param {Object} load options
15191 * @param {Object} jsonData from your request (normally this contains the Exception)
15193 loadexception : true
15197 this.proxy = Roo.factory(this.proxy, Roo.data);
15198 this.proxy.xmodule = this.xmodule || false;
15199 this.relayEvents(this.proxy, ["loadexception"]);
15201 this.sortToggle = {};
15202 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15204 Roo.data.Store.superclass.constructor.call(this);
15206 if(this.inlineData){
15207 this.loadData(this.inlineData);
15208 delete this.inlineData;
15212 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15214 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15215 * without a remote query - used by combo/forms at present.
15219 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15222 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15225 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15226 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15229 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15230 * on any HTTP request
15233 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15236 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15240 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15241 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15243 remoteSort : false,
15246 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15247 * loaded or when a record is removed. (defaults to false).
15249 pruneModifiedRecords : false,
15252 lastOptions : null,
15255 * Add Records to the Store and fires the add event.
15256 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15258 add : function(records){
15259 records = [].concat(records);
15260 for(var i = 0, len = records.length; i < len; i++){
15261 records[i].join(this);
15263 var index = this.data.length;
15264 this.data.addAll(records);
15265 this.fireEvent("add", this, records, index);
15269 * Remove a Record from the Store and fires the remove event.
15270 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15272 remove : function(record){
15273 var index = this.data.indexOf(record);
15274 this.data.removeAt(index);
15276 if(this.pruneModifiedRecords){
15277 this.modified.remove(record);
15279 this.fireEvent("remove", this, record, index);
15283 * Remove all Records from the Store and fires the clear event.
15285 removeAll : function(){
15287 if(this.pruneModifiedRecords){
15288 this.modified = [];
15290 this.fireEvent("clear", this);
15294 * Inserts Records to the Store at the given index and fires the add event.
15295 * @param {Number} index The start index at which to insert the passed Records.
15296 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15298 insert : function(index, records){
15299 records = [].concat(records);
15300 for(var i = 0, len = records.length; i < len; i++){
15301 this.data.insert(index, records[i]);
15302 records[i].join(this);
15304 this.fireEvent("add", this, records, index);
15308 * Get the index within the cache of the passed Record.
15309 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15310 * @return {Number} The index of the passed Record. Returns -1 if not found.
15312 indexOf : function(record){
15313 return this.data.indexOf(record);
15317 * Get the index within the cache of the Record with the passed id.
15318 * @param {String} id The id of the Record to find.
15319 * @return {Number} The index of the Record. Returns -1 if not found.
15321 indexOfId : function(id){
15322 return this.data.indexOfKey(id);
15326 * Get the Record with the specified id.
15327 * @param {String} id The id of the Record to find.
15328 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15330 getById : function(id){
15331 return this.data.key(id);
15335 * Get the Record at the specified index.
15336 * @param {Number} index The index of the Record to find.
15337 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15339 getAt : function(index){
15340 return this.data.itemAt(index);
15344 * Returns a range of Records between specified indices.
15345 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15346 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15347 * @return {Roo.data.Record[]} An array of Records
15349 getRange : function(start, end){
15350 return this.data.getRange(start, end);
15354 storeOptions : function(o){
15355 o = Roo.apply({}, o);
15358 this.lastOptions = o;
15362 * Loads the Record cache from the configured Proxy using the configured Reader.
15364 * If using remote paging, then the first load call must specify the <em>start</em>
15365 * and <em>limit</em> properties in the options.params property to establish the initial
15366 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15368 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15369 * and this call will return before the new data has been loaded. Perform any post-processing
15370 * in a callback function, or in a "load" event handler.</strong>
15372 * @param {Object} options An object containing properties which control loading options:<ul>
15373 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15374 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15377 data : data, // array of key=>value data like JsonReader
15378 total : data.length,
15384 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15385 * passed the following arguments:<ul>
15386 * <li>r : Roo.data.Record[]</li>
15387 * <li>options: Options object from the load call</li>
15388 * <li>success: Boolean success indicator</li></ul></li>
15389 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15390 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15393 load : function(options){
15394 options = options || {};
15395 if(this.fireEvent("beforeload", this, options) !== false){
15396 this.storeOptions(options);
15397 var p = Roo.apply(options.params || {}, this.baseParams);
15398 // if meta was not loaded from remote source.. try requesting it.
15399 if (!this.reader.metaFromRemote) {
15400 p._requestMeta = 1;
15402 if(this.sortInfo && this.remoteSort){
15403 var pn = this.paramNames;
15404 p[pn["sort"]] = this.sortInfo.field;
15405 p[pn["dir"]] = this.sortInfo.direction;
15407 if (this.multiSort) {
15408 var pn = this.paramNames;
15409 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15412 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15417 * Reloads the Record cache from the configured Proxy using the configured Reader and
15418 * the options from the last load operation performed.
15419 * @param {Object} options (optional) An object containing properties which may override the options
15420 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15421 * the most recently used options are reused).
15423 reload : function(options){
15424 this.load(Roo.applyIf(options||{}, this.lastOptions));
15428 // Called as a callback by the Reader during a load operation.
15429 loadRecords : function(o, options, success){
15432 if(success !== false){
15433 this.fireEvent("load", this, [], options, o);
15435 if(options.callback){
15436 options.callback.call(options.scope || this, [], options, false);
15440 // if data returned failure - throw an exception.
15441 if (o.success === false) {
15442 // show a message if no listener is registered.
15443 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15444 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15446 // loadmask wil be hooked into this..
15447 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15450 var r = o.records, t = o.totalRecords || r.length;
15452 this.fireEvent("beforeloadadd", this, r, options, o);
15454 if(!options || options.add !== true){
15455 if(this.pruneModifiedRecords){
15456 this.modified = [];
15458 for(var i = 0, len = r.length; i < len; i++){
15462 this.data = this.snapshot;
15463 delete this.snapshot;
15466 this.data.addAll(r);
15467 this.totalLength = t;
15469 this.fireEvent("datachanged", this);
15471 this.totalLength = Math.max(t, this.data.length+r.length);
15475 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15477 var e = new Roo.data.Record({});
15479 e.set(this.parent.displayField, this.parent.emptyTitle);
15480 e.set(this.parent.valueField, '');
15485 this.fireEvent("load", this, r, options, o);
15486 if(options.callback){
15487 options.callback.call(options.scope || this, r, options, true);
15493 * Loads data from a passed data block. A Reader which understands the format of the data
15494 * must have been configured in the constructor.
15495 * @param {Object} data The data block from which to read the Records. The format of the data expected
15496 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15497 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15499 loadData : function(o, append){
15500 var r = this.reader.readRecords(o);
15501 this.loadRecords(r, {add: append}, true);
15505 * using 'cn' the nested child reader read the child array into it's child stores.
15506 * @param {Object} rec The record with a 'children array
15508 loadDataFromChildren : function(rec)
15510 this.loadData(this.reader.toLoadData(rec));
15515 * Gets the number of cached records.
15517 * <em>If using paging, this may not be the total size of the dataset. If the data object
15518 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15519 * the data set size</em>
15521 getCount : function(){
15522 return this.data.length || 0;
15526 * Gets the total number of records in the dataset as returned by the server.
15528 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15529 * the dataset size</em>
15531 getTotalCount : function(){
15532 return this.totalLength || 0;
15536 * Returns the sort state of the Store as an object with two properties:
15538 field {String} The name of the field by which the Records are sorted
15539 direction {String} The sort order, "ASC" or "DESC"
15542 getSortState : function(){
15543 return this.sortInfo;
15547 applySort : function(){
15548 if(this.sortInfo && !this.remoteSort){
15549 var s = this.sortInfo, f = s.field;
15550 var st = this.fields.get(f).sortType;
15551 var fn = function(r1, r2){
15552 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15553 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15555 this.data.sort(s.direction, fn);
15556 if(this.snapshot && this.snapshot != this.data){
15557 this.snapshot.sort(s.direction, fn);
15563 * Sets the default sort column and order to be used by the next load operation.
15564 * @param {String} fieldName The name of the field to sort by.
15565 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15567 setDefaultSort : function(field, dir){
15568 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15572 * Sort the Records.
15573 * If remote sorting is used, the sort is performed on the server, and the cache is
15574 * reloaded. If local sorting is used, the cache is sorted internally.
15575 * @param {String} fieldName The name of the field to sort by.
15576 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15578 sort : function(fieldName, dir){
15579 var f = this.fields.get(fieldName);
15581 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15583 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15584 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15589 this.sortToggle[f.name] = dir;
15590 this.sortInfo = {field: f.name, direction: dir};
15591 if(!this.remoteSort){
15593 this.fireEvent("datachanged", this);
15595 this.load(this.lastOptions);
15600 * Calls the specified function for each of the Records in the cache.
15601 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15602 * Returning <em>false</em> aborts and exits the iteration.
15603 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15605 each : function(fn, scope){
15606 this.data.each(fn, scope);
15610 * Gets all records modified since the last commit. Modified records are persisted across load operations
15611 * (e.g., during paging).
15612 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15614 getModifiedRecords : function(){
15615 return this.modified;
15619 createFilterFn : function(property, value, anyMatch){
15620 if(!value.exec){ // not a regex
15621 value = String(value);
15622 if(value.length == 0){
15625 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15627 return function(r){
15628 return value.test(r.data[property]);
15633 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15634 * @param {String} property A field on your records
15635 * @param {Number} start The record index to start at (defaults to 0)
15636 * @param {Number} end The last record index to include (defaults to length - 1)
15637 * @return {Number} The sum
15639 sum : function(property, start, end){
15640 var rs = this.data.items, v = 0;
15641 start = start || 0;
15642 end = (end || end === 0) ? end : rs.length-1;
15644 for(var i = start; i <= end; i++){
15645 v += (rs[i].data[property] || 0);
15651 * Filter the records by a specified property.
15652 * @param {String} field A field on your records
15653 * @param {String/RegExp} value Either a string that the field
15654 * should start with or a RegExp to test against the field
15655 * @param {Boolean} anyMatch True to match any part not just the beginning
15657 filter : function(property, value, anyMatch){
15658 var fn = this.createFilterFn(property, value, anyMatch);
15659 return fn ? this.filterBy(fn) : this.clearFilter();
15663 * Filter by a function. The specified function will be called with each
15664 * record in this data source. If the function returns true the record is included,
15665 * otherwise it is filtered.
15666 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15667 * @param {Object} scope (optional) The scope of the function (defaults to this)
15669 filterBy : function(fn, scope){
15670 this.snapshot = this.snapshot || this.data;
15671 this.data = this.queryBy(fn, scope||this);
15672 this.fireEvent("datachanged", this);
15676 * Query the records by a specified property.
15677 * @param {String} field A field on your records
15678 * @param {String/RegExp} value Either a string that the field
15679 * should start with or a RegExp to test against the field
15680 * @param {Boolean} anyMatch True to match any part not just the beginning
15681 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15683 query : function(property, value, anyMatch){
15684 var fn = this.createFilterFn(property, value, anyMatch);
15685 return fn ? this.queryBy(fn) : this.data.clone();
15689 * Query by a function. The specified function will be called with each
15690 * record in this data source. If the function returns true the record is included
15692 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15693 * @param {Object} scope (optional) The scope of the function (defaults to this)
15694 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15696 queryBy : function(fn, scope){
15697 var data = this.snapshot || this.data;
15698 return data.filterBy(fn, scope||this);
15702 * Collects unique values for a particular dataIndex from this store.
15703 * @param {String} dataIndex The property to collect
15704 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15705 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15706 * @return {Array} An array of the unique values
15708 collect : function(dataIndex, allowNull, bypassFilter){
15709 var d = (bypassFilter === true && this.snapshot) ?
15710 this.snapshot.items : this.data.items;
15711 var v, sv, r = [], l = {};
15712 for(var i = 0, len = d.length; i < len; i++){
15713 v = d[i].data[dataIndex];
15715 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15724 * Revert to a view of the Record cache with no filtering applied.
15725 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15727 clearFilter : function(suppressEvent){
15728 if(this.snapshot && this.snapshot != this.data){
15729 this.data = this.snapshot;
15730 delete this.snapshot;
15731 if(suppressEvent !== true){
15732 this.fireEvent("datachanged", this);
15738 afterEdit : function(record){
15739 if(this.modified.indexOf(record) == -1){
15740 this.modified.push(record);
15742 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15746 afterReject : function(record){
15747 this.modified.remove(record);
15748 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15752 afterCommit : function(record){
15753 this.modified.remove(record);
15754 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15758 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15759 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15761 commitChanges : function(){
15762 var m = this.modified.slice(0);
15763 this.modified = [];
15764 for(var i = 0, len = m.length; i < len; i++){
15770 * Cancel outstanding changes on all changed records.
15772 rejectChanges : function(){
15773 var m = this.modified.slice(0);
15774 this.modified = [];
15775 for(var i = 0, len = m.length; i < len; i++){
15780 onMetaChange : function(meta, rtype, o){
15781 this.recordType = rtype;
15782 this.fields = rtype.prototype.fields;
15783 delete this.snapshot;
15784 this.sortInfo = meta.sortInfo || this.sortInfo;
15785 this.modified = [];
15786 this.fireEvent('metachange', this, this.reader.meta);
15789 moveIndex : function(data, type)
15791 var index = this.indexOf(data);
15793 var newIndex = index + type;
15797 this.insert(newIndex, data);
15802 * Ext JS Library 1.1.1
15803 * Copyright(c) 2006-2007, Ext JS, LLC.
15805 * Originally Released Under LGPL - original licence link has changed is not relivant.
15808 * <script type="text/javascript">
15812 * @class Roo.data.SimpleStore
15813 * @extends Roo.data.Store
15814 * Small helper class to make creating Stores from Array data easier.
15815 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15816 * @cfg {Array} fields An array of field definition objects, or field name strings.
15817 * @cfg {Object} an existing reader (eg. copied from another store)
15818 * @cfg {Array} data The multi-dimensional array of data
15819 * @cfg {Roo.data.DataProxy} proxy [not-required]
15820 * @cfg {Roo.data.Reader} reader [not-required]
15822 * @param {Object} config
15824 Roo.data.SimpleStore = function(config)
15826 Roo.data.SimpleStore.superclass.constructor.call(this, {
15828 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15831 Roo.data.Record.create(config.fields)
15833 proxy : new Roo.data.MemoryProxy(config.data)
15837 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15839 * Ext JS Library 1.1.1
15840 * Copyright(c) 2006-2007, Ext JS, LLC.
15842 * Originally Released Under LGPL - original licence link has changed is not relivant.
15845 * <script type="text/javascript">
15850 * @extends Roo.data.Store
15851 * @class Roo.data.JsonStore
15852 * Small helper class to make creating Stores for JSON data easier. <br/>
15854 var store = new Roo.data.JsonStore({
15855 url: 'get-images.php',
15857 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15860 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15861 * JsonReader and HttpProxy (unless inline data is provided).</b>
15862 * @cfg {Array} fields An array of field definition objects, or field name strings.
15864 * @param {Object} config
15866 Roo.data.JsonStore = function(c){
15867 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15868 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15869 reader: new Roo.data.JsonReader(c, c.fields)
15872 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15874 * Ext JS Library 1.1.1
15875 * Copyright(c) 2006-2007, Ext JS, LLC.
15877 * Originally Released Under LGPL - original licence link has changed is not relivant.
15880 * <script type="text/javascript">
15884 Roo.data.Field = function(config){
15885 if(typeof config == "string"){
15886 config = {name: config};
15888 Roo.apply(this, config);
15891 this.type = "auto";
15894 var st = Roo.data.SortTypes;
15895 // named sortTypes are supported, here we look them up
15896 if(typeof this.sortType == "string"){
15897 this.sortType = st[this.sortType];
15900 // set default sortType for strings and dates
15901 if(!this.sortType){
15904 this.sortType = st.asUCString;
15907 this.sortType = st.asDate;
15910 this.sortType = st.none;
15915 var stripRe = /[\$,%]/g;
15917 // prebuilt conversion function for this field, instead of
15918 // switching every time we're reading a value
15920 var cv, dateFormat = this.dateFormat;
15925 cv = function(v){ return v; };
15928 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15932 return v !== undefined && v !== null && v !== '' ?
15933 parseInt(String(v).replace(stripRe, ""), 10) : '';
15938 return v !== undefined && v !== null && v !== '' ?
15939 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15944 cv = function(v){ return v === true || v === "true" || v == 1; };
15951 if(v instanceof Date){
15955 if(dateFormat == "timestamp"){
15956 return new Date(v*1000);
15958 return Date.parseDate(v, dateFormat);
15960 var parsed = Date.parse(v);
15961 return parsed ? new Date(parsed) : null;
15970 Roo.data.Field.prototype = {
15978 * Ext JS Library 1.1.1
15979 * Copyright(c) 2006-2007, Ext JS, LLC.
15981 * Originally Released Under LGPL - original licence link has changed is not relivant.
15984 * <script type="text/javascript">
15987 // Base class for reading structured data from a data source. This class is intended to be
15988 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15991 * @class Roo.data.DataReader
15993 * Base class for reading structured data from a data source. This class is intended to be
15994 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15997 Roo.data.DataReader = function(meta, recordType){
16001 this.recordType = recordType instanceof Array ?
16002 Roo.data.Record.create(recordType) : recordType;
16005 Roo.data.DataReader.prototype = {
16008 readerType : 'Data',
16010 * Create an empty record
16011 * @param {Object} data (optional) - overlay some values
16012 * @return {Roo.data.Record} record created.
16014 newRow : function(d) {
16016 this.recordType.prototype.fields.each(function(c) {
16018 case 'int' : da[c.name] = 0; break;
16019 case 'date' : da[c.name] = new Date(); break;
16020 case 'float' : da[c.name] = 0.0; break;
16021 case 'boolean' : da[c.name] = false; break;
16022 default : da[c.name] = ""; break;
16026 return new this.recordType(Roo.apply(da, d));
16032 * Ext JS Library 1.1.1
16033 * Copyright(c) 2006-2007, Ext JS, LLC.
16035 * Originally Released Under LGPL - original licence link has changed is not relivant.
16038 * <script type="text/javascript">
16042 * @class Roo.data.DataProxy
16043 * @extends Roo.util.Observable
16045 * This class is an abstract base class for implementations which provide retrieval of
16046 * unformatted data objects.<br>
16048 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16049 * (of the appropriate type which knows how to parse the data object) to provide a block of
16050 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16052 * Custom implementations must implement the load method as described in
16053 * {@link Roo.data.HttpProxy#load}.
16055 Roo.data.DataProxy = function(){
16058 * @event beforeload
16059 * Fires before a network request is made to retrieve a data object.
16060 * @param {Object} This DataProxy object.
16061 * @param {Object} params The params parameter to the load function.
16066 * Fires before the load method's callback is called.
16067 * @param {Object} This DataProxy object.
16068 * @param {Object} o The data object.
16069 * @param {Object} arg The callback argument object passed to the load function.
16073 * @event loadexception
16074 * Fires if an Exception occurs during data retrieval.
16075 * @param {Object} This DataProxy object.
16076 * @param {Object} o The data object.
16077 * @param {Object} arg The callback argument object passed to the load function.
16078 * @param {Object} e The Exception.
16080 loadexception : true
16082 Roo.data.DataProxy.superclass.constructor.call(this);
16085 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16088 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16092 * Ext JS Library 1.1.1
16093 * Copyright(c) 2006-2007, Ext JS, LLC.
16095 * Originally Released Under LGPL - original licence link has changed is not relivant.
16098 * <script type="text/javascript">
16101 * @class Roo.data.MemoryProxy
16102 * @extends Roo.data.DataProxy
16103 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16104 * to the Reader when its load method is called.
16106 * @param {Object} config A config object containing the objects needed for the Store to access data,
16108 Roo.data.MemoryProxy = function(config){
16110 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16111 data = config.data;
16113 Roo.data.MemoryProxy.superclass.constructor.call(this);
16117 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16120 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16123 * Load data from the requested source (in this case an in-memory
16124 * data object passed to the constructor), read the data object into
16125 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16126 * process that block using the passed callback.
16127 * @param {Object} params This parameter is not used by the MemoryProxy class.
16128 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16129 * object into a block of Roo.data.Records.
16130 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16131 * The function must be passed <ul>
16132 * <li>The Record block object</li>
16133 * <li>The "arg" argument from the load function</li>
16134 * <li>A boolean success indicator</li>
16136 * @param {Object} scope The scope in which to call the callback
16137 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16139 load : function(params, reader, callback, scope, arg){
16140 params = params || {};
16143 result = reader.readRecords(params.data ? params.data :this.data);
16145 this.fireEvent("loadexception", this, arg, null, e);
16146 callback.call(scope, null, arg, false);
16149 callback.call(scope, result, arg, true);
16153 update : function(params, records){
16158 * Ext JS Library 1.1.1
16159 * Copyright(c) 2006-2007, Ext JS, LLC.
16161 * Originally Released Under LGPL - original licence link has changed is not relivant.
16164 * <script type="text/javascript">
16167 * @class Roo.data.HttpProxy
16168 * @extends Roo.data.DataProxy
16169 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16170 * configured to reference a certain URL.<br><br>
16172 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16173 * from which the running page was served.<br><br>
16175 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16177 * Be aware that to enable the browser to parse an XML document, the server must set
16178 * the Content-Type header in the HTTP response to "text/xml".
16180 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16181 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16182 * will be used to make the request.
16184 Roo.data.HttpProxy = function(conn){
16185 Roo.data.HttpProxy.superclass.constructor.call(this);
16186 // is conn a conn config or a real conn?
16188 this.useAjax = !conn || !conn.events;
16192 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16193 // thse are take from connection...
16196 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16199 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16200 * extra parameters to each request made by this object. (defaults to undefined)
16203 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16204 * to each request made by this object. (defaults to undefined)
16207 * @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)
16210 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16213 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16219 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16223 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16224 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16225 * a finer-grained basis than the DataProxy events.
16227 getConnection : function(){
16228 return this.useAjax ? Roo.Ajax : this.conn;
16232 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16233 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16234 * process that block using the passed callback.
16235 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16236 * for the request to the remote server.
16237 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16238 * object into a block of Roo.data.Records.
16239 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16240 * The function must be passed <ul>
16241 * <li>The Record block object</li>
16242 * <li>The "arg" argument from the load function</li>
16243 * <li>A boolean success indicator</li>
16245 * @param {Object} scope The scope in which to call the callback
16246 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16248 load : function(params, reader, callback, scope, arg){
16249 if(this.fireEvent("beforeload", this, params) !== false){
16251 params : params || {},
16253 callback : callback,
16258 callback : this.loadResponse,
16262 Roo.applyIf(o, this.conn);
16263 if(this.activeRequest){
16264 Roo.Ajax.abort(this.activeRequest);
16266 this.activeRequest = Roo.Ajax.request(o);
16268 this.conn.request(o);
16271 callback.call(scope||this, null, arg, false);
16276 loadResponse : function(o, success, response){
16277 delete this.activeRequest;
16279 this.fireEvent("loadexception", this, o, response);
16280 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16285 result = o.reader.read(response);
16288 o.raw = { errorMsg : response.responseText };
16289 this.fireEvent("loadexception", this, o, response, e);
16290 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16294 this.fireEvent("load", this, o, o.request.arg);
16295 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16299 update : function(dataSet){
16304 updateResponse : function(dataSet){
16309 * Ext JS Library 1.1.1
16310 * Copyright(c) 2006-2007, Ext JS, LLC.
16312 * Originally Released Under LGPL - original licence link has changed is not relivant.
16315 * <script type="text/javascript">
16319 * @class Roo.data.ScriptTagProxy
16320 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16321 * other than the originating domain of the running page.<br><br>
16323 * <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
16324 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16326 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16327 * source code that is used as the source inside a <script> tag.<br><br>
16329 * In order for the browser to process the returned data, the server must wrap the data object
16330 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16331 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16332 * depending on whether the callback name was passed:
16335 boolean scriptTag = false;
16336 String cb = request.getParameter("callback");
16339 response.setContentType("text/javascript");
16341 response.setContentType("application/x-json");
16343 Writer out = response.getWriter();
16345 out.write(cb + "(");
16347 out.print(dataBlock.toJsonString());
16354 * @param {Object} config A configuration object.
16356 Roo.data.ScriptTagProxy = function(config){
16357 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16358 Roo.apply(this, config);
16359 this.head = document.getElementsByTagName("head")[0];
16362 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16364 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16366 * @cfg {String} url The URL from which to request the data object.
16369 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16373 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16374 * the server the name of the callback function set up by the load call to process the returned data object.
16375 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16376 * javascript output which calls this named function passing the data object as its only parameter.
16378 callbackParam : "callback",
16380 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16381 * name to the request.
16386 * Load data from the configured URL, read the data object into
16387 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16388 * process that block using the passed callback.
16389 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16390 * for the request to the remote server.
16391 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16392 * object into a block of Roo.data.Records.
16393 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16394 * The function must be passed <ul>
16395 * <li>The Record block object</li>
16396 * <li>The "arg" argument from the load function</li>
16397 * <li>A boolean success indicator</li>
16399 * @param {Object} scope The scope in which to call the callback
16400 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16402 load : function(params, reader, callback, scope, arg){
16403 if(this.fireEvent("beforeload", this, params) !== false){
16405 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16407 var url = this.url;
16408 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16410 url += "&_dc=" + (new Date().getTime());
16412 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16415 cb : "stcCallback"+transId,
16416 scriptId : "stcScript"+transId,
16420 callback : callback,
16426 window[trans.cb] = function(o){
16427 conn.handleResponse(o, trans);
16430 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16432 if(this.autoAbort !== false){
16436 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16438 var script = document.createElement("script");
16439 script.setAttribute("src", url);
16440 script.setAttribute("type", "text/javascript");
16441 script.setAttribute("id", trans.scriptId);
16442 this.head.appendChild(script);
16444 this.trans = trans;
16446 callback.call(scope||this, null, arg, false);
16451 isLoading : function(){
16452 return this.trans ? true : false;
16456 * Abort the current server request.
16458 abort : function(){
16459 if(this.isLoading()){
16460 this.destroyTrans(this.trans);
16465 destroyTrans : function(trans, isLoaded){
16466 this.head.removeChild(document.getElementById(trans.scriptId));
16467 clearTimeout(trans.timeoutId);
16469 window[trans.cb] = undefined;
16471 delete window[trans.cb];
16474 // if hasn't been loaded, wait for load to remove it to prevent script error
16475 window[trans.cb] = function(){
16476 window[trans.cb] = undefined;
16478 delete window[trans.cb];
16485 handleResponse : function(o, trans){
16486 this.trans = false;
16487 this.destroyTrans(trans, true);
16490 result = trans.reader.readRecords(o);
16492 this.fireEvent("loadexception", this, o, trans.arg, e);
16493 trans.callback.call(trans.scope||window, null, trans.arg, false);
16496 this.fireEvent("load", this, o, trans.arg);
16497 trans.callback.call(trans.scope||window, result, trans.arg, true);
16501 handleFailure : function(trans){
16502 this.trans = false;
16503 this.destroyTrans(trans, false);
16504 this.fireEvent("loadexception", this, null, trans.arg);
16505 trans.callback.call(trans.scope||window, null, trans.arg, false);
16509 * Ext JS Library 1.1.1
16510 * Copyright(c) 2006-2007, Ext JS, LLC.
16512 * Originally Released Under LGPL - original licence link has changed is not relivant.
16515 * <script type="text/javascript">
16519 * @class Roo.data.JsonReader
16520 * @extends Roo.data.DataReader
16521 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16522 * based on mappings in a provided Roo.data.Record constructor.
16524 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16525 * in the reply previously.
16530 var RecordDef = Roo.data.Record.create([
16531 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16532 {name: 'occupation'} // This field will use "occupation" as the mapping.
16534 var myReader = new Roo.data.JsonReader({
16535 totalProperty: "results", // The property which contains the total dataset size (optional)
16536 root: "rows", // The property which contains an Array of row objects
16537 id: "id" // The property within each row object that provides an ID for the record (optional)
16541 * This would consume a JSON file like this:
16543 { 'results': 2, 'rows': [
16544 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16545 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16548 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16549 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16550 * paged from the remote server.
16551 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16552 * @cfg {String} root name of the property which contains the Array of row objects.
16553 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16554 * @cfg {Array} fields Array of field definition objects
16556 * Create a new JsonReader
16557 * @param {Object} meta Metadata configuration options
16558 * @param {Object} recordType Either an Array of field definition objects,
16559 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16561 Roo.data.JsonReader = function(meta, recordType){
16564 // set some defaults:
16565 Roo.applyIf(meta, {
16566 totalProperty: 'total',
16567 successProperty : 'success',
16572 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16574 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16576 readerType : 'Json',
16579 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16580 * Used by Store query builder to append _requestMeta to params.
16583 metaFromRemote : false,
16585 * This method is only used by a DataProxy which has retrieved data from a remote server.
16586 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16587 * @return {Object} data A data block which is used by an Roo.data.Store object as
16588 * a cache of Roo.data.Records.
16590 read : function(response){
16591 var json = response.responseText;
16593 var o = /* eval:var:o */ eval("("+json+")");
16595 throw {message: "JsonReader.read: Json object not found"};
16601 this.metaFromRemote = true;
16602 this.meta = o.metaData;
16603 this.recordType = Roo.data.Record.create(o.metaData.fields);
16604 this.onMetaChange(this.meta, this.recordType, o);
16606 return this.readRecords(o);
16609 // private function a store will implement
16610 onMetaChange : function(meta, recordType, o){
16617 simpleAccess: function(obj, subsc) {
16624 getJsonAccessor: function(){
16626 return function(expr) {
16628 return(re.test(expr))
16629 ? new Function("obj", "return obj." + expr)
16634 return Roo.emptyFn;
16639 * Create a data block containing Roo.data.Records from an XML document.
16640 * @param {Object} o An object which contains an Array of row objects in the property specified
16641 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16642 * which contains the total size of the dataset.
16643 * @return {Object} data A data block which is used by an Roo.data.Store object as
16644 * a cache of Roo.data.Records.
16646 readRecords : function(o){
16648 * After any data loads, the raw JSON data is available for further custom processing.
16652 var s = this.meta, Record = this.recordType,
16653 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16655 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16657 if(s.totalProperty) {
16658 this.getTotal = this.getJsonAccessor(s.totalProperty);
16660 if(s.successProperty) {
16661 this.getSuccess = this.getJsonAccessor(s.successProperty);
16663 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16665 var g = this.getJsonAccessor(s.id);
16666 this.getId = function(rec) {
16668 return (r === undefined || r === "") ? null : r;
16671 this.getId = function(){return null;};
16674 for(var jj = 0; jj < fl; jj++){
16676 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16677 this.ef[jj] = this.getJsonAccessor(map);
16681 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16682 if(s.totalProperty){
16683 var vt = parseInt(this.getTotal(o), 10);
16688 if(s.successProperty){
16689 var vs = this.getSuccess(o);
16690 if(vs === false || vs === 'false'){
16695 for(var i = 0; i < c; i++){
16698 var id = this.getId(n);
16699 for(var j = 0; j < fl; j++){
16701 var v = this.ef[j](n);
16703 Roo.log('missing convert for ' + f.name);
16707 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16711 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16717 var record = new Record(values, id);
16719 records[i] = record;
16725 totalRecords : totalRecords
16728 // used when loading children.. @see loadDataFromChildren
16729 toLoadData: function(rec)
16731 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16732 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16733 return { data : data, total : data.length };
16738 * Ext JS Library 1.1.1
16739 * Copyright(c) 2006-2007, Ext JS, LLC.
16741 * Originally Released Under LGPL - original licence link has changed is not relivant.
16744 * <script type="text/javascript">
16748 * @class Roo.data.ArrayReader
16749 * @extends Roo.data.DataReader
16750 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16751 * Each element of that Array represents a row of data fields. The
16752 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16753 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16757 var RecordDef = Roo.data.Record.create([
16758 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16759 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16761 var myReader = new Roo.data.ArrayReader({
16762 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16766 * This would consume an Array like this:
16768 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16772 * Create a new JsonReader
16773 * @param {Object} meta Metadata configuration options.
16774 * @param {Object|Array} recordType Either an Array of field definition objects
16776 * @cfg {Array} fields Array of field definition objects
16777 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16778 * as specified to {@link Roo.data.Record#create},
16779 * or an {@link Roo.data.Record} object
16782 * created using {@link Roo.data.Record#create}.
16784 Roo.data.ArrayReader = function(meta, recordType)
16786 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16789 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16792 * Create a data block containing Roo.data.Records from an XML document.
16793 * @param {Object} o An Array of row objects which represents the dataset.
16794 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16795 * a cache of Roo.data.Records.
16797 readRecords : function(o)
16799 var sid = this.meta ? this.meta.id : null;
16800 var recordType = this.recordType, fields = recordType.prototype.fields;
16803 for(var i = 0; i < root.length; i++){
16806 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16807 for(var j = 0, jlen = fields.length; j < jlen; j++){
16808 var f = fields.items[j];
16809 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16810 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16812 values[f.name] = v;
16814 var record = new recordType(values, id);
16816 records[records.length] = record;
16820 totalRecords : records.length
16823 // used when loading children.. @see loadDataFromChildren
16824 toLoadData: function(rec)
16826 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16827 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16838 * @class Roo.bootstrap.form.ComboBox
16839 * @extends Roo.bootstrap.form.TriggerField
16840 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16841 * @cfg {Boolean} append (true|false) default false
16842 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16843 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16844 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16845 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16846 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16847 * @cfg {Boolean} animate default true
16848 * @cfg {Boolean} emptyResultText only for touch device
16849 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16850 * @cfg {String} emptyTitle default ''
16851 * @cfg {Number} width fixed with? experimental
16853 * Create a new ComboBox.
16854 * @param {Object} config Configuration options
16856 Roo.bootstrap.form.ComboBox = function(config){
16857 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16861 * Fires when the dropdown list is expanded
16862 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867 * Fires when the dropdown list is collapsed
16868 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16872 * @event beforeselect
16873 * Fires before a list item is selected. Return false to cancel the selection.
16874 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16875 * @param {Roo.data.Record} record The data record returned from the underlying store
16876 * @param {Number} index The index of the selected item in the dropdown list
16878 'beforeselect' : true,
16881 * Fires when a list item is selected
16882 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16883 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16884 * @param {Number} index The index of the selected item in the dropdown list
16888 * @event beforequery
16889 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16890 * The event object passed has these properties:
16891 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892 * @param {String} query The query
16893 * @param {Boolean} forceAll true to force "all" query
16894 * @param {Boolean} cancel true to cancel the query
16895 * @param {Object} e The query event object
16897 'beforequery': true,
16900 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16901 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16906 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16907 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16913 * Fires when the remove value from the combobox array
16914 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16918 * @event afterremove
16919 * Fires when the remove value from the combobox array
16920 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16922 'afterremove' : true,
16924 * @event specialfilter
16925 * Fires when specialfilter
16926 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16928 'specialfilter' : true,
16931 * Fires when tick the element
16932 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16936 * @event touchviewdisplay
16937 * Fires when touch view require special display (default is using displayField)
16938 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16939 * @param {Object} cfg set html .
16941 'touchviewdisplay' : true
16946 this.tickItems = [];
16948 this.selectedIndex = -1;
16949 if(this.mode == 'local'){
16950 if(config.queryDelay === undefined){
16951 this.queryDelay = 10;
16953 if(config.minChars === undefined){
16959 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16962 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16963 * rendering into an Roo.Editor, defaults to false)
16966 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16967 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16970 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16973 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16974 * the dropdown list (defaults to undefined, with no header element)
16978 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16982 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16984 listWidth: undefined,
16986 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16987 * mode = 'remote' or 'text' if mode = 'local')
16989 displayField: undefined,
16992 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16993 * mode = 'remote' or 'value' if mode = 'local').
16994 * Note: use of a valueField requires the user make a selection
16995 * in order for a value to be mapped.
16997 valueField: undefined,
16999 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17004 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17005 * field's data value (defaults to the underlying DOM element's name)
17007 hiddenName: undefined,
17009 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17013 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17015 selectedClass: 'active',
17018 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17022 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17023 * anchor positions (defaults to 'tl-bl')
17025 listAlign: 'tl-bl?',
17027 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17031 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17032 * query specified by the allQuery config option (defaults to 'query')
17034 triggerAction: 'query',
17036 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17037 * (defaults to 4, does not apply if editable = false)
17041 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17042 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17046 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17047 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17051 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17052 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17056 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17057 * when editable = true (defaults to false)
17059 selectOnFocus:false,
17061 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17063 queryParam: 'query',
17065 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17066 * when mode = 'remote' (defaults to 'Loading...')
17068 loadingText: 'Loading...',
17070 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17074 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17078 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17079 * traditional select (defaults to true)
17083 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17087 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17091 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17092 * listWidth has a higher value)
17096 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17097 * allow the user to set arbitrary text into the field (defaults to false)
17099 forceSelection:false,
17101 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17102 * if typeAhead = true (defaults to 250)
17104 typeAheadDelay : 250,
17106 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17107 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17109 valueNotFoundText : undefined,
17111 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17113 blockFocus : false,
17116 * @cfg {Boolean} disableClear Disable showing of clear button.
17118 disableClear : false,
17120 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17122 alwaysQuery : false,
17125 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17130 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17132 invalidClass : "has-warning",
17135 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17137 validClass : "has-success",
17140 * @cfg {Boolean} specialFilter (true|false) special filter default false
17142 specialFilter : false,
17145 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17147 mobileTouchView : true,
17150 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17152 useNativeIOS : false,
17155 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17157 mobile_restrict_height : false,
17159 ios_options : false,
17171 btnPosition : 'right',
17172 triggerList : true,
17173 showToggleBtn : true,
17175 emptyResultText: 'Empty',
17176 triggerText : 'Select',
17180 // element that contains real text value.. (when hidden is used..)
17182 getAutoCreate : function()
17187 * Render classic select for iso
17190 if(Roo.isIOS && this.useNativeIOS){
17191 cfg = this.getAutoCreateNativeIOS();
17199 if(Roo.isTouch && this.mobileTouchView){
17200 cfg = this.getAutoCreateTouchView();
17207 if(!this.tickable){
17208 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17213 * ComboBox with tickable selections
17216 var align = this.labelAlign || this.parentLabelAlign();
17219 cls : 'form-group roo-combobox-tickable' //input-group
17222 var btn_text_select = '';
17223 var btn_text_done = '';
17224 var btn_text_cancel = '';
17226 if (this.btn_text_show) {
17227 btn_text_select = 'Select';
17228 btn_text_done = 'Done';
17229 btn_text_cancel = 'Cancel';
17234 cls : 'tickable-buttons',
17239 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17240 //html : this.triggerText
17241 html: btn_text_select
17247 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17249 html: btn_text_done
17255 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17257 html: btn_text_cancel
17263 buttons.cn.unshift({
17265 cls: 'roo-select2-search-field-input'
17271 Roo.each(buttons.cn, function(c){
17273 c.cls += ' btn-' + _this.size;
17276 if (_this.disabled) {
17283 style : 'display: contents',
17288 cls: 'form-hidden-field'
17292 cls: 'roo-select2-choices',
17296 cls: 'roo-select2-search-field',
17307 cls: 'roo-select2-container input-group roo-select2-container-multi',
17313 // cls: 'typeahead typeahead-long dropdown-menu',
17314 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17319 if(this.hasFeedback && !this.allowBlank){
17323 cls: 'glyphicon form-control-feedback'
17326 combobox.cn.push(feedback);
17333 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17334 tooltip : 'This field is required'
17336 if (Roo.bootstrap.version == 4) {
17339 style : 'display:none'
17342 if (align ==='left' && this.fieldLabel.length) {
17344 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17351 cls : 'control-label col-form-label',
17352 html : this.fieldLabel
17364 var labelCfg = cfg.cn[1];
17365 var contentCfg = cfg.cn[2];
17368 if(this.indicatorpos == 'right'){
17374 cls : 'control-label col-form-label',
17378 html : this.fieldLabel
17394 labelCfg = cfg.cn[0];
17395 contentCfg = cfg.cn[1];
17399 if(this.labelWidth > 12){
17400 labelCfg.style = "width: " + this.labelWidth + 'px';
17402 if(this.width * 1 > 0){
17403 contentCfg.style = "width: " + this.width + 'px';
17405 if(this.labelWidth < 13 && this.labelmd == 0){
17406 this.labelmd = this.labelWidth;
17409 if(this.labellg > 0){
17410 labelCfg.cls += ' col-lg-' + this.labellg;
17411 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17414 if(this.labelmd > 0){
17415 labelCfg.cls += ' col-md-' + this.labelmd;
17416 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17419 if(this.labelsm > 0){
17420 labelCfg.cls += ' col-sm-' + this.labelsm;
17421 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17424 if(this.labelxs > 0){
17425 labelCfg.cls += ' col-xs-' + this.labelxs;
17426 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17430 } else if ( this.fieldLabel.length) {
17431 // Roo.log(" label");
17436 //cls : 'input-group-addon',
17437 html : this.fieldLabel
17442 if(this.indicatorpos == 'right'){
17446 //cls : 'input-group-addon',
17447 html : this.fieldLabel
17457 // Roo.log(" no label && no align");
17464 ['xs','sm','md','lg'].map(function(size){
17465 if (settings[size]) {
17466 cfg.cls += ' col-' + size + '-' + settings[size];
17474 _initEventsCalled : false,
17477 initEvents: function()
17479 if (this._initEventsCalled) { // as we call render... prevent looping...
17482 this._initEventsCalled = true;
17485 throw "can not find store for combo";
17488 this.indicator = this.indicatorEl();
17490 this.store = Roo.factory(this.store, Roo.data);
17491 this.store.parent = this;
17493 // if we are building from html. then this element is so complex, that we can not really
17494 // use the rendered HTML.
17495 // so we have to trash and replace the previous code.
17496 if (Roo.XComponent.build_from_html) {
17497 // remove this element....
17498 var e = this.el.dom, k=0;
17499 while (e ) { e = e.previousSibling; ++k;}
17504 this.rendered = false;
17506 this.render(this.parent().getChildContainer(true), k);
17509 if(Roo.isIOS && this.useNativeIOS){
17510 this.initIOSView();
17518 if(Roo.isTouch && this.mobileTouchView){
17519 this.initTouchView();
17524 this.initTickableEvents();
17528 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17530 if(this.hiddenName){
17532 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17534 this.hiddenField.dom.value =
17535 this.hiddenValue !== undefined ? this.hiddenValue :
17536 this.value !== undefined ? this.value : '';
17538 // prevent input submission
17539 this.el.dom.removeAttribute('name');
17540 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17545 // this.el.dom.setAttribute('autocomplete', 'off');
17548 var cls = 'x-combo-list';
17550 //this.list = new Roo.Layer({
17551 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17557 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17558 _this.list.setWidth(lw);
17561 this.list.on('mouseover', this.onViewOver, this);
17562 this.list.on('mousemove', this.onViewMove, this);
17563 this.list.on('scroll', this.onViewScroll, this);
17566 this.list.swallowEvent('mousewheel');
17567 this.assetHeight = 0;
17570 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17571 this.assetHeight += this.header.getHeight();
17574 this.innerList = this.list.createChild({cls:cls+'-inner'});
17575 this.innerList.on('mouseover', this.onViewOver, this);
17576 this.innerList.on('mousemove', this.onViewMove, this);
17577 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17579 if(this.allowBlank && !this.pageSize && !this.disableClear){
17580 this.footer = this.list.createChild({cls:cls+'-ft'});
17581 this.pageTb = new Roo.Toolbar(this.footer);
17585 this.footer = this.list.createChild({cls:cls+'-ft'});
17586 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17587 {pageSize: this.pageSize});
17591 if (this.pageTb && this.allowBlank && !this.disableClear) {
17593 this.pageTb.add(new Roo.Toolbar.Fill(), {
17594 cls: 'x-btn-icon x-btn-clear',
17596 handler: function()
17599 _this.clearValue();
17600 _this.onSelect(false, -1);
17605 this.assetHeight += this.footer.getHeight();
17610 this.tpl = Roo.bootstrap.version == 4 ?
17611 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17612 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17615 this.view = new Roo.View(this.list, this.tpl, {
17616 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17618 //this.view.wrapEl.setDisplayed(false);
17619 this.view.on('click', this.onViewClick, this);
17622 this.store.on('beforeload', this.onBeforeLoad, this);
17623 this.store.on('load', this.onLoad, this);
17624 this.store.on('loadexception', this.onLoadException, this);
17626 if(this.resizable){
17627 this.resizer = new Roo.Resizable(this.list, {
17628 pinned:true, handles:'se'
17630 this.resizer.on('resize', function(r, w, h){
17631 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17632 this.listWidth = w;
17633 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17634 this.restrictHeight();
17636 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17639 if(!this.editable){
17640 this.editable = true;
17641 this.setEditable(false);
17646 if (typeof(this.events.add.listeners) != 'undefined') {
17648 this.addicon = this.wrap.createChild(
17649 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17651 this.addicon.on('click', function(e) {
17652 this.fireEvent('add', this);
17655 if (typeof(this.events.edit.listeners) != 'undefined') {
17657 this.editicon = this.wrap.createChild(
17658 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17659 if (this.addicon) {
17660 this.editicon.setStyle('margin-left', '40px');
17662 this.editicon.on('click', function(e) {
17664 // we fire even if inothing is selected..
17665 this.fireEvent('edit', this, this.lastData );
17671 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17672 "up" : function(e){
17673 this.inKeyMode = true;
17677 "down" : function(e){
17678 if(!this.isExpanded()){
17679 this.onTriggerClick();
17681 this.inKeyMode = true;
17686 "enter" : function(e){
17687 // this.onViewClick();
17691 if(this.fireEvent("specialkey", this, e)){
17692 this.onViewClick(false);
17698 "esc" : function(e){
17702 "tab" : function(e){
17705 if(this.fireEvent("specialkey", this, e)){
17706 this.onViewClick(false);
17714 doRelay : function(foo, bar, hname){
17715 if(hname == 'down' || this.scope.isExpanded()){
17716 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17725 this.queryDelay = Math.max(this.queryDelay || 10,
17726 this.mode == 'local' ? 10 : 250);
17729 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17731 if(this.typeAhead){
17732 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17734 if(this.editable !== false){
17735 this.inputEl().on("keyup", this.onKeyUp, this);
17737 if(this.forceSelection){
17738 this.inputEl().on('blur', this.doForce, this);
17742 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17743 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17747 initTickableEvents: function()
17751 if(this.hiddenName){
17753 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17755 this.hiddenField.dom.value =
17756 this.hiddenValue !== undefined ? this.hiddenValue :
17757 this.value !== undefined ? this.value : '';
17759 // prevent input submission
17760 this.el.dom.removeAttribute('name');
17761 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17766 // this.list = this.el.select('ul.dropdown-menu',true).first();
17768 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17769 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17770 if(this.triggerList){
17771 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17774 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17775 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17777 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17778 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17780 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17781 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17783 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17784 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17785 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17788 this.cancelBtn.hide();
17793 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17794 _this.list.setWidth(lw);
17797 this.list.on('mouseover', this.onViewOver, this);
17798 this.list.on('mousemove', this.onViewMove, this);
17800 this.list.on('scroll', this.onViewScroll, this);
17803 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17804 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17807 this.view = new Roo.View(this.list, this.tpl, {
17812 selectedClass: this.selectedClass
17815 //this.view.wrapEl.setDisplayed(false);
17816 this.view.on('click', this.onViewClick, this);
17820 this.store.on('beforeload', this.onBeforeLoad, this);
17821 this.store.on('load', this.onLoad, this);
17822 this.store.on('loadexception', this.onLoadException, this);
17825 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17826 "up" : function(e){
17827 this.inKeyMode = true;
17831 "down" : function(e){
17832 this.inKeyMode = true;
17836 "enter" : function(e){
17837 if(this.fireEvent("specialkey", this, e)){
17838 this.onViewClick(false);
17844 "esc" : function(e){
17845 this.onTickableFooterButtonClick(e, false, false);
17848 "tab" : function(e){
17849 this.fireEvent("specialkey", this, e);
17851 this.onTickableFooterButtonClick(e, false, false);
17858 doRelay : function(e, fn, key){
17859 if(this.scope.isExpanded()){
17860 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17869 this.queryDelay = Math.max(this.queryDelay || 10,
17870 this.mode == 'local' ? 10 : 250);
17873 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17875 if(this.typeAhead){
17876 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17879 if(this.editable !== false){
17880 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17883 this.indicator = this.indicatorEl();
17885 if(this.indicator){
17886 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17887 this.indicator.hide();
17892 onDestroy : function(){
17894 this.view.setStore(null);
17895 this.view.el.removeAllListeners();
17896 this.view.el.remove();
17897 this.view.purgeListeners();
17900 this.list.dom.innerHTML = '';
17904 this.store.un('beforeload', this.onBeforeLoad, this);
17905 this.store.un('load', this.onLoad, this);
17906 this.store.un('loadexception', this.onLoadException, this);
17908 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17912 fireKey : function(e){
17913 if(e.isNavKeyPress() && !this.list.isVisible()){
17914 this.fireEvent("specialkey", this, e);
17919 onResize: function(w, h)
17923 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17925 // if(typeof w != 'number'){
17926 // // we do not handle it!?!?
17929 // var tw = this.trigger.getWidth();
17930 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17931 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17933 // this.inputEl().setWidth( this.adjustWidth('input', x));
17935 // //this.trigger.setStyle('left', x+'px');
17937 // if(this.list && this.listWidth === undefined){
17938 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17939 // this.list.setWidth(lw);
17940 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17948 * Allow or prevent the user from directly editing the field text. If false is passed,
17949 * the user will only be able to select from the items defined in the dropdown list. This method
17950 * is the runtime equivalent of setting the 'editable' config option at config time.
17951 * @param {Boolean} value True to allow the user to directly edit the field text
17953 setEditable : function(value){
17954 if(value == this.editable){
17957 this.editable = value;
17959 this.inputEl().dom.setAttribute('readOnly', true);
17960 this.inputEl().on('mousedown', this.onTriggerClick, this);
17961 this.inputEl().addClass('x-combo-noedit');
17963 this.inputEl().dom.removeAttribute('readOnly');
17964 this.inputEl().un('mousedown', this.onTriggerClick, this);
17965 this.inputEl().removeClass('x-combo-noedit');
17971 onBeforeLoad : function(combo,opts){
17972 if(!this.hasFocus){
17976 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17978 this.restrictHeight();
17979 this.selectedIndex = -1;
17983 onLoad : function(){
17985 this.hasQuery = false;
17987 if(!this.hasFocus){
17991 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17992 this.loading.hide();
17995 if(this.store.getCount() > 0){
17998 this.restrictHeight();
17999 if(this.lastQuery == this.allQuery){
18000 if(this.editable && !this.tickable){
18001 this.inputEl().dom.select();
18005 !this.selectByValue(this.value, true) &&
18008 !this.store.lastOptions ||
18009 typeof(this.store.lastOptions.add) == 'undefined' ||
18010 this.store.lastOptions.add != true
18013 this.select(0, true);
18016 if(this.autoFocus){
18019 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18020 this.taTask.delay(this.typeAheadDelay);
18024 this.onEmptyResults();
18030 onLoadException : function()
18032 this.hasQuery = false;
18034 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18035 this.loading.hide();
18038 if(this.tickable && this.editable){
18043 // only causes errors at present
18044 //Roo.log(this.store.reader.jsonData);
18045 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18047 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18053 onTypeAhead : function(){
18054 if(this.store.getCount() > 0){
18055 var r = this.store.getAt(0);
18056 var newValue = r.data[this.displayField];
18057 var len = newValue.length;
18058 var selStart = this.getRawValue().length;
18060 if(selStart != len){
18061 this.setRawValue(newValue);
18062 this.selectText(selStart, newValue.length);
18068 onSelect : function(record, index){
18070 if(this.fireEvent('beforeselect', this, record, index) !== false){
18072 this.setFromData(index > -1 ? record.data : false);
18075 this.fireEvent('select', this, record, index);
18080 * Returns the currently selected field value or empty string if no value is set.
18081 * @return {String} value The selected value
18083 getValue : function()
18085 if(Roo.isIOS && this.useNativeIOS){
18086 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18090 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18093 if(this.valueField){
18094 return typeof this.value != 'undefined' ? this.value : '';
18096 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18100 getRawValue : function()
18102 if(Roo.isIOS && this.useNativeIOS){
18103 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18106 var v = this.inputEl().getValue();
18112 * Clears any text/value currently set in the field
18114 clearValue : function(){
18116 if(this.hiddenField){
18117 this.hiddenField.dom.value = '';
18120 this.setRawValue('');
18121 this.lastSelectionText = '';
18122 this.lastData = false;
18124 var close = this.closeTriggerEl();
18135 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18136 * will be displayed in the field. If the value does not match the data value of an existing item,
18137 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18138 * Otherwise the field will be blank (although the value will still be set).
18139 * @param {String} value The value to match
18141 setValue : function(v)
18143 if(Roo.isIOS && this.useNativeIOS){
18144 this.setIOSValue(v);
18154 if(this.valueField){
18155 var r = this.findRecord(this.valueField, v);
18157 text = r.data[this.displayField];
18158 }else if(this.valueNotFoundText !== undefined){
18159 text = this.valueNotFoundText;
18162 this.lastSelectionText = text;
18163 if(this.hiddenField){
18164 this.hiddenField.dom.value = v;
18166 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18169 var close = this.closeTriggerEl();
18172 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18178 * @property {Object} the last set data for the element
18183 * Sets the value of the field based on a object which is related to the record format for the store.
18184 * @param {Object} value the value to set as. or false on reset?
18186 setFromData : function(o){
18193 var dv = ''; // display value
18194 var vv = ''; // value value..
18196 if (this.displayField) {
18197 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18199 // this is an error condition!!!
18200 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18203 if(this.valueField){
18204 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18207 var close = this.closeTriggerEl();
18210 if(dv.length || vv * 1 > 0){
18212 this.blockFocus=true;
18218 if(this.hiddenField){
18219 this.hiddenField.dom.value = vv;
18221 this.lastSelectionText = dv;
18222 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18226 // no hidden field.. - we store the value in 'value', but still display
18227 // display field!!!!
18228 this.lastSelectionText = dv;
18229 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18236 reset : function(){
18237 // overridden so that last data is reset..
18244 this.setValue(this.originalValue);
18245 //this.clearInvalid();
18246 this.lastData = false;
18248 this.view.clearSelections();
18254 findRecord : function(prop, value){
18256 if(this.store.getCount() > 0){
18257 this.store.each(function(r){
18258 if(r.data[prop] == value){
18268 getName: function()
18270 // returns hidden if it's set..
18271 if (!this.rendered) {return ''};
18272 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18276 onViewMove : function(e, t){
18277 this.inKeyMode = false;
18281 onViewOver : function(e, t){
18282 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18285 var item = this.view.findItemFromChild(t);
18288 var index = this.view.indexOf(item);
18289 this.select(index, false);
18294 onViewClick : function(view, doFocus, el, e)
18296 var index = this.view.getSelectedIndexes()[0];
18298 var r = this.store.getAt(index);
18302 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18309 Roo.each(this.tickItems, function(v,k){
18311 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18313 _this.tickItems.splice(k, 1);
18315 if(typeof(e) == 'undefined' && view == false){
18316 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18328 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18329 this.tickItems.push(r.data);
18332 if(typeof(e) == 'undefined' && view == false){
18333 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18340 this.onSelect(r, index);
18342 if(doFocus !== false && !this.blockFocus){
18343 this.inputEl().focus();
18348 restrictHeight : function(){
18349 //this.innerList.dom.style.height = '';
18350 //var inner = this.innerList.dom;
18351 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18352 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18353 //this.list.beginUpdate();
18354 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18355 this.list.alignTo(this.inputEl(), this.listAlign);
18356 this.list.alignTo(this.inputEl(), this.listAlign);
18357 //this.list.endUpdate();
18361 onEmptyResults : function(){
18363 if(this.tickable && this.editable){
18364 this.hasFocus = false;
18365 this.restrictHeight();
18373 * Returns true if the dropdown list is expanded, else false.
18375 isExpanded : function(){
18376 return this.list.isVisible();
18380 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18381 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18382 * @param {String} value The data value of the item to select
18383 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18384 * selected item if it is not currently in view (defaults to true)
18385 * @return {Boolean} True if the value matched an item in the list, else false
18387 selectByValue : function(v, scrollIntoView){
18388 if(v !== undefined && v !== null){
18389 var r = this.findRecord(this.valueField || this.displayField, v);
18391 this.select(this.store.indexOf(r), scrollIntoView);
18399 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18400 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18401 * @param {Number} index The zero-based index of the list item to select
18402 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18403 * selected item if it is not currently in view (defaults to true)
18405 select : function(index, scrollIntoView){
18406 this.selectedIndex = index;
18407 this.view.select(index);
18408 if(scrollIntoView !== false){
18409 var el = this.view.getNode(index);
18411 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18414 this.list.scrollChildIntoView(el, false);
18420 selectNext : function(){
18421 var ct = this.store.getCount();
18423 if(this.selectedIndex == -1){
18425 }else if(this.selectedIndex < ct-1){
18426 this.select(this.selectedIndex+1);
18432 selectPrev : function(){
18433 var ct = this.store.getCount();
18435 if(this.selectedIndex == -1){
18437 }else if(this.selectedIndex != 0){
18438 this.select(this.selectedIndex-1);
18444 onKeyUp : function(e){
18445 if(this.editable !== false && !e.isSpecialKey()){
18446 this.lastKey = e.getKey();
18447 this.dqTask.delay(this.queryDelay);
18452 validateBlur : function(){
18453 return !this.list || !this.list.isVisible();
18457 initQuery : function(){
18459 var v = this.getRawValue();
18461 if(this.tickable && this.editable){
18462 v = this.tickableInputEl().getValue();
18469 doForce : function(){
18470 if(this.inputEl().dom.value.length > 0){
18471 this.inputEl().dom.value =
18472 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18478 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18479 * query allowing the query action to be canceled if needed.
18480 * @param {String} query The SQL query to execute
18481 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18482 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18483 * saved in the current store (defaults to false)
18485 doQuery : function(q, forceAll){
18487 if(q === undefined || q === null){
18492 forceAll: forceAll,
18496 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18501 forceAll = qe.forceAll;
18502 if(forceAll === true || (q.length >= this.minChars)){
18504 this.hasQuery = true;
18506 if(this.lastQuery != q || this.alwaysQuery){
18507 this.lastQuery = q;
18508 if(this.mode == 'local'){
18509 this.selectedIndex = -1;
18511 this.store.clearFilter();
18514 if(this.specialFilter){
18515 this.fireEvent('specialfilter', this);
18520 this.store.filter(this.displayField, q);
18523 this.store.fireEvent("datachanged", this.store);
18530 this.store.baseParams[this.queryParam] = q;
18532 var options = {params : this.getParams(q)};
18535 options.add = true;
18536 options.params.start = this.page * this.pageSize;
18539 this.store.load(options);
18542 * this code will make the page width larger, at the beginning, the list not align correctly,
18543 * we should expand the list on onLoad
18544 * so command out it
18549 this.selectedIndex = -1;
18554 this.loadNext = false;
18558 getParams : function(q){
18560 //p[this.queryParam] = q;
18564 p.limit = this.pageSize;
18570 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18572 collapse : function(){
18573 if(!this.isExpanded()){
18579 this.hasFocus = false;
18583 this.cancelBtn.hide();
18584 this.trigger.show();
18587 this.tickableInputEl().dom.value = '';
18588 this.tickableInputEl().blur();
18593 Roo.get(document).un('mousedown', this.collapseIf, this);
18594 Roo.get(document).un('mousewheel', this.collapseIf, this);
18595 if (!this.editable) {
18596 Roo.get(document).un('keydown', this.listKeyPress, this);
18598 this.fireEvent('collapse', this);
18604 collapseIf : function(e){
18605 var in_combo = e.within(this.el);
18606 var in_list = e.within(this.list);
18607 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18609 if (in_combo || in_list || is_list) {
18610 //e.stopPropagation();
18615 this.onTickableFooterButtonClick(e, false, false);
18623 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18625 expand : function(){
18627 if(this.isExpanded() || !this.hasFocus){
18631 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18632 this.list.setWidth(lw);
18638 this.restrictHeight();
18642 this.tickItems = Roo.apply([], this.item);
18645 this.cancelBtn.show();
18646 this.trigger.hide();
18649 this.tickableInputEl().focus();
18654 Roo.get(document).on('mousedown', this.collapseIf, this);
18655 Roo.get(document).on('mousewheel', this.collapseIf, this);
18656 if (!this.editable) {
18657 Roo.get(document).on('keydown', this.listKeyPress, this);
18660 this.fireEvent('expand', this);
18664 // Implements the default empty TriggerField.onTriggerClick function
18665 onTriggerClick : function(e)
18667 Roo.log('trigger click');
18669 if(this.disabled || !this.triggerList){
18674 this.loadNext = false;
18676 if(this.isExpanded()){
18678 if (!this.blockFocus) {
18679 this.inputEl().focus();
18683 this.hasFocus = true;
18684 if(this.triggerAction == 'all') {
18685 this.doQuery(this.allQuery, true);
18687 this.doQuery(this.getRawValue());
18689 if (!this.blockFocus) {
18690 this.inputEl().focus();
18695 onTickableTriggerClick : function(e)
18702 this.loadNext = false;
18703 this.hasFocus = true;
18705 if(this.triggerAction == 'all') {
18706 this.doQuery(this.allQuery, true);
18708 this.doQuery(this.getRawValue());
18712 onSearchFieldClick : function(e)
18714 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18715 this.onTickableFooterButtonClick(e, false, false);
18719 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18724 this.loadNext = false;
18725 this.hasFocus = true;
18727 if(this.triggerAction == 'all') {
18728 this.doQuery(this.allQuery, true);
18730 this.doQuery(this.getRawValue());
18734 listKeyPress : function(e)
18736 //Roo.log('listkeypress');
18737 // scroll to first matching element based on key pres..
18738 if (e.isSpecialKey()) {
18741 var k = String.fromCharCode(e.getKey()).toUpperCase();
18744 var csel = this.view.getSelectedNodes();
18745 var cselitem = false;
18747 var ix = this.view.indexOf(csel[0]);
18748 cselitem = this.store.getAt(ix);
18749 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18755 this.store.each(function(v) {
18757 // start at existing selection.
18758 if (cselitem.id == v.id) {
18764 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18765 match = this.store.indexOf(v);
18771 if (match === false) {
18772 return true; // no more action?
18775 this.view.select(match);
18776 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18777 sn.scrollIntoView(sn.dom.parentNode, false);
18780 onViewScroll : function(e, t){
18782 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){
18786 this.hasQuery = true;
18788 this.loading = this.list.select('.loading', true).first();
18790 if(this.loading === null){
18791 this.list.createChild({
18793 cls: 'loading roo-select2-more-results roo-select2-active',
18794 html: 'Loading more results...'
18797 this.loading = this.list.select('.loading', true).first();
18799 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18801 this.loading.hide();
18804 this.loading.show();
18809 this.loadNext = true;
18811 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18816 addItem : function(o)
18818 var dv = ''; // display value
18820 if (this.displayField) {
18821 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18823 // this is an error condition!!!
18824 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18831 var choice = this.choices.createChild({
18833 cls: 'roo-select2-search-choice',
18842 cls: 'roo-select2-search-choice-close fa fa-times',
18847 }, this.searchField);
18849 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18851 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18859 this.inputEl().dom.value = '';
18864 onRemoveItem : function(e, _self, o)
18866 e.preventDefault();
18868 this.lastItem = Roo.apply([], this.item);
18870 var index = this.item.indexOf(o.data) * 1;
18873 Roo.log('not this item?!');
18877 this.item.splice(index, 1);
18882 this.fireEvent('remove', this, e);
18888 syncValue : function()
18890 if(!this.item.length){
18897 Roo.each(this.item, function(i){
18898 if(_this.valueField){
18899 value.push(i[_this.valueField]);
18906 this.value = value.join(',');
18908 if(this.hiddenField){
18909 this.hiddenField.dom.value = this.value;
18912 this.store.fireEvent("datachanged", this.store);
18917 clearItem : function()
18919 if(!this.multiple){
18925 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18933 if(this.tickable && !Roo.isTouch){
18934 this.view.refresh();
18938 inputEl: function ()
18940 if(Roo.isIOS && this.useNativeIOS){
18941 return this.el.select('select.roo-ios-select', true).first();
18944 if(Roo.isTouch && this.mobileTouchView){
18945 return this.el.select('input.form-control',true).first();
18949 return this.searchField;
18952 return this.el.select('input.form-control',true).first();
18955 onTickableFooterButtonClick : function(e, btn, el)
18957 e.preventDefault();
18959 this.lastItem = Roo.apply([], this.item);
18961 if(btn && btn.name == 'cancel'){
18962 this.tickItems = Roo.apply([], this.item);
18971 Roo.each(this.tickItems, function(o){
18979 validate : function()
18981 if(this.getVisibilityEl().hasClass('hidden')){
18985 var v = this.getRawValue();
18988 v = this.getValue();
18991 if(this.disabled || this.allowBlank || v.length){
18996 this.markInvalid();
19000 tickableInputEl : function()
19002 if(!this.tickable || !this.editable){
19003 return this.inputEl();
19006 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19010 getAutoCreateTouchView : function()
19015 cls: 'form-group' //input-group
19021 type : this.inputType,
19022 cls : 'form-control x-combo-noedit',
19023 autocomplete: 'new-password',
19024 placeholder : this.placeholder || '',
19029 input.name = this.name;
19033 input.cls += ' input-' + this.size;
19036 if (this.disabled) {
19037 input.disabled = true;
19041 cls : 'roo-combobox-wrap',
19048 inputblock.cls += ' input-group';
19050 inputblock.cn.unshift({
19052 cls : 'input-group-addon input-group-prepend input-group-text',
19057 if(this.removable && !this.multiple){
19058 inputblock.cls += ' roo-removable';
19060 inputblock.cn.push({
19063 cls : 'roo-combo-removable-btn close'
19067 if(this.hasFeedback && !this.allowBlank){
19069 inputblock.cls += ' has-feedback';
19071 inputblock.cn.push({
19073 cls: 'glyphicon form-control-feedback'
19080 inputblock.cls += (this.before) ? '' : ' input-group';
19082 inputblock.cn.push({
19084 cls : 'input-group-addon input-group-append input-group-text',
19090 var ibwrap = inputblock;
19095 cls: 'roo-select2-choices',
19099 cls: 'roo-select2-search-field',
19112 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19117 cls: 'form-hidden-field'
19123 if(!this.multiple && this.showToggleBtn){
19129 if (this.caret != false) {
19132 cls: 'fa fa-' + this.caret
19139 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19141 Roo.bootstrap.version == 3 ? caret : '',
19144 cls: 'combobox-clear',
19158 combobox.cls += ' roo-select2-container-multi';
19161 var required = this.allowBlank ? {
19163 style: 'display: none'
19166 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19167 tooltip : 'This field is required'
19170 var align = this.labelAlign || this.parentLabelAlign();
19172 if (align ==='left' && this.fieldLabel.length) {
19178 cls : 'control-label col-form-label',
19179 html : this.fieldLabel
19183 cls : 'roo-combobox-wrap ',
19190 var labelCfg = cfg.cn[1];
19191 var contentCfg = cfg.cn[2];
19194 if(this.indicatorpos == 'right'){
19199 cls : 'control-label col-form-label',
19203 html : this.fieldLabel
19209 cls : "roo-combobox-wrap ",
19217 labelCfg = cfg.cn[0];
19218 contentCfg = cfg.cn[1];
19223 if(this.labelWidth > 12){
19224 labelCfg.style = "width: " + this.labelWidth + 'px';
19227 if(this.labelWidth < 13 && this.labelmd == 0){
19228 this.labelmd = this.labelWidth;
19231 if(this.labellg > 0){
19232 labelCfg.cls += ' col-lg-' + this.labellg;
19233 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19236 if(this.labelmd > 0){
19237 labelCfg.cls += ' col-md-' + this.labelmd;
19238 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19241 if(this.labelsm > 0){
19242 labelCfg.cls += ' col-sm-' + this.labelsm;
19243 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19246 if(this.labelxs > 0){
19247 labelCfg.cls += ' col-xs-' + this.labelxs;
19248 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19252 } else if ( this.fieldLabel.length) {
19257 cls : 'control-label',
19258 html : this.fieldLabel
19269 if(this.indicatorpos == 'right'){
19273 cls : 'control-label',
19274 html : this.fieldLabel,
19292 var settings = this;
19294 ['xs','sm','md','lg'].map(function(size){
19295 if (settings[size]) {
19296 cfg.cls += ' col-' + size + '-' + settings[size];
19303 initTouchView : function()
19305 this.renderTouchView();
19307 this.touchViewEl.on('scroll', function(){
19308 this.el.dom.scrollTop = 0;
19311 this.originalValue = this.getValue();
19313 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19315 this.inputEl().on("click", this.showTouchView, this);
19316 if (this.triggerEl) {
19317 this.triggerEl.on("click", this.showTouchView, this);
19321 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19322 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19324 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19326 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19327 this.store.on('load', this.onTouchViewLoad, this);
19328 this.store.on('loadexception', this.onTouchViewLoadException, this);
19330 if(this.hiddenName){
19332 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19334 this.hiddenField.dom.value =
19335 this.hiddenValue !== undefined ? this.hiddenValue :
19336 this.value !== undefined ? this.value : '';
19338 this.el.dom.removeAttribute('name');
19339 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19343 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19344 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19347 if(this.removable && !this.multiple){
19348 var close = this.closeTriggerEl();
19350 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19351 close.on('click', this.removeBtnClick, this, close);
19355 * fix the bug in Safari iOS8
19357 this.inputEl().on("focus", function(e){
19358 document.activeElement.blur();
19361 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19368 renderTouchView : function()
19370 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19371 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19373 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19374 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19377 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19378 this.touchViewBodyEl.setStyle('overflow', 'auto');
19380 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19381 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19383 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19384 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19388 showTouchView : function()
19394 this.touchViewHeaderEl.hide();
19396 if(this.modalTitle.length){
19397 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19398 this.touchViewHeaderEl.show();
19401 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19402 this.touchViewEl.show();
19404 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19406 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19407 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19409 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19411 if(this.modalTitle.length){
19412 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19415 this.touchViewBodyEl.setHeight(bodyHeight);
19419 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19421 this.touchViewEl.addClass(['in','show']);
19424 if(this._touchViewMask){
19425 Roo.get(document.body).addClass("x-body-masked");
19426 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19427 this._touchViewMask.setStyle('z-index', 10000);
19428 this._touchViewMask.addClass('show');
19431 this.doTouchViewQuery();
19435 hideTouchView : function()
19437 this.touchViewEl.removeClass(['in','show']);
19441 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19443 this.touchViewEl.setStyle('display', 'none');
19446 if(this._touchViewMask){
19447 this._touchViewMask.removeClass('show');
19448 Roo.get(document.body).removeClass("x-body-masked");
19452 setTouchViewValue : function()
19459 Roo.each(this.tickItems, function(o){
19464 this.hideTouchView();
19467 doTouchViewQuery : function()
19476 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19480 if(!this.alwaysQuery || this.mode == 'local'){
19481 this.onTouchViewLoad();
19488 onTouchViewBeforeLoad : function(combo,opts)
19494 onTouchViewLoad : function()
19496 if(this.store.getCount() < 1){
19497 this.onTouchViewEmptyResults();
19501 this.clearTouchView();
19503 var rawValue = this.getRawValue();
19505 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19507 this.tickItems = [];
19509 this.store.data.each(function(d, rowIndex){
19510 var row = this.touchViewListGroup.createChild(template);
19512 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19513 row.addClass(d.data.cls);
19516 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19519 html : d.data[this.displayField]
19522 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19523 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19526 row.removeClass('selected');
19527 if(!this.multiple && this.valueField &&
19528 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19531 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19532 row.addClass('selected');
19535 if(this.multiple && this.valueField &&
19536 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19540 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19541 this.tickItems.push(d.data);
19544 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19548 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19550 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19552 if(this.modalTitle.length){
19553 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19556 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19558 if(this.mobile_restrict_height && listHeight < bodyHeight){
19559 this.touchViewBodyEl.setHeight(listHeight);
19564 if(firstChecked && listHeight > bodyHeight){
19565 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19570 onTouchViewLoadException : function()
19572 this.hideTouchView();
19575 onTouchViewEmptyResults : function()
19577 this.clearTouchView();
19579 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19581 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19585 clearTouchView : function()
19587 this.touchViewListGroup.dom.innerHTML = '';
19590 onTouchViewClick : function(e, el, o)
19592 e.preventDefault();
19595 var rowIndex = o.rowIndex;
19597 var r = this.store.getAt(rowIndex);
19599 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19601 if(!this.multiple){
19602 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19603 c.dom.removeAttribute('checked');
19606 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19608 this.setFromData(r.data);
19610 var close = this.closeTriggerEl();
19616 this.hideTouchView();
19618 this.fireEvent('select', this, r, rowIndex);
19623 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19624 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19625 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19629 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19630 this.addItem(r.data);
19631 this.tickItems.push(r.data);
19635 getAutoCreateNativeIOS : function()
19638 cls: 'form-group' //input-group,
19643 cls : 'roo-ios-select'
19647 combobox.name = this.name;
19650 if (this.disabled) {
19651 combobox.disabled = true;
19654 var settings = this;
19656 ['xs','sm','md','lg'].map(function(size){
19657 if (settings[size]) {
19658 cfg.cls += ' col-' + size + '-' + settings[size];
19668 initIOSView : function()
19670 this.store.on('load', this.onIOSViewLoad, this);
19675 onIOSViewLoad : function()
19677 if(this.store.getCount() < 1){
19681 this.clearIOSView();
19683 if(this.allowBlank) {
19685 var default_text = '-- SELECT --';
19687 if(this.placeholder.length){
19688 default_text = this.placeholder;
19691 if(this.emptyTitle.length){
19692 default_text += ' - ' + this.emptyTitle + ' -';
19695 var opt = this.inputEl().createChild({
19698 html : default_text
19702 o[this.valueField] = 0;
19703 o[this.displayField] = default_text;
19705 this.ios_options.push({
19712 this.store.data.each(function(d, rowIndex){
19716 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19717 html = d.data[this.displayField];
19722 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19723 value = d.data[this.valueField];
19732 if(this.value == d.data[this.valueField]){
19733 option['selected'] = true;
19736 var opt = this.inputEl().createChild(option);
19738 this.ios_options.push({
19745 this.inputEl().on('change', function(){
19746 this.fireEvent('select', this);
19751 clearIOSView: function()
19753 this.inputEl().dom.innerHTML = '';
19755 this.ios_options = [];
19758 setIOSValue: function(v)
19762 if(!this.ios_options){
19766 Roo.each(this.ios_options, function(opts){
19768 opts.el.dom.removeAttribute('selected');
19770 if(opts.data[this.valueField] != v){
19774 opts.el.dom.setAttribute('selected', true);
19780 * @cfg {Boolean} grow
19784 * @cfg {Number} growMin
19788 * @cfg {Number} growMax
19797 Roo.apply(Roo.bootstrap.form.ComboBox, {
19801 cls: 'modal-header',
19823 cls: 'list-group-item',
19827 cls: 'roo-combobox-list-group-item-value'
19831 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19845 listItemCheckbox : {
19847 cls: 'list-group-item',
19851 cls: 'roo-combobox-list-group-item-value'
19855 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19871 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19876 cls: 'modal-footer',
19884 cls: 'col-xs-6 text-left',
19887 cls: 'btn btn-danger roo-touch-view-cancel',
19893 cls: 'col-xs-6 text-right',
19896 cls: 'btn btn-success roo-touch-view-ok',
19907 Roo.apply(Roo.bootstrap.form.ComboBox, {
19909 touchViewTemplate : {
19911 cls: 'modal fade roo-combobox-touch-view',
19915 cls: 'modal-dialog',
19916 style : 'position:fixed', // we have to fix position....
19920 cls: 'modal-content',
19922 Roo.bootstrap.form.ComboBox.header,
19923 Roo.bootstrap.form.ComboBox.body,
19924 Roo.bootstrap.form.ComboBox.footer
19933 * Ext JS Library 1.1.1
19934 * Copyright(c) 2006-2007, Ext JS, LLC.
19936 * Originally Released Under LGPL - original licence link has changed is not relivant.
19939 * <script type="text/javascript">
19944 * @extends Roo.util.Observable
19945 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19946 * This class also supports single and multi selection modes. <br>
19947 * Create a data model bound view:
19949 var store = new Roo.data.Store(...);
19951 var view = new Roo.View({
19953 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19955 singleSelect: true,
19956 selectedClass: "ydataview-selected",
19960 // listen for node click?
19961 view.on("click", function(vw, index, node, e){
19962 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19966 dataModel.load("foobar.xml");
19968 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19970 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19971 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19973 * Note: old style constructor is still suported (container, template, config)
19976 * Create a new View
19977 * @param {Object} config The config object
19980 Roo.View = function(config, depreciated_tpl, depreciated_config){
19982 this.parent = false;
19984 if (typeof(depreciated_tpl) == 'undefined') {
19985 // new way.. - universal constructor.
19986 Roo.apply(this, config);
19987 this.el = Roo.get(this.el);
19990 this.el = Roo.get(config);
19991 this.tpl = depreciated_tpl;
19992 Roo.apply(this, depreciated_config);
19994 this.wrapEl = this.el.wrap().wrap();
19995 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19998 if(typeof(this.tpl) == "string"){
19999 this.tpl = new Roo.Template(this.tpl);
20001 // support xtype ctors..
20002 this.tpl = new Roo.factory(this.tpl, Roo);
20006 this.tpl.compile();
20011 * @event beforeclick
20012 * Fires before a click is processed. Returns false to cancel the default action.
20013 * @param {Roo.View} this
20014 * @param {Number} index The index of the target node
20015 * @param {HTMLElement} node The target node
20016 * @param {Roo.EventObject} e The raw event object
20018 "beforeclick" : true,
20021 * Fires when a template node is clicked.
20022 * @param {Roo.View} this
20023 * @param {Number} index The index of the target node
20024 * @param {HTMLElement} node The target node
20025 * @param {Roo.EventObject} e The raw event object
20030 * Fires when a template node is double clicked.
20031 * @param {Roo.View} this
20032 * @param {Number} index The index of the target node
20033 * @param {HTMLElement} node The target node
20034 * @param {Roo.EventObject} e The raw event object
20038 * @event contextmenu
20039 * Fires when a template node is right clicked.
20040 * @param {Roo.View} this
20041 * @param {Number} index The index of the target node
20042 * @param {HTMLElement} node The target node
20043 * @param {Roo.EventObject} e The raw event object
20045 "contextmenu" : true,
20047 * @event selectionchange
20048 * Fires when the selected nodes change.
20049 * @param {Roo.View} this
20050 * @param {Array} selections Array of the selected nodes
20052 "selectionchange" : true,
20055 * @event beforeselect
20056 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20057 * @param {Roo.View} this
20058 * @param {HTMLElement} node The node to be selected
20059 * @param {Array} selections Array of currently selected nodes
20061 "beforeselect" : true,
20063 * @event preparedata
20064 * Fires on every row to render, to allow you to change the data.
20065 * @param {Roo.View} this
20066 * @param {Object} data to be rendered (change this)
20068 "preparedata" : true
20076 "click": this.onClick,
20077 "dblclick": this.onDblClick,
20078 "contextmenu": this.onContextMenu,
20082 this.selections = [];
20084 this.cmp = new Roo.CompositeElementLite([]);
20086 this.store = Roo.factory(this.store, Roo.data);
20087 this.setStore(this.store, true);
20090 if ( this.footer && this.footer.xtype) {
20092 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20094 this.footer.dataSource = this.store;
20095 this.footer.container = fctr;
20096 this.footer = Roo.factory(this.footer, Roo);
20097 fctr.insertFirst(this.el);
20099 // this is a bit insane - as the paging toolbar seems to detach the el..
20100 // dom.parentNode.parentNode.parentNode
20101 // they get detached?
20105 Roo.View.superclass.constructor.call(this);
20110 Roo.extend(Roo.View, Roo.util.Observable, {
20113 * @cfg {Roo.data.Store} store Data store to load data from.
20118 * @cfg {String|Roo.Element} el The container element.
20123 * @cfg {String|Roo.Template} tpl The template used by this View
20127 * @cfg {String} dataName the named area of the template to use as the data area
20128 * Works with domtemplates roo-name="name"
20132 * @cfg {String} selectedClass The css class to add to selected nodes
20134 selectedClass : "x-view-selected",
20136 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20141 * @cfg {String} text to display on mask (default Loading)
20145 * @cfg {Boolean} multiSelect Allow multiple selection
20147 multiSelect : false,
20149 * @cfg {Boolean} singleSelect Allow single selection
20151 singleSelect: false,
20154 * @cfg {Boolean} toggleSelect - selecting
20156 toggleSelect : false,
20159 * @cfg {Boolean} tickable - selecting
20164 * Returns the element this view is bound to.
20165 * @return {Roo.Element}
20167 getEl : function(){
20168 return this.wrapEl;
20174 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20176 refresh : function(){
20177 //Roo.log('refresh');
20180 // if we are using something like 'domtemplate', then
20181 // the what gets used is:
20182 // t.applySubtemplate(NAME, data, wrapping data..)
20183 // the outer template then get' applied with
20184 // the store 'extra data'
20185 // and the body get's added to the
20186 // roo-name="data" node?
20187 // <span class='roo-tpl-{name}'></span> ?????
20191 this.clearSelections();
20192 this.el.update("");
20194 var records = this.store.getRange();
20195 if(records.length < 1) {
20197 // is this valid?? = should it render a template??
20199 this.el.update(this.emptyText);
20203 if (this.dataName) {
20204 this.el.update(t.apply(this.store.meta)); //????
20205 el = this.el.child('.roo-tpl-' + this.dataName);
20208 for(var i = 0, len = records.length; i < len; i++){
20209 var data = this.prepareData(records[i].data, i, records[i]);
20210 this.fireEvent("preparedata", this, data, i, records[i]);
20212 var d = Roo.apply({}, data);
20215 Roo.apply(d, {'roo-id' : Roo.id()});
20219 Roo.each(this.parent.item, function(item){
20220 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20223 Roo.apply(d, {'roo-data-checked' : 'checked'});
20227 html[html.length] = Roo.util.Format.trim(
20229 t.applySubtemplate(this.dataName, d, this.store.meta) :
20236 el.update(html.join(""));
20237 this.nodes = el.dom.childNodes;
20238 this.updateIndexes(0);
20243 * Function to override to reformat the data that is sent to
20244 * the template for each node.
20245 * DEPRICATED - use the preparedata event handler.
20246 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20247 * a JSON object for an UpdateManager bound view).
20249 prepareData : function(data, index, record)
20251 this.fireEvent("preparedata", this, data, index, record);
20255 onUpdate : function(ds, record){
20256 // Roo.log('on update');
20257 this.clearSelections();
20258 var index = this.store.indexOf(record);
20259 var n = this.nodes[index];
20260 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20261 n.parentNode.removeChild(n);
20262 this.updateIndexes(index, index);
20268 onAdd : function(ds, records, index)
20270 //Roo.log(['on Add', ds, records, index] );
20271 this.clearSelections();
20272 if(this.nodes.length == 0){
20276 var n = this.nodes[index];
20277 for(var i = 0, len = records.length; i < len; i++){
20278 var d = this.prepareData(records[i].data, i, records[i]);
20280 this.tpl.insertBefore(n, d);
20283 this.tpl.append(this.el, d);
20286 this.updateIndexes(index);
20289 onRemove : function(ds, record, index){
20290 // Roo.log('onRemove');
20291 this.clearSelections();
20292 var el = this.dataName ?
20293 this.el.child('.roo-tpl-' + this.dataName) :
20296 el.dom.removeChild(this.nodes[index]);
20297 this.updateIndexes(index);
20301 * Refresh an individual node.
20302 * @param {Number} index
20304 refreshNode : function(index){
20305 this.onUpdate(this.store, this.store.getAt(index));
20308 updateIndexes : function(startIndex, endIndex){
20309 var ns = this.nodes;
20310 startIndex = startIndex || 0;
20311 endIndex = endIndex || ns.length - 1;
20312 for(var i = startIndex; i <= endIndex; i++){
20313 ns[i].nodeIndex = i;
20318 * Changes the data store this view uses and refresh the view.
20319 * @param {Store} store
20321 setStore : function(store, initial){
20322 if(!initial && this.store){
20323 this.store.un("datachanged", this.refresh);
20324 this.store.un("add", this.onAdd);
20325 this.store.un("remove", this.onRemove);
20326 this.store.un("update", this.onUpdate);
20327 this.store.un("clear", this.refresh);
20328 this.store.un("beforeload", this.onBeforeLoad);
20329 this.store.un("load", this.onLoad);
20330 this.store.un("loadexception", this.onLoad);
20334 store.on("datachanged", this.refresh, this);
20335 store.on("add", this.onAdd, this);
20336 store.on("remove", this.onRemove, this);
20337 store.on("update", this.onUpdate, this);
20338 store.on("clear", this.refresh, this);
20339 store.on("beforeload", this.onBeforeLoad, this);
20340 store.on("load", this.onLoad, this);
20341 store.on("loadexception", this.onLoad, this);
20349 * onbeforeLoad - masks the loading area.
20352 onBeforeLoad : function(store,opts)
20354 //Roo.log('onBeforeLoad');
20356 this.el.update("");
20358 this.el.mask(this.mask ? this.mask : "Loading" );
20360 onLoad : function ()
20367 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20368 * @param {HTMLElement} node
20369 * @return {HTMLElement} The template node
20371 findItemFromChild : function(node){
20372 var el = this.dataName ?
20373 this.el.child('.roo-tpl-' + this.dataName,true) :
20376 if(!node || node.parentNode == el){
20379 var p = node.parentNode;
20380 while(p && p != el){
20381 if(p.parentNode == el){
20390 onClick : function(e){
20391 var item = this.findItemFromChild(e.getTarget());
20393 var index = this.indexOf(item);
20394 if(this.onItemClick(item, index, e) !== false){
20395 this.fireEvent("click", this, index, item, e);
20398 this.clearSelections();
20403 onContextMenu : function(e){
20404 var item = this.findItemFromChild(e.getTarget());
20406 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20411 onDblClick : function(e){
20412 var item = this.findItemFromChild(e.getTarget());
20414 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20418 onItemClick : function(item, index, e)
20420 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20423 if (this.toggleSelect) {
20424 var m = this.isSelected(item) ? 'unselect' : 'select';
20427 _t[m](item, true, false);
20430 if(this.multiSelect || this.singleSelect){
20431 if(this.multiSelect && e.shiftKey && this.lastSelection){
20432 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20434 this.select(item, this.multiSelect && e.ctrlKey);
20435 this.lastSelection = item;
20438 if(!this.tickable){
20439 e.preventDefault();
20447 * Get the number of selected nodes.
20450 getSelectionCount : function(){
20451 return this.selections.length;
20455 * Get the currently selected nodes.
20456 * @return {Array} An array of HTMLElements
20458 getSelectedNodes : function(){
20459 return this.selections;
20463 * Get the indexes of the selected nodes.
20466 getSelectedIndexes : function(){
20467 var indexes = [], s = this.selections;
20468 for(var i = 0, len = s.length; i < len; i++){
20469 indexes.push(s[i].nodeIndex);
20475 * Clear all selections
20476 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20478 clearSelections : function(suppressEvent){
20479 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20480 this.cmp.elements = this.selections;
20481 this.cmp.removeClass(this.selectedClass);
20482 this.selections = [];
20483 if(!suppressEvent){
20484 this.fireEvent("selectionchange", this, this.selections);
20490 * Returns true if the passed node is selected
20491 * @param {HTMLElement/Number} node The node or node index
20492 * @return {Boolean}
20494 isSelected : function(node){
20495 var s = this.selections;
20499 node = this.getNode(node);
20500 return s.indexOf(node) !== -1;
20505 * @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
20506 * @param {Boolean} keepExisting (optional) true to keep existing selections
20507 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20509 select : function(nodeInfo, keepExisting, suppressEvent){
20510 if(nodeInfo instanceof Array){
20512 this.clearSelections(true);
20514 for(var i = 0, len = nodeInfo.length; i < len; i++){
20515 this.select(nodeInfo[i], true, true);
20519 var node = this.getNode(nodeInfo);
20520 if(!node || this.isSelected(node)){
20521 return; // already selected.
20524 this.clearSelections(true);
20527 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20528 Roo.fly(node).addClass(this.selectedClass);
20529 this.selections.push(node);
20530 if(!suppressEvent){
20531 this.fireEvent("selectionchange", this, this.selections);
20539 * @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
20540 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20541 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20543 unselect : function(nodeInfo, keepExisting, suppressEvent)
20545 if(nodeInfo instanceof Array){
20546 Roo.each(this.selections, function(s) {
20547 this.unselect(s, nodeInfo);
20551 var node = this.getNode(nodeInfo);
20552 if(!node || !this.isSelected(node)){
20553 //Roo.log("not selected");
20554 return; // not selected.
20558 Roo.each(this.selections, function(s) {
20560 Roo.fly(node).removeClass(this.selectedClass);
20567 this.selections= ns;
20568 this.fireEvent("selectionchange", this, this.selections);
20572 * Gets a template node.
20573 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20574 * @return {HTMLElement} The node or null if it wasn't found
20576 getNode : function(nodeInfo){
20577 if(typeof nodeInfo == "string"){
20578 return document.getElementById(nodeInfo);
20579 }else if(typeof nodeInfo == "number"){
20580 return this.nodes[nodeInfo];
20586 * Gets a range template nodes.
20587 * @param {Number} startIndex
20588 * @param {Number} endIndex
20589 * @return {Array} An array of nodes
20591 getNodes : function(start, end){
20592 var ns = this.nodes;
20593 start = start || 0;
20594 end = typeof end == "undefined" ? ns.length - 1 : end;
20597 for(var i = start; i <= end; i++){
20601 for(var i = start; i >= end; i--){
20609 * Finds the index of the passed node
20610 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20611 * @return {Number} The index of the node or -1
20613 indexOf : function(node){
20614 node = this.getNode(node);
20615 if(typeof node.nodeIndex == "number"){
20616 return node.nodeIndex;
20618 var ns = this.nodes;
20619 for(var i = 0, len = ns.length; i < len; i++){
20630 * based on jquery fullcalendar
20634 Roo.bootstrap = Roo.bootstrap || {};
20636 * @class Roo.bootstrap.Calendar
20637 * @extends Roo.bootstrap.Component
20638 * Bootstrap Calendar class
20639 * @cfg {Boolean} loadMask (true|false) default false
20640 * @cfg {Object} header generate the user specific header of the calendar, default false
20643 * Create a new Container
20644 * @param {Object} config The config object
20649 Roo.bootstrap.Calendar = function(config){
20650 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20654 * Fires when a date is selected
20655 * @param {DatePicker} this
20656 * @param {Date} date The selected date
20660 * @event monthchange
20661 * Fires when the displayed month changes
20662 * @param {DatePicker} this
20663 * @param {Date} date The selected month
20665 'monthchange': true,
20667 * @event evententer
20668 * Fires when mouse over an event
20669 * @param {Calendar} this
20670 * @param {event} Event
20672 'evententer': true,
20674 * @event eventleave
20675 * Fires when the mouse leaves an
20676 * @param {Calendar} this
20679 'eventleave': true,
20681 * @event eventclick
20682 * Fires when the mouse click an
20683 * @param {Calendar} this
20692 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20695 * @cfg {Roo.data.Store} store
20696 * The data source for the calendar
20700 * @cfg {Number} startDay
20701 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20709 getAutoCreate : function(){
20712 var fc_button = function(name, corner, style, content ) {
20713 return Roo.apply({},{
20715 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20717 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20720 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20731 style : 'width:100%',
20738 cls : 'fc-header-left',
20740 fc_button('prev', 'left', 'arrow', '‹' ),
20741 fc_button('next', 'right', 'arrow', '›' ),
20742 { tag: 'span', cls: 'fc-header-space' },
20743 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20751 cls : 'fc-header-center',
20755 cls: 'fc-header-title',
20758 html : 'month / year'
20766 cls : 'fc-header-right',
20768 /* fc_button('month', 'left', '', 'month' ),
20769 fc_button('week', '', '', 'week' ),
20770 fc_button('day', 'right', '', 'day' )
20782 header = this.header;
20785 var cal_heads = function() {
20787 // fixme - handle this.
20789 for (var i =0; i < Date.dayNames.length; i++) {
20790 var d = Date.dayNames[i];
20793 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20794 html : d.substring(0,3)
20798 ret[0].cls += ' fc-first';
20799 ret[6].cls += ' fc-last';
20802 var cal_cell = function(n) {
20805 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20810 cls: 'fc-day-number',
20814 cls: 'fc-day-content',
20818 style: 'position: relative;' // height: 17px;
20830 var cal_rows = function() {
20833 for (var r = 0; r < 6; r++) {
20840 for (var i =0; i < Date.dayNames.length; i++) {
20841 var d = Date.dayNames[i];
20842 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20845 row.cn[0].cls+=' fc-first';
20846 row.cn[0].cn[0].style = 'min-height:90px';
20847 row.cn[6].cls+=' fc-last';
20851 ret[0].cls += ' fc-first';
20852 ret[4].cls += ' fc-prev-last';
20853 ret[5].cls += ' fc-last';
20860 cls: 'fc-border-separate',
20861 style : 'width:100%',
20869 cls : 'fc-first fc-last',
20887 cls : 'fc-content',
20888 style : "position: relative;",
20891 cls : 'fc-view fc-view-month fc-grid',
20892 style : 'position: relative',
20893 unselectable : 'on',
20896 cls : 'fc-event-container',
20897 style : 'position:absolute;z-index:8;top:0;left:0;'
20915 initEvents : function()
20918 throw "can not find store for calendar";
20924 style: "text-align:center",
20928 style: "background-color:white;width:50%;margin:250 auto",
20932 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20943 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20945 var size = this.el.select('.fc-content', true).first().getSize();
20946 this.maskEl.setSize(size.width, size.height);
20947 this.maskEl.enableDisplayMode("block");
20948 if(!this.loadMask){
20949 this.maskEl.hide();
20952 this.store = Roo.factory(this.store, Roo.data);
20953 this.store.on('load', this.onLoad, this);
20954 this.store.on('beforeload', this.onBeforeLoad, this);
20958 this.cells = this.el.select('.fc-day',true);
20959 //Roo.log(this.cells);
20960 this.textNodes = this.el.query('.fc-day-number');
20961 this.cells.addClassOnOver('fc-state-hover');
20963 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20964 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20965 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20966 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20968 this.on('monthchange', this.onMonthChange, this);
20970 this.update(new Date().clearTime());
20973 resize : function() {
20974 var sz = this.el.getSize();
20976 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20977 this.el.select('.fc-day-content div',true).setHeight(34);
20982 showPrevMonth : function(e){
20983 this.update(this.activeDate.add("mo", -1));
20985 showToday : function(e){
20986 this.update(new Date().clearTime());
20989 showNextMonth : function(e){
20990 this.update(this.activeDate.add("mo", 1));
20994 showPrevYear : function(){
20995 this.update(this.activeDate.add("y", -1));
20999 showNextYear : function(){
21000 this.update(this.activeDate.add("y", 1));
21005 update : function(date)
21007 var vd = this.activeDate;
21008 this.activeDate = date;
21009 // if(vd && this.el){
21010 // var t = date.getTime();
21011 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21012 // Roo.log('using add remove');
21014 // this.fireEvent('monthchange', this, date);
21016 // this.cells.removeClass("fc-state-highlight");
21017 // this.cells.each(function(c){
21018 // if(c.dateValue == t){
21019 // c.addClass("fc-state-highlight");
21020 // setTimeout(function(){
21021 // try{c.dom.firstChild.focus();}catch(e){}
21031 var days = date.getDaysInMonth();
21033 var firstOfMonth = date.getFirstDateOfMonth();
21034 var startingPos = firstOfMonth.getDay()-this.startDay;
21036 if(startingPos < this.startDay){
21040 var pm = date.add(Date.MONTH, -1);
21041 var prevStart = pm.getDaysInMonth()-startingPos;
21043 this.cells = this.el.select('.fc-day',true);
21044 this.textNodes = this.el.query('.fc-day-number');
21045 this.cells.addClassOnOver('fc-state-hover');
21047 var cells = this.cells.elements;
21048 var textEls = this.textNodes;
21050 Roo.each(cells, function(cell){
21051 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21054 days += startingPos;
21056 // convert everything to numbers so it's fast
21057 var day = 86400000;
21058 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21061 //Roo.log(prevStart);
21063 var today = new Date().clearTime().getTime();
21064 var sel = date.clearTime().getTime();
21065 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21066 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21067 var ddMatch = this.disabledDatesRE;
21068 var ddText = this.disabledDatesText;
21069 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21070 var ddaysText = this.disabledDaysText;
21071 var format = this.format;
21073 var setCellClass = function(cal, cell){
21077 //Roo.log('set Cell Class');
21079 var t = d.getTime();
21083 cell.dateValue = t;
21085 cell.className += " fc-today";
21086 cell.className += " fc-state-highlight";
21087 cell.title = cal.todayText;
21090 // disable highlight in other month..
21091 //cell.className += " fc-state-highlight";
21096 cell.className = " fc-state-disabled";
21097 cell.title = cal.minText;
21101 cell.className = " fc-state-disabled";
21102 cell.title = cal.maxText;
21106 if(ddays.indexOf(d.getDay()) != -1){
21107 cell.title = ddaysText;
21108 cell.className = " fc-state-disabled";
21111 if(ddMatch && format){
21112 var fvalue = d.dateFormat(format);
21113 if(ddMatch.test(fvalue)){
21114 cell.title = ddText.replace("%0", fvalue);
21115 cell.className = " fc-state-disabled";
21119 if (!cell.initialClassName) {
21120 cell.initialClassName = cell.dom.className;
21123 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21128 for(; i < startingPos; i++) {
21129 textEls[i].innerHTML = (++prevStart);
21130 d.setDate(d.getDate()+1);
21132 cells[i].className = "fc-past fc-other-month";
21133 setCellClass(this, cells[i]);
21138 for(; i < days; i++){
21139 intDay = i - startingPos + 1;
21140 textEls[i].innerHTML = (intDay);
21141 d.setDate(d.getDate()+1);
21143 cells[i].className = ''; // "x-date-active";
21144 setCellClass(this, cells[i]);
21148 for(; i < 42; i++) {
21149 textEls[i].innerHTML = (++extraDays);
21150 d.setDate(d.getDate()+1);
21152 cells[i].className = "fc-future fc-other-month";
21153 setCellClass(this, cells[i]);
21156 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21158 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21160 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21161 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21163 if(totalRows != 6){
21164 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21165 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21168 this.fireEvent('monthchange', this, date);
21172 if(!this.internalRender){
21173 var main = this.el.dom.firstChild;
21174 var w = main.offsetWidth;
21175 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21176 Roo.fly(main).setWidth(w);
21177 this.internalRender = true;
21178 // opera does not respect the auto grow header center column
21179 // then, after it gets a width opera refuses to recalculate
21180 // without a second pass
21181 if(Roo.isOpera && !this.secondPass){
21182 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21183 this.secondPass = true;
21184 this.update.defer(10, this, [date]);
21191 findCell : function(dt) {
21192 dt = dt.clearTime().getTime();
21194 this.cells.each(function(c){
21195 //Roo.log("check " +c.dateValue + '?=' + dt);
21196 if(c.dateValue == dt){
21206 findCells : function(ev) {
21207 var s = ev.start.clone().clearTime().getTime();
21209 var e= ev.end.clone().clearTime().getTime();
21212 this.cells.each(function(c){
21213 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21215 if(c.dateValue > e){
21218 if(c.dateValue < s){
21227 // findBestRow: function(cells)
21231 // for (var i =0 ; i < cells.length;i++) {
21232 // ret = Math.max(cells[i].rows || 0,ret);
21239 addItem : function(ev)
21241 // look for vertical location slot in
21242 var cells = this.findCells(ev);
21244 // ev.row = this.findBestRow(cells);
21246 // work out the location.
21250 for(var i =0; i < cells.length; i++) {
21252 cells[i].row = cells[0].row;
21255 cells[i].row = cells[i].row + 1;
21265 if (crow.start.getY() == cells[i].getY()) {
21267 crow.end = cells[i];
21284 cells[0].events.push(ev);
21286 this.calevents.push(ev);
21289 clearEvents: function() {
21291 if(!this.calevents){
21295 Roo.each(this.cells.elements, function(c){
21301 Roo.each(this.calevents, function(e) {
21302 Roo.each(e.els, function(el) {
21303 el.un('mouseenter' ,this.onEventEnter, this);
21304 el.un('mouseleave' ,this.onEventLeave, this);
21309 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21315 renderEvents: function()
21319 this.cells.each(function(c) {
21328 if(c.row != c.events.length){
21329 r = 4 - (4 - (c.row - c.events.length));
21332 c.events = ev.slice(0, r);
21333 c.more = ev.slice(r);
21335 if(c.more.length && c.more.length == 1){
21336 c.events.push(c.more.pop());
21339 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21343 this.cells.each(function(c) {
21345 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21348 for (var e = 0; e < c.events.length; e++){
21349 var ev = c.events[e];
21350 var rows = ev.rows;
21352 for(var i = 0; i < rows.length; i++) {
21354 // how many rows should it span..
21357 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21358 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21360 unselectable : "on",
21363 cls: 'fc-event-inner',
21367 // cls: 'fc-event-time',
21368 // html : cells.length > 1 ? '' : ev.time
21372 cls: 'fc-event-title',
21373 html : String.format('{0}', ev.title)
21380 cls: 'ui-resizable-handle ui-resizable-e',
21381 html : '  '
21388 cfg.cls += ' fc-event-start';
21390 if ((i+1) == rows.length) {
21391 cfg.cls += ' fc-event-end';
21394 var ctr = _this.el.select('.fc-event-container',true).first();
21395 var cg = ctr.createChild(cfg);
21397 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21398 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21400 var r = (c.more.length) ? 1 : 0;
21401 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21402 cg.setWidth(ebox.right - sbox.x -2);
21404 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21405 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21406 cg.on('click', _this.onEventClick, _this, ev);
21417 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21418 style : 'position: absolute',
21419 unselectable : "on",
21422 cls: 'fc-event-inner',
21426 cls: 'fc-event-title',
21434 cls: 'ui-resizable-handle ui-resizable-e',
21435 html : '  '
21441 var ctr = _this.el.select('.fc-event-container',true).first();
21442 var cg = ctr.createChild(cfg);
21444 var sbox = c.select('.fc-day-content',true).first().getBox();
21445 var ebox = c.select('.fc-day-content',true).first().getBox();
21447 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21448 cg.setWidth(ebox.right - sbox.x -2);
21450 cg.on('click', _this.onMoreEventClick, _this, c.more);
21460 onEventEnter: function (e, el,event,d) {
21461 this.fireEvent('evententer', this, el, event);
21464 onEventLeave: function (e, el,event,d) {
21465 this.fireEvent('eventleave', this, el, event);
21468 onEventClick: function (e, el,event,d) {
21469 this.fireEvent('eventclick', this, el, event);
21472 onMonthChange: function () {
21476 onMoreEventClick: function(e, el, more)
21480 this.calpopover.placement = 'right';
21481 this.calpopover.setTitle('More');
21483 this.calpopover.setContent('');
21485 var ctr = this.calpopover.el.select('.popover-content', true).first();
21487 Roo.each(more, function(m){
21489 cls : 'fc-event-hori fc-event-draggable',
21492 var cg = ctr.createChild(cfg);
21494 cg.on('click', _this.onEventClick, _this, m);
21497 this.calpopover.show(el);
21502 onLoad: function ()
21504 this.calevents = [];
21507 if(this.store.getCount() > 0){
21508 this.store.data.each(function(d){
21511 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21512 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21513 time : d.data.start_time,
21514 title : d.data.title,
21515 description : d.data.description,
21516 venue : d.data.venue
21521 this.renderEvents();
21523 if(this.calevents.length && this.loadMask){
21524 this.maskEl.hide();
21528 onBeforeLoad: function()
21530 this.clearEvents();
21532 this.maskEl.show();
21546 * @class Roo.bootstrap.Popover
21547 * @extends Roo.bootstrap.Component
21548 * @parent none builder
21549 * @children Roo.bootstrap.Component
21550 * Bootstrap Popover class
21551 * @cfg {String} html contents of the popover (or false to use children..)
21552 * @cfg {String} title of popover (or false to hide)
21553 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21554 * @cfg {String} trigger click || hover (or false to trigger manually)
21555 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21556 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21557 * - if false and it has a 'parent' then it will be automatically added to that element
21558 * - if string - Roo.get will be called
21559 * @cfg {Number} delay - delay before showing
21562 * Create a new Popover
21563 * @param {Object} config The config object
21566 Roo.bootstrap.Popover = function(config){
21567 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21573 * After the popover show
21575 * @param {Roo.bootstrap.Popover} this
21580 * After the popover hide
21582 * @param {Roo.bootstrap.Popover} this
21588 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21593 placement : 'right',
21594 trigger : 'hover', // hover
21600 can_build_overlaid : false,
21602 maskEl : false, // the mask element
21605 alignEl : false, // when show is called with an element - this get's stored.
21607 getChildContainer : function()
21609 return this.contentEl;
21612 getPopoverHeader : function()
21614 this.title = true; // flag not to hide it..
21615 this.headerEl.addClass('p-0');
21616 return this.headerEl
21620 getAutoCreate : function(){
21623 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21624 style: 'display:block',
21630 cls : 'popover-inner ',
21634 cls: 'popover-title popover-header',
21635 html : this.title === false ? '' : this.title
21638 cls : 'popover-content popover-body ' + (this.cls || ''),
21639 html : this.html || ''
21650 * @param {string} the title
21652 setTitle: function(str)
21656 this.headerEl.dom.innerHTML = str;
21661 * @param {string} the body content
21663 setContent: function(str)
21666 if (this.contentEl) {
21667 this.contentEl.dom.innerHTML = str;
21671 // as it get's added to the bottom of the page.
21672 onRender : function(ct, position)
21674 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21679 var cfg = Roo.apply({}, this.getAutoCreate());
21683 cfg.cls += ' ' + this.cls;
21686 cfg.style = this.style;
21688 //Roo.log("adding to ");
21689 this.el = Roo.get(document.body).createChild(cfg, position);
21690 // Roo.log(this.el);
21693 this.contentEl = this.el.select('.popover-content',true).first();
21694 this.headerEl = this.el.select('.popover-title',true).first();
21697 if(typeof(this.items) != 'undefined'){
21698 var items = this.items;
21701 for(var i =0;i < items.length;i++) {
21702 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21706 this.items = nitems;
21708 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21709 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21716 resizeMask : function()
21718 this.maskEl.setSize(
21719 Roo.lib.Dom.getViewWidth(true),
21720 Roo.lib.Dom.getViewHeight(true)
21724 initEvents : function()
21728 Roo.bootstrap.Popover.register(this);
21731 this.arrowEl = this.el.select('.arrow',true).first();
21732 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21733 this.el.enableDisplayMode('block');
21737 if (this.over === false && !this.parent()) {
21740 if (this.triggers === false) {
21745 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21746 var triggers = this.trigger ? this.trigger.split(' ') : [];
21747 Roo.each(triggers, function(trigger) {
21749 if (trigger == 'click') {
21750 on_el.on('click', this.toggle, this);
21751 } else if (trigger != 'manual') {
21752 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21753 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21755 on_el.on(eventIn ,this.enter, this);
21756 on_el.on(eventOut, this.leave, this);
21766 toggle : function () {
21767 this.hoverState == 'in' ? this.leave() : this.enter();
21770 enter : function () {
21772 clearTimeout(this.timeout);
21774 this.hoverState = 'in';
21776 if (!this.delay || !this.delay.show) {
21781 this.timeout = setTimeout(function () {
21782 if (_t.hoverState == 'in') {
21785 }, this.delay.show)
21788 leave : function() {
21789 clearTimeout(this.timeout);
21791 this.hoverState = 'out';
21793 if (!this.delay || !this.delay.hide) {
21798 this.timeout = setTimeout(function () {
21799 if (_t.hoverState == 'out') {
21802 }, this.delay.hide)
21806 * update the position of the dialog
21807 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21812 doAlign : function()
21815 if (this.alignEl) {
21816 this.updatePosition(this.placement, true);
21819 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21820 var es = this.el.getSize();
21821 var x = Roo.lib.Dom.getViewWidth()/2;
21822 var y = Roo.lib.Dom.getViewHeight()/2;
21823 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21835 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21836 * @param {string} (left|right|top|bottom) position
21838 show : function (on_el, placement)
21840 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21841 on_el = on_el || false; // default to false
21844 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21845 on_el = this.parent().el;
21846 } else if (this.over) {
21847 on_el = Roo.get(this.over);
21852 this.alignEl = Roo.get( on_el );
21855 this.render(document.body);
21861 if (this.title === false) {
21862 this.headerEl.hide();
21867 this.el.dom.style.display = 'block';
21871 //var arrow = this.el.select('.arrow',true).first();
21872 //arrow.set(align[2],
21874 this.el.addClass('in');
21878 this.hoverState = 'in';
21881 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21882 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21883 this.maskEl.dom.style.display = 'block';
21884 this.maskEl.addClass('show');
21886 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21888 this.fireEvent('show', this);
21892 * fire this manually after loading a grid in the table for example
21893 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21894 * @param {Boolean} try and move it if we cant get right position.
21896 updatePosition : function(placement, try_move)
21898 // allow for calling with no parameters
21899 placement = placement ? placement : this.placement;
21900 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21902 this.el.removeClass([
21903 'fade','top','bottom', 'left', 'right','in',
21904 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21906 this.el.addClass(placement + ' bs-popover-' + placement);
21908 if (!this.alignEl ) {
21912 switch (placement) {
21914 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21915 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21916 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21917 //normal display... or moved up/down.
21918 this.el.setXY(offset);
21919 var xy = this.alignEl.getAnchorXY('tr', false);
21921 this.arrowEl.setXY(xy);
21924 // continue through...
21925 return this.updatePosition('left', false);
21929 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21930 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21931 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21932 //normal display... or moved up/down.
21933 this.el.setXY(offset);
21934 var xy = this.alignEl.getAnchorXY('tl', false);
21935 xy[0]-=10;xy[1]+=5; // << fix me
21936 this.arrowEl.setXY(xy);
21940 return this.updatePosition('right', false);
21943 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21944 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21945 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21946 //normal display... or moved up/down.
21947 this.el.setXY(offset);
21948 var xy = this.alignEl.getAnchorXY('t', false);
21949 xy[1]-=10; // << fix me
21950 this.arrowEl.setXY(xy);
21954 return this.updatePosition('bottom', false);
21957 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21958 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21959 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21960 //normal display... or moved up/down.
21961 this.el.setXY(offset);
21962 var xy = this.alignEl.getAnchorXY('b', false);
21963 xy[1]+=2; // << fix me
21964 this.arrowEl.setXY(xy);
21968 return this.updatePosition('top', false);
21979 this.el.setXY([0,0]);
21980 this.el.removeClass('in');
21982 this.hoverState = null;
21983 this.maskEl.hide(); // always..
21984 this.fireEvent('hide', this);
21990 Roo.apply(Roo.bootstrap.Popover, {
21993 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21994 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21995 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21996 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22001 clickHander : false,
22005 onMouseDown : function(e)
22007 if (this.popups.length && !e.getTarget(".roo-popover")) {
22008 /// what is nothing is showing..
22017 register : function(popup)
22019 if (!Roo.bootstrap.Popover.clickHandler) {
22020 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22022 // hide other popups.
22023 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22024 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22025 this.hideAll(); //<< why?
22026 //this.popups.push(popup);
22028 hideAll : function()
22030 this.popups.forEach(function(p) {
22034 onShow : function() {
22035 Roo.bootstrap.Popover.popups.push(this);
22037 onHide : function() {
22038 Roo.bootstrap.Popover.popups.remove(this);
22043 * @class Roo.bootstrap.PopoverNav
22044 * @extends Roo.bootstrap.nav.Simplebar
22045 * @parent Roo.bootstrap.Popover
22046 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22048 * Bootstrap Popover header navigation class
22049 * FIXME? should this go under nav?
22053 * Create a new Popover Header Navigation
22054 * @param {Object} config The config object
22057 Roo.bootstrap.PopoverNav = function(config){
22058 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22061 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22064 container_method : 'getPopoverHeader'
22082 * @class Roo.bootstrap.Progress
22083 * @extends Roo.bootstrap.Component
22084 * @children Roo.bootstrap.ProgressBar
22085 * Bootstrap Progress class
22086 * @cfg {Boolean} striped striped of the progress bar
22087 * @cfg {Boolean} active animated of the progress bar
22091 * Create a new Progress
22092 * @param {Object} config The config object
22095 Roo.bootstrap.Progress = function(config){
22096 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22099 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22104 getAutoCreate : function(){
22112 cfg.cls += ' progress-striped';
22116 cfg.cls += ' active';
22135 * @class Roo.bootstrap.ProgressBar
22136 * @extends Roo.bootstrap.Component
22137 * Bootstrap ProgressBar class
22138 * @cfg {Number} aria_valuenow aria-value now
22139 * @cfg {Number} aria_valuemin aria-value min
22140 * @cfg {Number} aria_valuemax aria-value max
22141 * @cfg {String} label label for the progress bar
22142 * @cfg {String} panel (success | info | warning | danger )
22143 * @cfg {String} role role of the progress bar
22144 * @cfg {String} sr_only text
22148 * Create a new ProgressBar
22149 * @param {Object} config The config object
22152 Roo.bootstrap.ProgressBar = function(config){
22153 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22156 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22160 aria_valuemax : 100,
22166 getAutoCreate : function()
22171 cls: 'progress-bar',
22172 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22184 cfg.role = this.role;
22187 if(this.aria_valuenow){
22188 cfg['aria-valuenow'] = this.aria_valuenow;
22191 if(this.aria_valuemin){
22192 cfg['aria-valuemin'] = this.aria_valuemin;
22195 if(this.aria_valuemax){
22196 cfg['aria-valuemax'] = this.aria_valuemax;
22199 if(this.label && !this.sr_only){
22200 cfg.html = this.label;
22204 cfg.cls += ' progress-bar-' + this.panel;
22210 update : function(aria_valuenow)
22212 this.aria_valuenow = aria_valuenow;
22214 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22222 * @class Roo.bootstrap.TabGroup
22223 * @extends Roo.bootstrap.Column
22224 * @children Roo.bootstrap.TabPanel
22225 * Bootstrap Column class
22226 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22227 * @cfg {Boolean} carousel true to make the group behave like a carousel
22228 * @cfg {Boolean} bullets show bullets for the panels
22229 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22230 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22231 * @cfg {Boolean} showarrow (true|false) show arrow default true
22234 * Create a new TabGroup
22235 * @param {Object} config The config object
22238 Roo.bootstrap.TabGroup = function(config){
22239 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22241 this.navId = Roo.id();
22244 Roo.bootstrap.TabGroup.register(this);
22248 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22251 transition : false,
22256 slideOnTouch : false,
22259 getAutoCreate : function()
22261 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22263 cfg.cls += ' tab-content';
22265 if (this.carousel) {
22266 cfg.cls += ' carousel slide';
22269 cls : 'carousel-inner',
22273 if(this.bullets && !Roo.isTouch){
22276 cls : 'carousel-bullets',
22280 if(this.bullets_cls){
22281 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22288 cfg.cn[0].cn.push(bullets);
22291 if(this.showarrow){
22292 cfg.cn[0].cn.push({
22294 class : 'carousel-arrow',
22298 class : 'carousel-prev',
22302 class : 'fa fa-chevron-left'
22308 class : 'carousel-next',
22312 class : 'fa fa-chevron-right'
22325 initEvents: function()
22327 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22328 // this.el.on("touchstart", this.onTouchStart, this);
22331 if(this.autoslide){
22334 this.slideFn = window.setInterval(function() {
22335 _this.showPanelNext();
22339 if(this.showarrow){
22340 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22341 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22347 // onTouchStart : function(e, el, o)
22349 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22353 // this.showPanelNext();
22357 getChildContainer : function()
22359 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22363 * register a Navigation item
22364 * @param {Roo.bootstrap.nav.Item} the navitem to add
22366 register : function(item)
22368 this.tabs.push( item);
22369 item.navId = this.navId; // not really needed..
22374 getActivePanel : function()
22377 Roo.each(this.tabs, function(t) {
22387 getPanelByName : function(n)
22390 Roo.each(this.tabs, function(t) {
22391 if (t.tabId == n) {
22399 indexOfPanel : function(p)
22402 Roo.each(this.tabs, function(t,i) {
22403 if (t.tabId == p.tabId) {
22412 * show a specific panel
22413 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22414 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22416 showPanel : function (pan)
22418 if(this.transition || typeof(pan) == 'undefined'){
22419 Roo.log("waiting for the transitionend");
22423 if (typeof(pan) == 'number') {
22424 pan = this.tabs[pan];
22427 if (typeof(pan) == 'string') {
22428 pan = this.getPanelByName(pan);
22431 var cur = this.getActivePanel();
22434 Roo.log('pan or acitve pan is undefined');
22438 if (pan.tabId == this.getActivePanel().tabId) {
22442 if (false === cur.fireEvent('beforedeactivate')) {
22446 if(this.bullets > 0 && !Roo.isTouch){
22447 this.setActiveBullet(this.indexOfPanel(pan));
22450 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22452 //class="carousel-item carousel-item-next carousel-item-left"
22454 this.transition = true;
22455 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22456 var lr = dir == 'next' ? 'left' : 'right';
22457 pan.el.addClass(dir); // or prev
22458 pan.el.addClass('carousel-item-' + dir); // or prev
22459 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22460 cur.el.addClass(lr); // or right
22461 pan.el.addClass(lr);
22462 cur.el.addClass('carousel-item-' +lr); // or right
22463 pan.el.addClass('carousel-item-' +lr);
22467 cur.el.on('transitionend', function() {
22468 Roo.log("trans end?");
22470 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22471 pan.setActive(true);
22473 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22474 cur.setActive(false);
22476 _this.transition = false;
22478 }, this, { single: true } );
22483 cur.setActive(false);
22484 pan.setActive(true);
22489 showPanelNext : function()
22491 var i = this.indexOfPanel(this.getActivePanel());
22493 if (i >= this.tabs.length - 1 && !this.autoslide) {
22497 if (i >= this.tabs.length - 1 && this.autoslide) {
22501 this.showPanel(this.tabs[i+1]);
22504 showPanelPrev : function()
22506 var i = this.indexOfPanel(this.getActivePanel());
22508 if (i < 1 && !this.autoslide) {
22512 if (i < 1 && this.autoslide) {
22513 i = this.tabs.length;
22516 this.showPanel(this.tabs[i-1]);
22520 addBullet: function()
22522 if(!this.bullets || Roo.isTouch){
22525 var ctr = this.el.select('.carousel-bullets',true).first();
22526 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22527 var bullet = ctr.createChild({
22528 cls : 'bullet bullet-' + i
22529 },ctr.dom.lastChild);
22534 bullet.on('click', (function(e, el, o, ii, t){
22536 e.preventDefault();
22538 this.showPanel(ii);
22540 if(this.autoslide && this.slideFn){
22541 clearInterval(this.slideFn);
22542 this.slideFn = window.setInterval(function() {
22543 _this.showPanelNext();
22547 }).createDelegate(this, [i, bullet], true));
22552 setActiveBullet : function(i)
22558 Roo.each(this.el.select('.bullet', true).elements, function(el){
22559 el.removeClass('selected');
22562 var bullet = this.el.select('.bullet-' + i, true).first();
22568 bullet.addClass('selected');
22579 Roo.apply(Roo.bootstrap.TabGroup, {
22583 * register a Navigation Group
22584 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22586 register : function(navgrp)
22588 this.groups[navgrp.navId] = navgrp;
22592 * fetch a Navigation Group based on the navigation ID
22593 * if one does not exist , it will get created.
22594 * @param {string} the navgroup to add
22595 * @returns {Roo.bootstrap.nav.Group} the navgroup
22597 get: function(navId) {
22598 if (typeof(this.groups[navId]) == 'undefined') {
22599 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22601 return this.groups[navId] ;
22616 * @class Roo.bootstrap.TabPanel
22617 * @extends Roo.bootstrap.Component
22618 * @children Roo.bootstrap.Component
22619 * Bootstrap TabPanel class
22620 * @cfg {Boolean} active panel active
22621 * @cfg {String} html panel content
22622 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22623 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22624 * @cfg {String} href click to link..
22625 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22629 * Create a new TabPanel
22630 * @param {Object} config The config object
22633 Roo.bootstrap.TabPanel = function(config){
22634 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22638 * Fires when the active status changes
22639 * @param {Roo.bootstrap.TabPanel} this
22640 * @param {Boolean} state the new state
22645 * @event beforedeactivate
22646 * Fires before a tab is de-activated - can be used to do validation on a form.
22647 * @param {Roo.bootstrap.TabPanel} this
22648 * @return {Boolean} false if there is an error
22651 'beforedeactivate': true
22654 this.tabId = this.tabId || Roo.id();
22658 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22665 touchSlide : false,
22666 getAutoCreate : function(){
22671 // item is needed for carousel - not sure if it has any effect otherwise
22672 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22673 html: this.html || ''
22677 cfg.cls += ' active';
22681 cfg.tabId = this.tabId;
22689 initEvents: function()
22691 var p = this.parent();
22693 this.navId = this.navId || p.navId;
22695 if (typeof(this.navId) != 'undefined') {
22696 // not really needed.. but just in case.. parent should be a NavGroup.
22697 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22701 var i = tg.tabs.length - 1;
22703 if(this.active && tg.bullets > 0 && i < tg.bullets){
22704 tg.setActiveBullet(i);
22708 this.el.on('click', this.onClick, this);
22710 if(Roo.isTouch && this.touchSlide){
22711 this.el.on("touchstart", this.onTouchStart, this);
22712 this.el.on("touchmove", this.onTouchMove, this);
22713 this.el.on("touchend", this.onTouchEnd, this);
22718 onRender : function(ct, position)
22720 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22723 setActive : function(state)
22725 Roo.log("panel - set active " + this.tabId + "=" + state);
22727 this.active = state;
22729 this.el.removeClass('active');
22731 } else if (!this.el.hasClass('active')) {
22732 this.el.addClass('active');
22735 this.fireEvent('changed', this, state);
22738 onClick : function(e)
22740 e.preventDefault();
22742 if(!this.href.length){
22746 window.location.href = this.href;
22755 onTouchStart : function(e)
22757 this.swiping = false;
22759 this.startX = e.browserEvent.touches[0].clientX;
22760 this.startY = e.browserEvent.touches[0].clientY;
22763 onTouchMove : function(e)
22765 this.swiping = true;
22767 this.endX = e.browserEvent.touches[0].clientX;
22768 this.endY = e.browserEvent.touches[0].clientY;
22771 onTouchEnd : function(e)
22778 var tabGroup = this.parent();
22780 if(this.endX > this.startX){ // swiping right
22781 tabGroup.showPanelPrev();
22785 if(this.startX > this.endX){ // swiping left
22786 tabGroup.showPanelNext();
22805 * @class Roo.bootstrap.form.DateField
22806 * @extends Roo.bootstrap.form.Input
22807 * Bootstrap DateField class
22808 * @cfg {Number} weekStart default 0
22809 * @cfg {String} viewMode default empty, (months|years)
22810 * @cfg {String} minViewMode default empty, (months|years)
22811 * @cfg {Number} startDate default -Infinity
22812 * @cfg {Number} endDate default Infinity
22813 * @cfg {Boolean} todayHighlight default false
22814 * @cfg {Boolean} todayBtn default false
22815 * @cfg {Boolean} calendarWeeks default false
22816 * @cfg {Object} daysOfWeekDisabled default empty
22817 * @cfg {Boolean} singleMode default false (true | false)
22819 * @cfg {Boolean} keyboardNavigation default true
22820 * @cfg {String} language default en
22823 * Create a new DateField
22824 * @param {Object} config The config object
22827 Roo.bootstrap.form.DateField = function(config){
22828 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22832 * Fires when this field show.
22833 * @param {Roo.bootstrap.form.DateField} this
22834 * @param {Mixed} date The date value
22839 * Fires when this field hide.
22840 * @param {Roo.bootstrap.form.DateField} this
22841 * @param {Mixed} date The date value
22846 * Fires when select a date.
22847 * @param {Roo.bootstrap.form.DateField} this
22848 * @param {Mixed} date The date value
22852 * @event beforeselect
22853 * Fires when before select a date.
22854 * @param {Roo.bootstrap.form.DateField} this
22855 * @param {Mixed} date The date value
22857 beforeselect : true
22861 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22864 * @cfg {String} format
22865 * The default date format string which can be overriden for localization support. The format must be
22866 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22870 * @cfg {String} altFormats
22871 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22872 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22874 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22882 todayHighlight : false,
22888 keyboardNavigation: true,
22890 calendarWeeks: false,
22892 startDate: -Infinity,
22896 daysOfWeekDisabled: [],
22900 singleMode : false,
22902 UTCDate: function()
22904 return new Date(Date.UTC.apply(Date, arguments));
22907 UTCToday: function()
22909 var today = new Date();
22910 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22913 getDate: function() {
22914 var d = this.getUTCDate();
22915 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22918 getUTCDate: function() {
22922 setDate: function(d) {
22923 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22926 setUTCDate: function(d) {
22928 this.setValue(this.formatDate(this.date));
22931 onRender: function(ct, position)
22934 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22936 this.language = this.language || 'en';
22937 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22938 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22940 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22941 this.format = this.format || 'm/d/y';
22942 this.isInline = false;
22943 this.isInput = true;
22944 this.component = this.el.select('.add-on', true).first() || false;
22945 this.component = (this.component && this.component.length === 0) ? false : this.component;
22946 this.hasInput = this.component && this.inputEl().length;
22948 if (typeof(this.minViewMode === 'string')) {
22949 switch (this.minViewMode) {
22951 this.minViewMode = 1;
22954 this.minViewMode = 2;
22957 this.minViewMode = 0;
22962 if (typeof(this.viewMode === 'string')) {
22963 switch (this.viewMode) {
22976 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22978 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22980 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22982 this.picker().on('mousedown', this.onMousedown, this);
22983 this.picker().on('click', this.onClick, this);
22985 this.picker().addClass('datepicker-dropdown');
22987 this.startViewMode = this.viewMode;
22989 if(this.singleMode){
22990 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22991 v.setVisibilityMode(Roo.Element.DISPLAY);
22995 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22996 v.setStyle('width', '189px');
23000 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23001 if(!this.calendarWeeks){
23006 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23007 v.attr('colspan', function(i, val){
23008 return parseInt(val) + 1;
23013 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23015 this.setStartDate(this.startDate);
23016 this.setEndDate(this.endDate);
23018 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23025 if(this.isInline) {
23030 picker : function()
23032 return this.pickerEl;
23033 // return this.el.select('.datepicker', true).first();
23036 fillDow: function()
23038 var dowCnt = this.weekStart;
23047 if(this.calendarWeeks){
23055 while (dowCnt < this.weekStart + 7) {
23059 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23063 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23066 fillMonths: function()
23069 var months = this.picker().select('>.datepicker-months td', true).first();
23071 months.dom.innerHTML = '';
23077 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23080 months.createChild(month);
23087 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;
23089 if (this.date < this.startDate) {
23090 this.viewDate = new Date(this.startDate);
23091 } else if (this.date > this.endDate) {
23092 this.viewDate = new Date(this.endDate);
23094 this.viewDate = new Date(this.date);
23102 var d = new Date(this.viewDate),
23103 year = d.getUTCFullYear(),
23104 month = d.getUTCMonth(),
23105 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23106 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23107 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23108 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23109 currentDate = this.date && this.date.valueOf(),
23110 today = this.UTCToday();
23112 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23114 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23116 // this.picker.select('>tfoot th.today').
23117 // .text(dates[this.language].today)
23118 // .toggle(this.todayBtn !== false);
23120 this.updateNavArrows();
23123 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23125 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23127 prevMonth.setUTCDate(day);
23129 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23131 var nextMonth = new Date(prevMonth);
23133 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23135 nextMonth = nextMonth.valueOf();
23137 var fillMonths = false;
23139 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23141 while(prevMonth.valueOf() <= nextMonth) {
23144 if (prevMonth.getUTCDay() === this.weekStart) {
23146 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23154 if(this.calendarWeeks){
23155 // ISO 8601: First week contains first thursday.
23156 // ISO also states week starts on Monday, but we can be more abstract here.
23158 // Start of current week: based on weekstart/current date
23159 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23160 // Thursday of this week
23161 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23162 // First Thursday of year, year from thursday
23163 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23164 // Calendar week: ms between thursdays, div ms per day, div 7 days
23165 calWeek = (th - yth) / 864e5 / 7 + 1;
23167 fillMonths.cn.push({
23175 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23177 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23180 if (this.todayHighlight &&
23181 prevMonth.getUTCFullYear() == today.getFullYear() &&
23182 prevMonth.getUTCMonth() == today.getMonth() &&
23183 prevMonth.getUTCDate() == today.getDate()) {
23184 clsName += ' today';
23187 if (currentDate && prevMonth.valueOf() === currentDate) {
23188 clsName += ' active';
23191 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23192 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23193 clsName += ' disabled';
23196 fillMonths.cn.push({
23198 cls: 'day ' + clsName,
23199 html: prevMonth.getDate()
23202 prevMonth.setDate(prevMonth.getDate()+1);
23205 var currentYear = this.date && this.date.getUTCFullYear();
23206 var currentMonth = this.date && this.date.getUTCMonth();
23208 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23210 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23211 v.removeClass('active');
23213 if(currentYear === year && k === currentMonth){
23214 v.addClass('active');
23217 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23218 v.addClass('disabled');
23224 year = parseInt(year/10, 10) * 10;
23226 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23228 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23231 for (var i = -1; i < 11; i++) {
23232 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23234 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23242 showMode: function(dir)
23245 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23248 Roo.each(this.picker().select('>div',true).elements, function(v){
23249 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23252 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23257 if(this.isInline) {
23261 this.picker().removeClass(['bottom', 'top']);
23263 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23265 * place to the top of element!
23269 this.picker().addClass('top');
23270 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23275 this.picker().addClass('bottom');
23277 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23280 parseDate : function(value)
23282 if(!value || value instanceof Date){
23285 var v = Date.parseDate(value, this.format);
23286 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23287 v = Date.parseDate(value, 'Y-m-d');
23289 if(!v && this.altFormats){
23290 if(!this.altFormatsArray){
23291 this.altFormatsArray = this.altFormats.split("|");
23293 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23294 v = Date.parseDate(value, this.altFormatsArray[i]);
23300 formatDate : function(date, fmt)
23302 return (!date || !(date instanceof Date)) ?
23303 date : date.dateFormat(fmt || this.format);
23306 onFocus : function()
23308 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23312 onBlur : function()
23314 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23316 var d = this.inputEl().getValue();
23323 showPopup : function()
23325 this.picker().show();
23329 this.fireEvent('showpopup', this, this.date);
23332 hidePopup : function()
23334 if(this.isInline) {
23337 this.picker().hide();
23338 this.viewMode = this.startViewMode;
23341 this.fireEvent('hidepopup', this, this.date);
23345 onMousedown: function(e)
23347 e.stopPropagation();
23348 e.preventDefault();
23353 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23357 setValue: function(v)
23359 if(this.fireEvent('beforeselect', this, v) !== false){
23360 var d = new Date(this.parseDate(v) ).clearTime();
23362 if(isNaN(d.getTime())){
23363 this.date = this.viewDate = '';
23364 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23368 v = this.formatDate(d);
23370 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23372 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23376 this.fireEvent('select', this, this.date);
23380 getValue: function()
23382 return this.formatDate(this.date);
23385 fireKey: function(e)
23387 if (!this.picker().isVisible()){
23388 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23394 var dateChanged = false,
23396 newDate, newViewDate;
23401 e.preventDefault();
23405 if (!this.keyboardNavigation) {
23408 dir = e.keyCode == 37 ? -1 : 1;
23411 newDate = this.moveYear(this.date, dir);
23412 newViewDate = this.moveYear(this.viewDate, dir);
23413 } else if (e.shiftKey){
23414 newDate = this.moveMonth(this.date, dir);
23415 newViewDate = this.moveMonth(this.viewDate, dir);
23417 newDate = new Date(this.date);
23418 newDate.setUTCDate(this.date.getUTCDate() + dir);
23419 newViewDate = new Date(this.viewDate);
23420 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23422 if (this.dateWithinRange(newDate)){
23423 this.date = newDate;
23424 this.viewDate = newViewDate;
23425 this.setValue(this.formatDate(this.date));
23427 e.preventDefault();
23428 dateChanged = true;
23433 if (!this.keyboardNavigation) {
23436 dir = e.keyCode == 38 ? -1 : 1;
23438 newDate = this.moveYear(this.date, dir);
23439 newViewDate = this.moveYear(this.viewDate, dir);
23440 } else if (e.shiftKey){
23441 newDate = this.moveMonth(this.date, dir);
23442 newViewDate = this.moveMonth(this.viewDate, dir);
23444 newDate = new Date(this.date);
23445 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23446 newViewDate = new Date(this.viewDate);
23447 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23449 if (this.dateWithinRange(newDate)){
23450 this.date = newDate;
23451 this.viewDate = newViewDate;
23452 this.setValue(this.formatDate(this.date));
23454 e.preventDefault();
23455 dateChanged = true;
23459 this.setValue(this.formatDate(this.date));
23461 e.preventDefault();
23464 this.setValue(this.formatDate(this.date));
23478 onClick: function(e)
23480 e.stopPropagation();
23481 e.preventDefault();
23483 var target = e.getTarget();
23485 if(target.nodeName.toLowerCase() === 'i'){
23486 target = Roo.get(target).dom.parentNode;
23489 var nodeName = target.nodeName;
23490 var className = target.className;
23491 var html = target.innerHTML;
23492 //Roo.log(nodeName);
23494 switch(nodeName.toLowerCase()) {
23496 switch(className) {
23502 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23503 switch(this.viewMode){
23505 this.viewDate = this.moveMonth(this.viewDate, dir);
23509 this.viewDate = this.moveYear(this.viewDate, dir);
23515 var date = new Date();
23516 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23518 this.setValue(this.formatDate(this.date));
23525 if (className.indexOf('disabled') < 0) {
23526 if (!this.viewDate) {
23527 this.viewDate = new Date();
23529 this.viewDate.setUTCDate(1);
23530 if (className.indexOf('month') > -1) {
23531 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23533 var year = parseInt(html, 10) || 0;
23534 this.viewDate.setUTCFullYear(year);
23538 if(this.singleMode){
23539 this.setValue(this.formatDate(this.viewDate));
23550 //Roo.log(className);
23551 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23552 var day = parseInt(html, 10) || 1;
23553 var year = (this.viewDate || new Date()).getUTCFullYear(),
23554 month = (this.viewDate || new Date()).getUTCMonth();
23556 if (className.indexOf('old') > -1) {
23563 } else if (className.indexOf('new') > -1) {
23571 //Roo.log([year,month,day]);
23572 this.date = this.UTCDate(year, month, day,0,0,0,0);
23573 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23575 //Roo.log(this.formatDate(this.date));
23576 this.setValue(this.formatDate(this.date));
23583 setStartDate: function(startDate)
23585 this.startDate = startDate || -Infinity;
23586 if (this.startDate !== -Infinity) {
23587 this.startDate = this.parseDate(this.startDate);
23590 this.updateNavArrows();
23593 setEndDate: function(endDate)
23595 this.endDate = endDate || Infinity;
23596 if (this.endDate !== Infinity) {
23597 this.endDate = this.parseDate(this.endDate);
23600 this.updateNavArrows();
23603 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23605 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23606 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23607 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23609 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23610 return parseInt(d, 10);
23613 this.updateNavArrows();
23616 updateNavArrows: function()
23618 if(this.singleMode){
23622 var d = new Date(this.viewDate),
23623 year = d.getUTCFullYear(),
23624 month = d.getUTCMonth();
23626 Roo.each(this.picker().select('.prev', true).elements, function(v){
23628 switch (this.viewMode) {
23631 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23637 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23644 Roo.each(this.picker().select('.next', true).elements, function(v){
23646 switch (this.viewMode) {
23649 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23655 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23663 moveMonth: function(date, dir)
23668 var new_date = new Date(date.valueOf()),
23669 day = new_date.getUTCDate(),
23670 month = new_date.getUTCMonth(),
23671 mag = Math.abs(dir),
23673 dir = dir > 0 ? 1 : -1;
23676 // If going back one month, make sure month is not current month
23677 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23679 return new_date.getUTCMonth() == month;
23681 // If going forward one month, make sure month is as expected
23682 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23684 return new_date.getUTCMonth() != new_month;
23686 new_month = month + dir;
23687 new_date.setUTCMonth(new_month);
23688 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23689 if (new_month < 0 || new_month > 11) {
23690 new_month = (new_month + 12) % 12;
23693 // For magnitudes >1, move one month at a time...
23694 for (var i=0; i<mag; i++) {
23695 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23696 new_date = this.moveMonth(new_date, dir);
23698 // ...then reset the day, keeping it in the new month
23699 new_month = new_date.getUTCMonth();
23700 new_date.setUTCDate(day);
23702 return new_month != new_date.getUTCMonth();
23705 // Common date-resetting loop -- if date is beyond end of month, make it
23708 new_date.setUTCDate(--day);
23709 new_date.setUTCMonth(new_month);
23714 moveYear: function(date, dir)
23716 return this.moveMonth(date, dir*12);
23719 dateWithinRange: function(date)
23721 return date >= this.startDate && date <= this.endDate;
23727 this.picker().remove();
23730 validateValue : function(value)
23732 if(this.getVisibilityEl().hasClass('hidden')){
23736 if(value.length < 1) {
23737 if(this.allowBlank){
23743 if(value.length < this.minLength){
23746 if(value.length > this.maxLength){
23750 var vt = Roo.form.VTypes;
23751 if(!vt[this.vtype](value, this)){
23755 if(typeof this.validator == "function"){
23756 var msg = this.validator(value);
23762 if(this.regex && !this.regex.test(value)){
23766 if(typeof(this.parseDate(value)) == 'undefined'){
23770 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23774 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23784 this.date = this.viewDate = '';
23786 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23791 Roo.apply(Roo.bootstrap.form.DateField, {
23802 html: '<i class="fa fa-arrow-left"/>'
23812 html: '<i class="fa fa-arrow-right"/>'
23854 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23855 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23856 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23857 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23858 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23871 navFnc: 'FullYear',
23876 navFnc: 'FullYear',
23881 Roo.apply(Roo.bootstrap.form.DateField, {
23885 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23889 cls: 'datepicker-days',
23893 cls: 'table-condensed',
23895 Roo.bootstrap.form.DateField.head,
23899 Roo.bootstrap.form.DateField.footer
23906 cls: 'datepicker-months',
23910 cls: 'table-condensed',
23912 Roo.bootstrap.form.DateField.head,
23913 Roo.bootstrap.form.DateField.content,
23914 Roo.bootstrap.form.DateField.footer
23921 cls: 'datepicker-years',
23925 cls: 'table-condensed',
23927 Roo.bootstrap.form.DateField.head,
23928 Roo.bootstrap.form.DateField.content,
23929 Roo.bootstrap.form.DateField.footer
23948 * @class Roo.bootstrap.form.TimeField
23949 * @extends Roo.bootstrap.form.Input
23950 * Bootstrap DateField class
23954 * Create a new TimeField
23955 * @param {Object} config The config object
23958 Roo.bootstrap.form.TimeField = function(config){
23959 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23963 * Fires when this field show.
23964 * @param {Roo.bootstrap.form.DateField} thisthis
23965 * @param {Mixed} date The date value
23970 * Fires when this field hide.
23971 * @param {Roo.bootstrap.form.DateField} this
23972 * @param {Mixed} date The date value
23977 * Fires when select a date.
23978 * @param {Roo.bootstrap.form.DateField} this
23979 * @param {Mixed} date The date value
23985 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23988 * @cfg {String} format
23989 * The default time format string which can be overriden for localization support. The format must be
23990 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23994 getAutoCreate : function()
23996 this.after = '<i class="fa far fa-clock"></i>';
23997 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24001 onRender: function(ct, position)
24004 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24006 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24008 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24010 this.pop = this.picker().select('>.datepicker-time',true).first();
24011 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24013 this.picker().on('mousedown', this.onMousedown, this);
24014 this.picker().on('click', this.onClick, this);
24016 this.picker().addClass('datepicker-dropdown');
24021 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24022 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24023 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24024 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24025 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24026 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24030 fireKey: function(e){
24031 if (!this.picker().isVisible()){
24032 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24038 e.preventDefault();
24046 this.onTogglePeriod();
24049 this.onIncrementMinutes();
24052 this.onDecrementMinutes();
24061 onClick: function(e) {
24062 e.stopPropagation();
24063 e.preventDefault();
24066 picker : function()
24068 return this.pickerEl;
24071 fillTime: function()
24073 var time = this.pop.select('tbody', true).first();
24075 time.dom.innerHTML = '';
24090 cls: 'hours-up fa fas fa-chevron-up'
24110 cls: 'minutes-up fa fas fa-chevron-up'
24131 cls: 'timepicker-hour',
24146 cls: 'timepicker-minute',
24161 cls: 'btn btn-primary period',
24183 cls: 'hours-down fa fas fa-chevron-down'
24203 cls: 'minutes-down fa fas fa-chevron-down'
24221 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24228 var hours = this.time.getHours();
24229 var minutes = this.time.getMinutes();
24242 hours = hours - 12;
24246 hours = '0' + hours;
24250 minutes = '0' + minutes;
24253 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24254 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24255 this.pop.select('button', true).first().dom.innerHTML = period;
24261 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24263 var cls = ['bottom'];
24265 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24272 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24276 //this.picker().setXY(20000,20000);
24277 this.picker().addClass(cls.join('-'));
24281 Roo.each(cls, function(c){
24286 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24287 //_this.picker().setTop(_this.inputEl().getHeight());
24291 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24293 //_this.picker().setTop(0 - _this.picker().getHeight());
24298 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24302 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24310 onFocus : function()
24312 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24316 onBlur : function()
24318 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24324 this.picker().show();
24329 this.fireEvent('show', this, this.date);
24334 this.picker().hide();
24337 this.fireEvent('hide', this, this.date);
24340 setTime : function()
24343 this.setValue(this.time.format(this.format));
24345 this.fireEvent('select', this, this.date);
24350 onMousedown: function(e){
24351 e.stopPropagation();
24352 e.preventDefault();
24355 onIncrementHours: function()
24357 Roo.log('onIncrementHours');
24358 this.time = this.time.add(Date.HOUR, 1);
24363 onDecrementHours: function()
24365 Roo.log('onDecrementHours');
24366 this.time = this.time.add(Date.HOUR, -1);
24370 onIncrementMinutes: function()
24372 Roo.log('onIncrementMinutes');
24373 this.time = this.time.add(Date.MINUTE, 1);
24377 onDecrementMinutes: function()
24379 Roo.log('onDecrementMinutes');
24380 this.time = this.time.add(Date.MINUTE, -1);
24384 onTogglePeriod: function()
24386 Roo.log('onTogglePeriod');
24387 this.time = this.time.add(Date.HOUR, 12);
24395 Roo.apply(Roo.bootstrap.form.TimeField, {
24399 cls: 'datepicker dropdown-menu',
24403 cls: 'datepicker-time',
24407 cls: 'table-condensed',
24436 cls: 'btn btn-info ok',
24464 * @class Roo.bootstrap.form.MonthField
24465 * @extends Roo.bootstrap.form.Input
24466 * Bootstrap MonthField class
24468 * @cfg {String} language default en
24471 * Create a new MonthField
24472 * @param {Object} config The config object
24475 Roo.bootstrap.form.MonthField = function(config){
24476 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24481 * Fires when this field show.
24482 * @param {Roo.bootstrap.form.MonthField} this
24483 * @param {Mixed} date The date value
24488 * Fires when this field hide.
24489 * @param {Roo.bootstrap.form.MonthField} this
24490 * @param {Mixed} date The date value
24495 * Fires when select a date.
24496 * @param {Roo.bootstrap.form.MonthField} this
24497 * @param {String} oldvalue The old value
24498 * @param {String} newvalue The new value
24504 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24506 onRender: function(ct, position)
24509 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24511 this.language = this.language || 'en';
24512 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24513 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24515 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24516 this.isInline = false;
24517 this.isInput = true;
24518 this.component = this.el.select('.add-on', true).first() || false;
24519 this.component = (this.component && this.component.length === 0) ? false : this.component;
24520 this.hasInput = this.component && this.inputEL().length;
24522 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24524 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24526 this.picker().on('mousedown', this.onMousedown, this);
24527 this.picker().on('click', this.onClick, this);
24529 this.picker().addClass('datepicker-dropdown');
24531 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24532 v.setStyle('width', '189px');
24539 if(this.isInline) {
24545 setValue: function(v, suppressEvent)
24547 var o = this.getValue();
24549 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24553 if(suppressEvent !== true){
24554 this.fireEvent('select', this, o, v);
24559 getValue: function()
24564 onClick: function(e)
24566 e.stopPropagation();
24567 e.preventDefault();
24569 var target = e.getTarget();
24571 if(target.nodeName.toLowerCase() === 'i'){
24572 target = Roo.get(target).dom.parentNode;
24575 var nodeName = target.nodeName;
24576 var className = target.className;
24577 var html = target.innerHTML;
24579 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24583 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24585 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24591 picker : function()
24593 return this.pickerEl;
24596 fillMonths: function()
24599 var months = this.picker().select('>.datepicker-months td', true).first();
24601 months.dom.innerHTML = '';
24607 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24610 months.createChild(month);
24619 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24620 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24623 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24624 e.removeClass('active');
24626 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24627 e.addClass('active');
24634 if(this.isInline) {
24638 this.picker().removeClass(['bottom', 'top']);
24640 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24642 * place to the top of element!
24646 this.picker().addClass('top');
24647 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24652 this.picker().addClass('bottom');
24654 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24657 onFocus : function()
24659 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24663 onBlur : function()
24665 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24667 var d = this.inputEl().getValue();
24676 this.picker().show();
24677 this.picker().select('>.datepicker-months', true).first().show();
24681 this.fireEvent('show', this, this.date);
24686 if(this.isInline) {
24689 this.picker().hide();
24690 this.fireEvent('hide', this, this.date);
24694 onMousedown: function(e)
24696 e.stopPropagation();
24697 e.preventDefault();
24702 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24706 fireKey: function(e)
24708 if (!this.picker().isVisible()){
24709 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24720 e.preventDefault();
24724 dir = e.keyCode == 37 ? -1 : 1;
24726 this.vIndex = this.vIndex + dir;
24728 if(this.vIndex < 0){
24732 if(this.vIndex > 11){
24736 if(isNaN(this.vIndex)){
24740 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24746 dir = e.keyCode == 38 ? -1 : 1;
24748 this.vIndex = this.vIndex + dir * 4;
24750 if(this.vIndex < 0){
24754 if(this.vIndex > 11){
24758 if(isNaN(this.vIndex)){
24762 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24767 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24768 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24772 e.preventDefault();
24775 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24776 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24792 this.picker().remove();
24797 Roo.apply(Roo.bootstrap.form.MonthField, {
24816 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24817 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24822 Roo.apply(Roo.bootstrap.form.MonthField, {
24826 cls: 'datepicker dropdown-menu roo-dynamic',
24830 cls: 'datepicker-months',
24834 cls: 'table-condensed',
24836 Roo.bootstrap.form.DateField.content
24856 * @class Roo.bootstrap.form.CheckBox
24857 * @extends Roo.bootstrap.form.Input
24858 * Bootstrap CheckBox class
24860 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24861 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24862 * @cfg {String} boxLabel The text that appears beside the checkbox
24863 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24864 * @cfg {Boolean} checked initnal the element
24865 * @cfg {Boolean} inline inline the element (default false)
24866 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24867 * @cfg {String} tooltip label tooltip
24870 * Create a new CheckBox
24871 * @param {Object} config The config object
24874 Roo.bootstrap.form.CheckBox = function(config){
24875 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24880 * Fires when the element is checked or unchecked.
24881 * @param {Roo.bootstrap.form.CheckBox} this This input
24882 * @param {Boolean} checked The new checked value
24887 * Fires when the element is click.
24888 * @param {Roo.bootstrap.form.CheckBox} this This input
24895 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24897 inputType: 'checkbox',
24906 // checkbox success does not make any sense really..
24911 getAutoCreate : function()
24913 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24919 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24922 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24928 type : this.inputType,
24929 value : this.inputValue,
24930 cls : 'roo-' + this.inputType, //'form-box',
24931 placeholder : this.placeholder || ''
24935 if(this.inputType != 'radio'){
24939 cls : 'roo-hidden-value',
24940 value : this.checked ? this.inputValue : this.valueOff
24945 if (this.weight) { // Validity check?
24946 cfg.cls += " " + this.inputType + "-" + this.weight;
24949 if (this.disabled) {
24950 input.disabled=true;
24954 input.checked = this.checked;
24959 input.name = this.name;
24961 if(this.inputType != 'radio'){
24962 hidden.name = this.name;
24963 input.name = '_hidden_' + this.name;
24968 input.cls += ' input-' + this.size;
24973 ['xs','sm','md','lg'].map(function(size){
24974 if (settings[size]) {
24975 cfg.cls += ' col-' + size + '-' + settings[size];
24979 var inputblock = input;
24981 if (this.before || this.after) {
24984 cls : 'input-group',
24989 inputblock.cn.push({
24991 cls : 'input-group-addon',
24996 inputblock.cn.push(input);
24998 if(this.inputType != 'radio'){
24999 inputblock.cn.push(hidden);
25003 inputblock.cn.push({
25005 cls : 'input-group-addon',
25011 var boxLabelCfg = false;
25017 //'for': id, // box label is handled by onclick - so no for...
25019 html: this.boxLabel
25022 boxLabelCfg.tooltip = this.tooltip;
25028 if (align ==='left' && this.fieldLabel.length) {
25029 // Roo.log("left and has label");
25034 cls : 'control-label',
25035 html : this.fieldLabel
25046 cfg.cn[1].cn.push(boxLabelCfg);
25049 if(this.labelWidth > 12){
25050 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25053 if(this.labelWidth < 13 && this.labelmd == 0){
25054 this.labelmd = this.labelWidth;
25057 if(this.labellg > 0){
25058 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25059 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25062 if(this.labelmd > 0){
25063 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25064 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25067 if(this.labelsm > 0){
25068 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25069 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25072 if(this.labelxs > 0){
25073 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25074 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25077 } else if ( this.fieldLabel.length) {
25078 // Roo.log(" label");
25082 tag: this.boxLabel ? 'span' : 'label',
25084 cls: 'control-label box-input-label',
25085 //cls : 'input-group-addon',
25086 html : this.fieldLabel
25093 cfg.cn.push(boxLabelCfg);
25098 // Roo.log(" no label && no align");
25099 cfg.cn = [ inputblock ] ;
25101 cfg.cn.push(boxLabelCfg);
25109 if(this.inputType != 'radio'){
25110 cfg.cn.push(hidden);
25118 * return the real input element.
25120 inputEl: function ()
25122 return this.el.select('input.roo-' + this.inputType,true).first();
25124 hiddenEl: function ()
25126 return this.el.select('input.roo-hidden-value',true).first();
25129 labelEl: function()
25131 return this.el.select('label.control-label',true).first();
25133 /* depricated... */
25137 return this.labelEl();
25140 boxLabelEl: function()
25142 return this.el.select('label.box-label',true).first();
25145 initEvents : function()
25147 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25149 this.inputEl().on('click', this.onClick, this);
25151 if (this.boxLabel) {
25152 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25155 this.startValue = this.getValue();
25158 Roo.bootstrap.form.CheckBox.register(this);
25162 onClick : function(e)
25164 if(this.fireEvent('click', this, e) !== false){
25165 this.setChecked(!this.checked);
25170 setChecked : function(state,suppressEvent)
25172 this.startValue = this.getValue();
25174 if(this.inputType == 'radio'){
25176 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25177 e.dom.checked = false;
25180 this.inputEl().dom.checked = true;
25182 this.inputEl().dom.value = this.inputValue;
25184 if(suppressEvent !== true){
25185 this.fireEvent('check', this, true);
25193 this.checked = state;
25195 this.inputEl().dom.checked = state;
25198 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25200 if(suppressEvent !== true){
25201 this.fireEvent('check', this, state);
25207 getValue : function()
25209 if(this.inputType == 'radio'){
25210 return this.getGroupValue();
25213 return this.hiddenEl().dom.value;
25217 getGroupValue : function()
25219 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25223 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25226 setValue : function(v,suppressEvent)
25228 if(this.inputType == 'radio'){
25229 this.setGroupValue(v, suppressEvent);
25233 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25238 setGroupValue : function(v, suppressEvent)
25240 this.startValue = this.getValue();
25242 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25243 e.dom.checked = false;
25245 if(e.dom.value == v){
25246 e.dom.checked = true;
25250 if(suppressEvent !== true){
25251 this.fireEvent('check', this, true);
25259 validate : function()
25261 if(this.getVisibilityEl().hasClass('hidden')){
25267 (this.inputType == 'radio' && this.validateRadio()) ||
25268 (this.inputType == 'checkbox' && this.validateCheckbox())
25274 this.markInvalid();
25278 validateRadio : function()
25280 if(this.getVisibilityEl().hasClass('hidden')){
25284 if(this.allowBlank){
25290 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25291 if(!e.dom.checked){
25303 validateCheckbox : function()
25306 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25307 //return (this.getValue() == this.inputValue) ? true : false;
25310 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25318 for(var i in group){
25319 if(group[i].el.isVisible(true)){
25327 for(var i in group){
25332 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25339 * Mark this field as valid
25341 markValid : function()
25345 this.fireEvent('valid', this);
25347 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25350 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25357 if(this.inputType == 'radio'){
25358 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25359 var fg = e.findParent('.form-group', false, true);
25360 if (Roo.bootstrap.version == 3) {
25361 fg.removeClass([_this.invalidClass, _this.validClass]);
25362 fg.addClass(_this.validClass);
25364 fg.removeClass(['is-valid', 'is-invalid']);
25365 fg.addClass('is-valid');
25373 var fg = this.el.findParent('.form-group', false, true);
25374 if (Roo.bootstrap.version == 3) {
25375 fg.removeClass([this.invalidClass, this.validClass]);
25376 fg.addClass(this.validClass);
25378 fg.removeClass(['is-valid', 'is-invalid']);
25379 fg.addClass('is-valid');
25384 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25390 for(var i in group){
25391 var fg = group[i].el.findParent('.form-group', false, true);
25392 if (Roo.bootstrap.version == 3) {
25393 fg.removeClass([this.invalidClass, this.validClass]);
25394 fg.addClass(this.validClass);
25396 fg.removeClass(['is-valid', 'is-invalid']);
25397 fg.addClass('is-valid');
25403 * Mark this field as invalid
25404 * @param {String} msg The validation message
25406 markInvalid : function(msg)
25408 if(this.allowBlank){
25414 this.fireEvent('invalid', this, msg);
25416 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25419 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25423 label.markInvalid();
25426 if(this.inputType == 'radio'){
25428 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25429 var fg = e.findParent('.form-group', false, true);
25430 if (Roo.bootstrap.version == 3) {
25431 fg.removeClass([_this.invalidClass, _this.validClass]);
25432 fg.addClass(_this.invalidClass);
25434 fg.removeClass(['is-invalid', 'is-valid']);
25435 fg.addClass('is-invalid');
25443 var fg = this.el.findParent('.form-group', false, true);
25444 if (Roo.bootstrap.version == 3) {
25445 fg.removeClass([_this.invalidClass, _this.validClass]);
25446 fg.addClass(_this.invalidClass);
25448 fg.removeClass(['is-invalid', 'is-valid']);
25449 fg.addClass('is-invalid');
25454 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25460 for(var i in group){
25461 var fg = group[i].el.findParent('.form-group', false, true);
25462 if (Roo.bootstrap.version == 3) {
25463 fg.removeClass([_this.invalidClass, _this.validClass]);
25464 fg.addClass(_this.invalidClass);
25466 fg.removeClass(['is-invalid', 'is-valid']);
25467 fg.addClass('is-invalid');
25473 clearInvalid : function()
25475 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25477 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25479 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25481 if (label && label.iconEl) {
25482 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25483 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25487 disable : function()
25489 if(this.inputType != 'radio'){
25490 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25497 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25498 _this.getActionEl().addClass(this.disabledClass);
25499 e.dom.disabled = true;
25503 this.disabled = true;
25504 this.fireEvent("disable", this);
25508 enable : function()
25510 if(this.inputType != 'radio'){
25511 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25518 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25519 _this.getActionEl().removeClass(this.disabledClass);
25520 e.dom.disabled = false;
25524 this.disabled = false;
25525 this.fireEvent("enable", this);
25529 setBoxLabel : function(v)
25534 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25540 Roo.apply(Roo.bootstrap.form.CheckBox, {
25545 * register a CheckBox Group
25546 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25548 register : function(checkbox)
25550 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25551 this.groups[checkbox.groupId] = {};
25554 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25558 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25562 * fetch a CheckBox Group based on the group ID
25563 * @param {string} the group ID
25564 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25566 get: function(groupId) {
25567 if (typeof(this.groups[groupId]) == 'undefined') {
25571 return this.groups[groupId] ;
25584 * @class Roo.bootstrap.form.Radio
25585 * @extends Roo.bootstrap.Component
25586 * Bootstrap Radio class
25587 * @cfg {String} boxLabel - the label associated
25588 * @cfg {String} value - the value of radio
25591 * Create a new Radio
25592 * @param {Object} config The config object
25594 Roo.bootstrap.form.Radio = function(config){
25595 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25599 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25605 getAutoCreate : function()
25609 cls : 'form-group radio',
25614 html : this.boxLabel
25622 initEvents : function()
25624 this.parent().register(this);
25626 this.el.on('click', this.onClick, this);
25630 onClick : function(e)
25632 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25633 this.setChecked(true);
25637 setChecked : function(state, suppressEvent)
25639 this.parent().setValue(this.value, suppressEvent);
25643 setBoxLabel : function(v)
25648 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25663 * @class Roo.bootstrap.form.SecurePass
25664 * @extends Roo.bootstrap.form.Input
25665 * Bootstrap SecurePass class
25669 * Create a new SecurePass
25670 * @param {Object} config The config object
25673 Roo.bootstrap.form.SecurePass = function (config) {
25674 // these go here, so the translation tool can replace them..
25676 PwdEmpty: "Please type a password, and then retype it to confirm.",
25677 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25678 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25679 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25680 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25681 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25682 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25683 TooWeak: "Your password is Too Weak."
25685 this.meterLabel = "Password strength:";
25686 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25687 this.meterClass = [
25688 "roo-password-meter-tooweak",
25689 "roo-password-meter-weak",
25690 "roo-password-meter-medium",
25691 "roo-password-meter-strong",
25692 "roo-password-meter-grey"
25697 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25700 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25702 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25704 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25705 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25706 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25707 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25708 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25709 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25710 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25720 * @cfg {String/Object} Label for the strength meter (defaults to
25721 * 'Password strength:')
25726 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25727 * ['Weak', 'Medium', 'Strong'])
25730 pwdStrengths: false,
25743 initEvents: function ()
25745 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25747 if (this.el.is('input[type=password]') && Roo.isSafari) {
25748 this.el.on('keydown', this.SafariOnKeyDown, this);
25751 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25754 onRender: function (ct, position)
25756 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25757 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25758 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25760 this.trigger.createChild({
25765 cls: 'roo-password-meter-grey col-xs-12',
25768 //width: this.meterWidth + 'px'
25772 cls: 'roo-password-meter-text'
25778 if (this.hideTrigger) {
25779 this.trigger.setDisplayed(false);
25781 this.setSize(this.width || '', this.height || '');
25784 onDestroy: function ()
25786 if (this.trigger) {
25787 this.trigger.removeAllListeners();
25788 this.trigger.remove();
25791 this.wrap.remove();
25793 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25796 checkStrength: function ()
25798 var pwd = this.inputEl().getValue();
25799 if (pwd == this._lastPwd) {
25804 if (this.ClientSideStrongPassword(pwd)) {
25806 } else if (this.ClientSideMediumPassword(pwd)) {
25808 } else if (this.ClientSideWeakPassword(pwd)) {
25814 Roo.log('strength1: ' + strength);
25816 //var pm = this.trigger.child('div/div/div').dom;
25817 var pm = this.trigger.child('div/div');
25818 pm.removeClass(this.meterClass);
25819 pm.addClass(this.meterClass[strength]);
25822 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25824 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25826 this._lastPwd = pwd;
25830 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25832 this._lastPwd = '';
25834 var pm = this.trigger.child('div/div');
25835 pm.removeClass(this.meterClass);
25836 pm.addClass('roo-password-meter-grey');
25839 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25842 this.inputEl().dom.type='password';
25845 validateValue: function (value)
25847 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25850 if (value.length == 0) {
25851 if (this.allowBlank) {
25852 this.clearInvalid();
25856 this.markInvalid(this.errors.PwdEmpty);
25857 this.errorMsg = this.errors.PwdEmpty;
25865 if (!value.match(/[\x21-\x7e]+/)) {
25866 this.markInvalid(this.errors.PwdBadChar);
25867 this.errorMsg = this.errors.PwdBadChar;
25870 if (value.length < 6) {
25871 this.markInvalid(this.errors.PwdShort);
25872 this.errorMsg = this.errors.PwdShort;
25875 if (value.length > 16) {
25876 this.markInvalid(this.errors.PwdLong);
25877 this.errorMsg = this.errors.PwdLong;
25881 if (this.ClientSideStrongPassword(value)) {
25883 } else if (this.ClientSideMediumPassword(value)) {
25885 } else if (this.ClientSideWeakPassword(value)) {
25892 if (strength < 2) {
25893 //this.markInvalid(this.errors.TooWeak);
25894 this.errorMsg = this.errors.TooWeak;
25899 console.log('strength2: ' + strength);
25901 //var pm = this.trigger.child('div/div/div').dom;
25903 var pm = this.trigger.child('div/div');
25904 pm.removeClass(this.meterClass);
25905 pm.addClass(this.meterClass[strength]);
25907 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25909 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25911 this.errorMsg = '';
25915 CharacterSetChecks: function (type)
25918 this.fResult = false;
25921 isctype: function (character, type)
25924 case this.kCapitalLetter:
25925 if (character >= 'A' && character <= 'Z') {
25930 case this.kSmallLetter:
25931 if (character >= 'a' && character <= 'z') {
25937 if (character >= '0' && character <= '9') {
25942 case this.kPunctuation:
25943 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25954 IsLongEnough: function (pwd, size)
25956 return !(pwd == null || isNaN(size) || pwd.length < size);
25959 SpansEnoughCharacterSets: function (word, nb)
25961 if (!this.IsLongEnough(word, nb))
25966 var characterSetChecks = new Array(
25967 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25968 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25971 for (var index = 0; index < word.length; ++index) {
25972 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25973 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25974 characterSetChecks[nCharSet].fResult = true;
25981 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25982 if (characterSetChecks[nCharSet].fResult) {
25987 if (nCharSets < nb) {
25993 ClientSideStrongPassword: function (pwd)
25995 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25998 ClientSideMediumPassword: function (pwd)
26000 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26003 ClientSideWeakPassword: function (pwd)
26005 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26009 Roo.htmleditor = {};
26012 * @class Roo.htmleditor.Filter
26013 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26014 * @cfg {DomElement} node The node to iterate and filter
26015 * @cfg {boolean|String|Array} tag Tags to replace
26017 * Create a new Filter.
26018 * @param {Object} config Configuration options
26023 Roo.htmleditor.Filter = function(cfg) {
26024 Roo.apply(this.cfg);
26025 // this does not actually call walk as it's really just a abstract class
26029 Roo.htmleditor.Filter.prototype = {
26035 // overrride to do replace comments.
26036 replaceComment : false,
26038 // overrride to do replace or do stuff with tags..
26039 replaceTag : false,
26041 walk : function(dom)
26043 Roo.each( Array.from(dom.childNodes), function( e ) {
26046 case e.nodeType == 8 && this.replaceComment !== false: // comment
26047 this.replaceComment(e);
26050 case e.nodeType != 1: //not a node.
26053 case this.tag === true: // everything
26054 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26055 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26056 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26057 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26058 if (this.replaceTag && false === this.replaceTag(e)) {
26061 if (e.hasChildNodes()) {
26066 default: // tags .. that do not match.
26067 if (e.hasChildNodes()) {
26077 removeNodeKeepChildren : function( node)
26080 ar = Array.from(node.childNodes);
26081 for (var i = 0; i < ar.length; i++) {
26083 node.removeChild(ar[i]);
26084 // what if we need to walk these???
26085 node.parentNode.insertBefore(ar[i], node);
26088 node.parentNode.removeChild(node);
26093 * @class Roo.htmleditor.FilterAttributes
26094 * clean attributes and styles including http:// etc.. in attribute
26096 * Run a new Attribute Filter
26097 * @param {Object} config Configuration options
26099 Roo.htmleditor.FilterAttributes = function(cfg)
26101 Roo.apply(this, cfg);
26102 this.attrib_black = this.attrib_black || [];
26103 this.attrib_white = this.attrib_white || [];
26105 this.attrib_clean = this.attrib_clean || [];
26106 this.style_white = this.style_white || [];
26107 this.style_black = this.style_black || [];
26108 this.walk(cfg.node);
26111 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26113 tag: true, // all tags
26115 attrib_black : false, // array
26116 attrib_clean : false,
26117 attrib_white : false,
26119 style_white : false,
26120 style_black : false,
26123 replaceTag : function(node)
26125 if (!node.attributes || !node.attributes.length) {
26129 for (var i = node.attributes.length-1; i > -1 ; i--) {
26130 var a = node.attributes[i];
26132 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26133 node.removeAttribute(a.name);
26139 if (a.name.toLowerCase().substr(0,2)=='on') {
26140 node.removeAttribute(a.name);
26145 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26146 node.removeAttribute(a.name);
26149 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26150 this.cleanAttr(node,a.name,a.value); // fixme..
26153 if (a.name == 'style') {
26154 this.cleanStyle(node,a.name,a.value);
26157 /// clean up MS crap..
26158 // tecnically this should be a list of valid class'es..
26161 if (a.name == 'class') {
26162 if (a.value.match(/^Mso/)) {
26163 node.removeAttribute('class');
26166 if (a.value.match(/^body$/)) {
26167 node.removeAttribute('class');
26177 return true; // clean children
26180 cleanAttr: function(node, n,v)
26183 if (v.match(/^\./) || v.match(/^\//)) {
26186 if (v.match(/^(http|https):\/\//)
26187 || v.match(/^mailto:/)
26188 || v.match(/^ftp:/)
26189 || v.match(/^data:/)
26193 if (v.match(/^#/)) {
26196 if (v.match(/^\{/)) { // allow template editing.
26199 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26200 node.removeAttribute(n);
26203 cleanStyle : function(node, n,v)
26205 if (v.match(/expression/)) { //XSS?? should we even bother..
26206 node.removeAttribute(n);
26210 var parts = v.split(/;/);
26213 Roo.each(parts, function(p) {
26214 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26218 var l = p.split(':').shift().replace(/\s+/g,'');
26219 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26221 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26225 // only allow 'c whitelisted system attributes'
26226 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26234 if (clean.length) {
26235 node.setAttribute(n, clean.join(';'));
26237 node.removeAttribute(n);
26246 * @class Roo.htmleditor.FilterBlack
26247 * remove blacklisted elements.
26249 * Run a new Blacklisted Filter
26250 * @param {Object} config Configuration options
26253 Roo.htmleditor.FilterBlack = function(cfg)
26255 Roo.apply(this, cfg);
26256 this.walk(cfg.node);
26259 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26261 tag : true, // all elements.
26263 replaceTag : function(n)
26265 n.parentNode.removeChild(n);
26269 * @class Roo.htmleditor.FilterComment
26272 * Run a new Comments Filter
26273 * @param {Object} config Configuration options
26275 Roo.htmleditor.FilterComment = function(cfg)
26277 this.walk(cfg.node);
26280 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26283 replaceComment : function(n)
26285 n.parentNode.removeChild(n);
26288 * @class Roo.htmleditor.FilterKeepChildren
26289 * remove tags but keep children
26291 * Run a new Keep Children Filter
26292 * @param {Object} config Configuration options
26295 Roo.htmleditor.FilterKeepChildren = function(cfg)
26297 Roo.apply(this, cfg);
26298 if (this.tag === false) {
26299 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26302 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26303 this.cleanNamespace = true;
26306 this.walk(cfg.node);
26309 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26311 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26313 replaceTag : function(node)
26315 // walk children...
26316 //Roo.log(node.tagName);
26317 var ar = Array.from(node.childNodes);
26320 for (var i = 0; i < ar.length; i++) {
26322 if (e.nodeType == 1) {
26324 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26325 || // array and it matches
26326 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26328 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26330 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26332 this.replaceTag(ar[i]); // child is blacklisted as well...
26337 ar = Array.from(node.childNodes);
26338 for (var i = 0; i < ar.length; i++) {
26340 node.removeChild(ar[i]);
26341 // what if we need to walk these???
26342 node.parentNode.insertBefore(ar[i], node);
26343 if (this.tag !== false) {
26348 //Roo.log("REMOVE:" + node.tagName);
26349 node.parentNode.removeChild(node);
26350 return false; // don't walk children
26355 * @class Roo.htmleditor.FilterParagraph
26356 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26357 * like on 'push' to remove the <p> tags and replace them with line breaks.
26359 * Run a new Paragraph Filter
26360 * @param {Object} config Configuration options
26363 Roo.htmleditor.FilterParagraph = function(cfg)
26365 // no need to apply config.
26366 this.walk(cfg.node);
26369 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26376 replaceTag : function(node)
26379 if (node.childNodes.length == 1 &&
26380 node.childNodes[0].nodeType == 3 &&
26381 node.childNodes[0].textContent.trim().length < 1
26383 // remove and replace with '<BR>';
26384 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26385 return false; // no need to walk..
26387 var ar = Array.from(node.childNodes);
26388 for (var i = 0; i < ar.length; i++) {
26389 node.removeChild(ar[i]);
26390 // what if we need to walk these???
26391 node.parentNode.insertBefore(ar[i], node);
26393 // now what about this?
26397 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26398 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26399 node.parentNode.removeChild(node);
26406 * @class Roo.htmleditor.FilterSpan
26407 * filter span's with no attributes out..
26409 * Run a new Span Filter
26410 * @param {Object} config Configuration options
26413 Roo.htmleditor.FilterSpan = function(cfg)
26415 // no need to apply config.
26416 this.walk(cfg.node);
26419 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26425 replaceTag : function(node)
26427 if (node.attributes && node.attributes.length > 0) {
26428 return true; // walk if there are any.
26430 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26436 * @class Roo.htmleditor.FilterTableWidth
26437 try and remove table width data - as that frequently messes up other stuff.
26439 * was cleanTableWidths.
26441 * Quite often pasting from word etc.. results in tables with column and widths.
26442 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26445 * Run a new Table Filter
26446 * @param {Object} config Configuration options
26449 Roo.htmleditor.FilterTableWidth = function(cfg)
26451 // no need to apply config.
26452 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26453 this.walk(cfg.node);
26456 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26461 replaceTag: function(node) {
26465 if (node.hasAttribute('width')) {
26466 node.removeAttribute('width');
26470 if (node.hasAttribute("style")) {
26473 var styles = node.getAttribute("style").split(";");
26475 Roo.each(styles, function(s) {
26476 if (!s.match(/:/)) {
26479 var kv = s.split(":");
26480 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26483 // what ever is left... we allow.
26486 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26487 if (!nstyle.length) {
26488 node.removeAttribute('style');
26492 return true; // continue doing children..
26495 * @class Roo.htmleditor.FilterWord
26496 * try and clean up all the mess that Word generates.
26498 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26501 * Run a new Span Filter
26502 * @param {Object} config Configuration options
26505 Roo.htmleditor.FilterWord = function(cfg)
26507 // no need to apply config.
26508 this.replaceDocBullets(cfg.node);
26510 this.replaceAname(cfg.node);
26511 // this is disabled as the removal is done by other filters;
26512 // this.walk(cfg.node);
26517 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26523 * Clean up MS wordisms...
26525 replaceTag : function(node)
26528 // no idea what this does - span with text, replaceds with just text.
26530 node.nodeName == 'SPAN' &&
26531 !node.hasAttributes() &&
26532 node.childNodes.length == 1 &&
26533 node.firstChild.nodeName == "#text"
26535 var textNode = node.firstChild;
26536 node.removeChild(textNode);
26537 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26538 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26540 node.parentNode.insertBefore(textNode, node);
26541 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26542 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26545 node.parentNode.removeChild(node);
26546 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26551 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26552 node.parentNode.removeChild(node);
26553 return false; // dont do chidlren
26555 //Roo.log(node.tagName);
26556 // remove - but keep children..
26557 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26558 //Roo.log('-- removed');
26559 while (node.childNodes.length) {
26560 var cn = node.childNodes[0];
26561 node.removeChild(cn);
26562 node.parentNode.insertBefore(cn, node);
26563 // move node to parent - and clean it..
26564 if (cn.nodeType == 1) {
26565 this.replaceTag(cn);
26569 node.parentNode.removeChild(node);
26570 /// no need to iterate chidlren = it's got none..
26571 //this.iterateChildren(node, this.cleanWord);
26572 return false; // no need to iterate children.
26575 if (node.className.length) {
26577 var cn = node.className.split(/\W+/);
26579 Roo.each(cn, function(cls) {
26580 if (cls.match(/Mso[a-zA-Z]+/)) {
26585 node.className = cna.length ? cna.join(' ') : '';
26587 node.removeAttribute("class");
26591 if (node.hasAttribute("lang")) {
26592 node.removeAttribute("lang");
26595 if (node.hasAttribute("style")) {
26597 var styles = node.getAttribute("style").split(";");
26599 Roo.each(styles, function(s) {
26600 if (!s.match(/:/)) {
26603 var kv = s.split(":");
26604 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26607 // what ever is left... we allow.
26610 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26611 if (!nstyle.length) {
26612 node.removeAttribute('style');
26615 return true; // do children
26621 styleToObject: function(node)
26623 var styles = (node.getAttribute("style") || '').split(";");
26625 Roo.each(styles, function(s) {
26626 if (!s.match(/:/)) {
26629 var kv = s.split(":");
26631 // what ever is left... we allow.
26632 ret[kv[0].trim()] = kv[1];
26638 replaceAname : function (doc)
26640 // replace all the a/name without..
26641 var aa = Array.from(doc.getElementsByTagName('a'));
26642 for (var i = 0; i < aa.length; i++) {
26644 if (a.hasAttribute("name")) {
26645 a.removeAttribute("name");
26647 if (a.hasAttribute("href")) {
26650 // reparent children.
26651 this.removeNodeKeepChildren(a);
26661 replaceDocBullets : function(doc)
26663 // this is a bit odd - but it appears some indents use ql-indent-1
26664 //Roo.log(doc.innerHTML);
26666 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26667 for( var i = 0; i < listpara.length; i ++) {
26668 listpara[i].className = "MsoListParagraph";
26671 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26672 for( var i = 0; i < listpara.length; i ++) {
26673 listpara[i].className = "MsoListParagraph";
26675 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26676 for( var i = 0; i < listpara.length; i ++) {
26677 listpara[i].className = "MsoListParagraph";
26679 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
26680 for( var i = 0; i < listpara.length; i ++) {
26681 listpara[i].className = "MsoListParagraph";
26684 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26685 var htwo = Array.from(doc.getElementsByTagName('h2'));
26686 for( var i = 0; i < htwo.length; i ++) {
26687 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26688 htwo[i].className = "MsoListParagraph";
26691 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
26692 for( var i = 0; i < listpara.length; i ++) {
26693 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26694 listpara[i].className = "MsoListParagraph";
26696 listpara[i].className = "MsoNormalx";
26700 listpara = doc.getElementsByClassName('MsoListParagraph');
26701 // Roo.log(doc.innerHTML);
26705 while(listpara.length) {
26707 this.replaceDocBullet(listpara.item(0));
26714 replaceDocBullet : function(p)
26716 // gather all the siblings.
26718 parent = p.parentNode,
26719 doc = parent.ownerDocument,
26722 var listtype = 'ul';
26724 if (ns.nodeType != 1) {
26725 ns = ns.nextSibling;
26728 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26731 var spans = ns.getElementsByTagName('span');
26732 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26734 ns = ns.nextSibling;
26736 if (spans.length && spans[0].hasAttribute('style')) {
26737 var style = this.styleToObject(spans[0]);
26738 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26745 var spans = ns.getElementsByTagName('span');
26746 if (!spans.length) {
26749 var has_list = false;
26750 for(var i = 0; i < spans.length; i++) {
26751 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26760 ns = ns.nextSibling;
26764 if (!items.length) {
26769 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26770 parent.insertBefore(ul, p);
26772 var stack = [ ul ];
26773 var last_li = false;
26775 var margin_to_depth = {};
26778 items.forEach(function(n, ipos) {
26779 //Roo.log("got innertHMLT=" + n.innerHTML);
26781 var spans = n.getElementsByTagName('span');
26782 if (!spans.length) {
26783 //Roo.log("No spans found");
26785 parent.removeChild(n);
26788 return; // skip it...
26794 for(var i = 0; i < spans.length; i++) {
26796 style = this.styleToObject(spans[i]);
26797 if (typeof(style['mso-list']) == 'undefined') {
26800 if (listtype == 'ol') {
26801 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
26803 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26806 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26807 style = this.styleToObject(n); // mo-list is from the parent node.
26808 if (typeof(style['mso-list']) == 'undefined') {
26809 //Roo.log("parent is missing level");
26811 parent.removeChild(n);
26816 var margin = style['margin-left'];
26817 if (typeof(margin_to_depth[margin]) == 'undefined') {
26819 margin_to_depth[margin] = max_margins;
26821 nlvl = margin_to_depth[margin] ;
26825 var nul = doc.createElement(listtype); // what about number lists...
26827 last_li = doc.createElement('li');
26828 stack[lvl].appendChild(last_li);
26830 last_li.appendChild(nul);
26836 // not starting at 1..
26837 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26838 stack[nlvl].setAttribute("start", num);
26841 var nli = stack[nlvl].appendChild(doc.createElement('li'));
26843 nli.innerHTML = n.innerHTML;
26844 //Roo.log("innerHTML = " + n.innerHTML);
26845 parent.removeChild(n);
26861 * @class Roo.htmleditor.FilterStyleToTag
26862 * part of the word stuff... - certain 'styles' should be converted to tags.
26864 * font-weight: bold -> bold
26865 * ?? super / subscrit etc..
26868 * Run a new style to tag filter.
26869 * @param {Object} config Configuration options
26871 Roo.htmleditor.FilterStyleToTag = function(cfg)
26875 B : [ 'fontWeight' , 'bold'],
26876 I : [ 'fontStyle' , 'italic'],
26877 //pre : [ 'font-style' , 'italic'],
26878 // h1.. h6 ?? font-size?
26879 SUP : [ 'verticalAlign' , 'super' ],
26880 SUB : [ 'verticalAlign' , 'sub' ]
26885 Roo.apply(this, cfg);
26888 this.walk(cfg.node);
26895 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26897 tag: true, // all tags
26902 replaceTag : function(node)
26906 if (node.getAttribute("style") === null) {
26910 for (var k in this.tags) {
26911 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26913 node.style.removeProperty(this.tags[k][0]);
26916 if (!inject.length) {
26919 var cn = Array.from(node.childNodes);
26921 Roo.each(inject, function(t) {
26922 var nc = node.ownerDocument.createElement(t);
26923 nn.appendChild(nc);
26926 for(var i = 0;i < cn.length;cn++) {
26927 node.removeChild(cn[i]);
26928 nn.appendChild(cn[i]);
26930 return true /// iterate thru
26934 * @class Roo.htmleditor.FilterLongBr
26935 * BR/BR/BR - keep a maximum of 2...
26937 * Run a new Long BR Filter
26938 * @param {Object} config Configuration options
26941 Roo.htmleditor.FilterLongBr = function(cfg)
26943 // no need to apply config.
26944 this.walk(cfg.node);
26947 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26954 replaceTag : function(node)
26957 var ps = node.nextSibling;
26958 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26959 ps = ps.nextSibling;
26962 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
26963 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26967 if (!ps || ps.nodeType != 1) {
26971 if (!ps || ps.tagName != 'BR') {
26980 if (!node.previousSibling) {
26983 var ps = node.previousSibling;
26985 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26986 ps = ps.previousSibling;
26988 if (!ps || ps.nodeType != 1) {
26991 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26992 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26996 node.parentNode.removeChild(node); // remove me...
26998 return false; // no need to do children
27005 * @class Roo.htmleditor.FilterBlock
27006 * removes id / data-block and contenteditable that are associated with blocks
27007 * usage should be done on a cloned copy of the dom
27009 * Run a new Attribute Filter { node : xxxx }}
27010 * @param {Object} config Configuration options
27012 Roo.htmleditor.FilterBlock = function(cfg)
27014 Roo.apply(this, cfg);
27015 var qa = cfg.node.querySelectorAll;
27016 this.removeAttributes('data-block');
27017 this.removeAttributes('contenteditable');
27018 this.removeAttributes('id');
27022 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27024 node: true, // all tags
27027 removeAttributes : function(attr)
27029 var ar = this.node.querySelectorAll('*[' + attr + ']');
27030 for (var i =0;i<ar.length;i++) {
27031 ar[i].removeAttribute(attr);
27040 * This is based loosely on tinymce
27041 * @class Roo.htmleditor.TidySerializer
27042 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27044 * @method Serializer
27045 * @param {Object} settings Name/value settings object.
27049 Roo.htmleditor.TidySerializer = function(settings)
27051 Roo.apply(this, settings);
27053 this.writer = new Roo.htmleditor.TidyWriter(settings);
27058 Roo.htmleditor.TidySerializer.prototype = {
27061 * @param {boolean} inner do the inner of the node.
27068 * Serializes the specified node into a string.
27071 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27072 * @method serialize
27073 * @param {DomElement} node Node instance to serialize.
27074 * @return {String} String with HTML based on DOM tree.
27076 serialize : function(node) {
27078 // = settings.validate;
27079 var writer = this.writer;
27083 3: function(node) {
27085 writer.text(node.nodeValue, node);
27088 8: function(node) {
27089 writer.comment(node.nodeValue);
27091 // Processing instruction
27092 7: function(node) {
27093 writer.pi(node.name, node.nodeValue);
27096 10: function(node) {
27097 writer.doctype(node.nodeValue);
27100 4: function(node) {
27101 writer.cdata(node.nodeValue);
27103 // Document fragment
27104 11: function(node) {
27105 node = node.firstChild;
27111 node = node.nextSibling
27116 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27117 return writer.getContent();
27120 walk: function(node)
27122 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27123 handler = this.handlers[node.nodeType];
27130 var name = node.nodeName;
27131 var isEmpty = node.childNodes.length < 1;
27133 var writer = this.writer;
27134 var attrs = node.attributes;
27137 writer.start(node.nodeName, attrs, isEmpty, node);
27141 node = node.firstChild;
27148 node = node.nextSibling;
27154 // Serialize element and treat all non elements as fragments
27159 * This is based loosely on tinymce
27160 * @class Roo.htmleditor.TidyWriter
27161 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27164 * - not tested much with 'PRE' formated elements.
27170 Roo.htmleditor.TidyWriter = function(settings)
27173 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27174 Roo.apply(this, settings);
27178 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27181 Roo.htmleditor.TidyWriter.prototype = {
27188 // part of state...
27192 last_inline : false,
27197 * Writes the a start element such as <p id="a">.
27200 * @param {String} name Name of the element.
27201 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27202 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27204 start: function(name, attrs, empty, node)
27206 var i, l, attr, value;
27208 // there are some situations where adding line break && indentation will not work. will not work.
27209 // <span / b / i ... formating?
27211 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27212 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27214 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27216 var add_lb = name == 'BR' ? false : in_inline;
27218 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27222 var indentstr = this.indentstr;
27224 // e_inline = elements that can be inline, but still allow \n before and after?
27225 // only 'BR' ??? any others?
27227 // ADD LINE BEFORE tage
27228 if (!this.in_pre) {
27231 if (name == 'BR') {
27233 } else if (this.lastElementEndsWS()) {
27236 // otherwise - no new line. (and dont indent.)
27247 this.html.push(indentstr + '<', name.toLowerCase());
27250 for (i = 0, l = attrs.length; i < l; i++) {
27252 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27258 this.html[this.html.length] = '/>';
27260 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27262 var e_inline = name == 'BR' ? false : this.in_inline;
27264 if (!e_inline && !this.in_pre) {
27271 this.html[this.html.length] = '>';
27273 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27275 if (!in_inline && !in_pre) {
27276 var cn = node.firstChild;
27278 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27282 cn = cn.nextSibling;
27290 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27292 in_inline : in_inline
27294 // add a line after if we are not in a
27296 if (!in_inline && !in_pre) {
27305 lastElementEndsWS : function()
27307 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27308 if (value === false) {
27311 return value.match(/\s+$/);
27316 * Writes the a end element such as </p>.
27319 * @param {String} name Name of the element.
27321 end: function(name) {
27324 var indentstr = '';
27325 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27327 if (!this.in_pre && !in_inline) {
27329 indentstr = this.indentstr;
27331 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27332 this.last_inline = in_inline;
27334 // pop the indent state..
27337 * Writes a text node.
27339 * In pre - we should not mess with the contents.
27343 * @param {String} text String to write out.
27344 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27346 text: function(in_text, node)
27348 // if not in whitespace critical
27349 if (in_text.length < 1) {
27352 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27355 this.html[this.html.length] = text;
27359 if (this.in_inline) {
27360 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27362 text = text.replace(/\s+/,' '); // all white space to single white space
27365 // if next tag is '<BR>', then we can trim right..
27366 if (node.nextSibling &&
27367 node.nextSibling.nodeType == 1 &&
27368 node.nextSibling.nodeName == 'BR' )
27370 text = text.replace(/\s+$/g,'');
27372 // if previous tag was a BR, we can also trim..
27373 if (node.previousSibling &&
27374 node.previousSibling.nodeType == 1 &&
27375 node.previousSibling.nodeName == 'BR' )
27377 text = this.indentstr + text.replace(/^\s+/g,'');
27379 if (text.match(/\n/)) {
27380 text = text.replace(
27381 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27383 // remoeve the last whitespace / line break.
27384 text = text.replace(/\n\s+$/,'');
27386 // repace long lines
27390 this.html[this.html.length] = text;
27393 // see if previous element was a inline element.
27394 var indentstr = this.indentstr;
27396 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27398 // should trim left?
27399 if (node.previousSibling &&
27400 node.previousSibling.nodeType == 1 &&
27401 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27407 text = text.replace(/^\s+/,''); // trim left
27410 // should trim right?
27411 if (node.nextSibling &&
27412 node.nextSibling.nodeType == 1 &&
27413 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27418 text = text.replace(/\s+$/,''); // trim right
27425 if (text.length < 1) {
27428 if (!text.match(/\n/)) {
27429 this.html.push(indentstr + text);
27433 text = this.indentstr + text.replace(
27434 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27436 // remoeve the last whitespace / line break.
27437 text = text.replace(/\s+$/,'');
27439 this.html.push(text);
27441 // split and indent..
27446 * Writes a cdata node such as <![CDATA[data]]>.
27449 * @param {String} text String to write out inside the cdata.
27451 cdata: function(text) {
27452 this.html.push('<![CDATA[', text, ']]>');
27455 * Writes a comment node such as <!-- Comment -->.
27458 * @param {String} text String to write out inside the comment.
27460 comment: function(text) {
27461 this.html.push('<!--', text, '-->');
27464 * Writes a PI node such as <?xml attr="value" ?>.
27467 * @param {String} name Name of the pi.
27468 * @param {String} text String to write out inside the pi.
27470 pi: function(name, text) {
27471 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27472 this.indent != '' && this.html.push('\n');
27475 * Writes a doctype node such as <!DOCTYPE data>.
27478 * @param {String} text String to write out inside the doctype.
27480 doctype: function(text) {
27481 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27484 * Resets the internal buffer if one wants to reuse the writer.
27488 reset: function() {
27489 this.html.length = 0;
27498 * Returns the contents that got serialized.
27500 * @method getContent
27501 * @return {String} HTML contents that got written down.
27503 getContent: function() {
27504 return this.html.join('').replace(/\n$/, '');
27507 pushState : function(cfg)
27509 this.state.push(cfg);
27510 Roo.apply(this, cfg);
27513 popState : function()
27515 if (this.state.length < 1) {
27516 return; // nothing to push
27523 if (this.state.length > 0) {
27524 cfg = this.state[this.state.length-1];
27526 Roo.apply(this, cfg);
27529 addLine: function()
27531 if (this.html.length < 1) {
27536 var value = this.html[this.html.length - 1];
27537 if (value.length > 0 && '\n' !== value) {
27538 this.html.push('\n');
27543 //'pre script noscript style textarea video audio iframe object code'
27544 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
27548 Roo.htmleditor.TidyWriter.inline_elements = [
27549 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27550 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27552 Roo.htmleditor.TidyWriter.shortend_elements = [
27553 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27554 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27557 Roo.htmleditor.TidyWriter.whitespace_elements = [
27558 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27560 * This is based loosely on tinymce
27561 * @class Roo.htmleditor.TidyEntities
27563 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27565 * Not 100% sure this is actually used or needed.
27568 Roo.htmleditor.TidyEntities = {
27571 * initialize data..
27573 init : function (){
27575 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27580 buildEntitiesLookup: function(items, radix) {
27581 var i, chr, entity, lookup = {};
27585 items = typeof(items) == 'string' ? items.split(',') : items;
27586 radix = radix || 10;
27587 // Build entities lookup table
27588 for (i = 0; i < items.length; i += 2) {
27589 chr = String.fromCharCode(parseInt(items[i], radix));
27590 // Only add non base entities
27591 if (!this.baseEntities[chr]) {
27592 entity = '&' + items[i + 1] + ';';
27593 lookup[chr] = entity;
27594 lookup[entity] = chr;
27633 // Needs to be escaped since the YUI compressor would otherwise break the code
27640 // Reverse lookup table for raw entities
27641 reverseEntities : {
27649 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27650 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27651 rawCharsRegExp : /[<>&\"\']/g,
27652 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27653 namedEntities : false,
27654 namedEntitiesData : [
28155 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28157 * @method encodeRaw
28158 * @param {String} text Text to encode.
28159 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28160 * @return {String} Entity encoded text.
28162 encodeRaw: function(text, attr)
28165 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28166 return t.baseEntities[chr] || chr;
28170 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28171 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28172 * and is exposed as the DOMUtils.encode function.
28174 * @method encodeAllRaw
28175 * @param {String} text Text to encode.
28176 * @return {String} Entity encoded text.
28178 encodeAllRaw: function(text) {
28180 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28181 return t.baseEntities[chr] || chr;
28185 * Encodes the specified string using numeric entities. The core entities will be
28186 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28188 * @method encodeNumeric
28189 * @param {String} text Text to encode.
28190 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28191 * @return {String} Entity encoded text.
28193 encodeNumeric: function(text, attr) {
28195 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28196 // Multi byte sequence convert it to a single entity
28197 if (chr.length > 1) {
28198 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28200 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28204 * Encodes the specified string using named entities. The core entities will be encoded
28205 * as named ones but all non lower ascii characters will be encoded into named entities.
28207 * @method encodeNamed
28208 * @param {String} text Text to encode.
28209 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28210 * @param {Object} entities Optional parameter with entities to use.
28211 * @return {String} Entity encoded text.
28213 encodeNamed: function(text, attr, entities) {
28215 entities = entities || this.namedEntities;
28216 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28217 return t.baseEntities[chr] || entities[chr] || chr;
28221 * Returns an encode function based on the name(s) and it's optional entities.
28223 * @method getEncodeFunc
28224 * @param {String} name Comma separated list of encoders for example named,numeric.
28225 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28226 * @return {function} Encode function to be used.
28228 getEncodeFunc: function(name, entities) {
28229 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28231 function encodeNamedAndNumeric(text, attr) {
28232 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28233 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28237 function encodeCustomNamed(text, attr) {
28238 return t.encodeNamed(text, attr, entities);
28240 // Replace + with , to be compatible with previous TinyMCE versions
28241 name = this.makeMap(name.replace(/\+/g, ','));
28242 // Named and numeric encoder
28243 if (name.named && name.numeric) {
28244 return this.encodeNamedAndNumeric;
28250 return encodeCustomNamed;
28252 return this.encodeNamed;
28255 if (name.numeric) {
28256 return this.encodeNumeric;
28259 return this.encodeRaw;
28262 * Decodes the specified string, this will replace entities with raw UTF characters.
28265 * @param {String} text Text to entity decode.
28266 * @return {String} Entity decoded string.
28268 decode: function(text)
28271 return text.replace(this.entityRegExp, function(all, numeric) {
28273 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28274 // Support upper UTF
28275 if (numeric > 65535) {
28277 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28279 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28281 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28284 nativeDecode : function (text) {
28287 makeMap : function (items, delim, map) {
28289 items = items || [];
28290 delim = delim || ',';
28291 if (typeof items == "string") {
28292 items = items.split(delim);
28297 map[items[i]] = {};
28305 Roo.htmleditor.TidyEntities.init();
28307 * @class Roo.htmleditor.KeyEnter
28308 * Handle Enter press..
28309 * @cfg {Roo.HtmlEditorCore} core the editor.
28311 * Create a new Filter.
28312 * @param {Object} config Configuration options
28319 Roo.htmleditor.KeyEnter = function(cfg) {
28320 Roo.apply(this, cfg);
28321 // this does not actually call walk as it's really just a abstract class
28323 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28326 //Roo.htmleditor.KeyEnter.i = 0;
28329 Roo.htmleditor.KeyEnter.prototype = {
28333 keypress : function(e)
28335 if (e.charCode != 13 && e.charCode != 10) {
28336 Roo.log([e.charCode,e]);
28339 e.preventDefault();
28340 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28341 var doc = this.core.doc;
28345 var sel = this.core.getSelection();
28346 var range = sel.getRangeAt(0);
28347 var n = range.commonAncestorContainer;
28348 var pc = range.closest([ 'ol', 'ul']);
28349 var pli = range.closest('li');
28350 if (!pc || e.ctrlKey) {
28351 // on it list, or ctrl pressed.
28353 sel.insertNode('br', 'after');
28355 // only do this if we have ctrl key..
28356 var br = doc.createElement('br');
28357 br.className = 'clear';
28358 br.setAttribute('style', 'clear: both');
28359 sel.insertNode(br, 'after');
28363 this.core.undoManager.addEvent();
28364 this.core.fireEditorEvent(e);
28368 // deal with <li> insetion
28369 if (pli.innerText.trim() == '' &&
28370 pli.previousSibling &&
28371 pli.previousSibling.nodeName == 'LI' &&
28372 pli.previousSibling.innerText.trim() == '') {
28373 pli.parentNode.removeChild(pli.previousSibling);
28374 sel.cursorAfter(pc);
28375 this.core.undoManager.addEvent();
28376 this.core.fireEditorEvent(e);
28380 var li = doc.createElement('LI');
28381 li.innerHTML = ' ';
28382 if (!pli || !pli.firstSibling) {
28383 pc.appendChild(li);
28385 pli.parentNode.insertBefore(li, pli.firstSibling);
28387 sel.cursorText (li.firstChild);
28389 this.core.undoManager.addEvent();
28390 this.core.fireEditorEvent(e);
28402 * @class Roo.htmleditor.Block
28403 * Base class for html editor blocks - do not use it directly .. extend it..
28404 * @cfg {DomElement} node The node to apply stuff to.
28405 * @cfg {String} friendly_name the name that appears in the context bar about this block
28406 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28409 * Create a new Filter.
28410 * @param {Object} config Configuration options
28413 Roo.htmleditor.Block = function(cfg)
28415 // do nothing .. should not be called really.
28418 * factory method to get the block from an element (using cache if necessary)
28420 * @param {HtmlElement} the dom element
28422 Roo.htmleditor.Block.factory = function(node)
28424 var cc = Roo.htmleditor.Block.cache;
28425 var id = Roo.get(node).id;
28426 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28427 Roo.htmleditor.Block.cache[id].readElement(node);
28428 return Roo.htmleditor.Block.cache[id];
28430 var db = node.getAttribute('data-block');
28432 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28434 var cls = Roo.htmleditor['Block' + db];
28435 if (typeof(cls) == 'undefined') {
28436 //Roo.log(node.getAttribute('data-block'));
28437 Roo.log("OOps missing block : " + 'Block' + db);
28440 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28441 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
28445 * initalize all Elements from content that are 'blockable'
28447 * @param the body element
28449 Roo.htmleditor.Block.initAll = function(body, type)
28451 if (typeof(type) == 'undefined') {
28452 var ia = Roo.htmleditor.Block.initAll;
28458 Roo.each(Roo.get(body).query(type), function(e) {
28459 Roo.htmleditor.Block.factory(e);
28462 // question goes here... do we need to clear out this cache sometimes?
28463 // or show we make it relivant to the htmleditor.
28464 Roo.htmleditor.Block.cache = {};
28466 Roo.htmleditor.Block.prototype = {
28470 // used by context menu
28471 friendly_name : 'Based Block',
28473 // text for button to delete this element
28474 deleteTitle : false,
28478 * Update a node with values from this object
28479 * @param {DomElement} node
28481 updateElement : function(node)
28483 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28486 * convert to plain HTML for calling insertAtCursor..
28488 toHTML : function()
28490 return Roo.DomHelper.markup(this.toObject());
28493 * used by readEleemnt to extract data from a node
28494 * may need improving as it's pretty basic
28496 * @param {DomElement} node
28497 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28498 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28499 * @param {String} style the style property - eg. text-align
28501 getVal : function(node, tag, attr, style)
28504 if (tag !== true && n.tagName != tag.toUpperCase()) {
28505 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28506 // but kiss for now.
28507 n = node.getElementsByTagName(tag).item(0);
28512 if (attr === false) {
28515 if (attr == 'html') {
28516 return n.innerHTML;
28518 if (attr == 'style') {
28519 return n.style[style];
28522 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28526 * create a DomHelper friendly object - for use with
28527 * Roo.DomHelper.markup / overwrite / etc..
28530 toObject : function()
28535 * Read a node that has a 'data-block' property - and extract the values from it.
28536 * @param {DomElement} node - the node
28538 readElement : function(node)
28549 * @class Roo.htmleditor.BlockFigure
28550 * Block that has an image and a figcaption
28551 * @cfg {String} image_src the url for the image
28552 * @cfg {String} align (left|right) alignment for the block default left
28553 * @cfg {String} caption the text to appear below (and in the alt tag)
28554 * @cfg {String} caption_display (block|none) display or not the caption
28555 * @cfg {String|number} image_width the width of the image number or %?
28556 * @cfg {String|number} image_height the height of the image number or %?
28559 * Create a new Filter.
28560 * @param {Object} config Configuration options
28563 Roo.htmleditor.BlockFigure = function(cfg)
28566 this.readElement(cfg.node);
28567 this.updateElement(cfg.node);
28569 Roo.apply(this, cfg);
28571 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28578 caption_display : 'block',
28584 // margin: '2%', not used
28586 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
28589 // used by context menu
28590 friendly_name : 'Image with caption',
28591 deleteTitle : "Delete Image and Caption",
28593 contextMenu : function(toolbar)
28596 var block = function() {
28597 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28601 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28603 var syncValue = toolbar.editorcore.syncValue;
28609 xtype : 'TextItem',
28611 xns : rooui.Toolbar //Boostrap?
28615 text: 'Change Image URL',
28618 click: function (btn, state)
28622 Roo.MessageBox.show({
28623 title : "Image Source URL",
28624 msg : "Enter the url for the image",
28625 buttons: Roo.MessageBox.OKCANCEL,
28626 fn: function(btn, val){
28633 toolbar.editorcore.onEditorEvent();
28637 //multiline: multiline,
28639 value : b.image_src
28643 xns : rooui.Toolbar
28648 text: 'Change Link URL',
28651 click: function (btn, state)
28655 Roo.MessageBox.show({
28656 title : "Link URL",
28657 msg : "Enter the url for the link - leave blank to have no link",
28658 buttons: Roo.MessageBox.OKCANCEL,
28659 fn: function(btn, val){
28666 toolbar.editorcore.onEditorEvent();
28670 //multiline: multiline,
28676 xns : rooui.Toolbar
28680 text: 'Show Video URL',
28683 click: function (btn, state)
28685 Roo.MessageBox.alert("Video URL",
28686 block().video_url == '' ? 'This image is not linked ot a video' :
28687 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28690 xns : rooui.Toolbar
28695 xtype : 'TextItem',
28697 xns : rooui.Toolbar //Boostrap?
28700 xtype : 'ComboBox',
28701 allowBlank : false,
28702 displayField : 'val',
28705 triggerAction : 'all',
28707 valueField : 'val',
28711 select : function (combo, r, index)
28713 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28715 b.width = r.get('val');
28718 toolbar.editorcore.onEditorEvent();
28723 xtype : 'SimpleStore',
28736 xtype : 'TextItem',
28738 xns : rooui.Toolbar //Boostrap?
28741 xtype : 'ComboBox',
28742 allowBlank : false,
28743 displayField : 'val',
28746 triggerAction : 'all',
28748 valueField : 'val',
28752 select : function (combo, r, index)
28754 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28756 b.align = r.get('val');
28759 toolbar.editorcore.onEditorEvent();
28764 xtype : 'SimpleStore',
28778 text: 'Hide Caption',
28779 name : 'caption_display',
28781 enableToggle : true,
28782 setValue : function(v) {
28783 // this trigger toggle.
28785 this.setText(v ? "Hide Caption" : "Show Caption");
28786 this.setPressed(v != 'block');
28789 toggle: function (btn, state)
28792 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28793 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28796 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28797 toolbar.editorcore.onEditorEvent();
28800 xns : rooui.Toolbar
28806 * create a DomHelper friendly object - for use with
28807 * Roo.DomHelper.markup / overwrite / etc..
28809 toObject : function()
28811 var d = document.createElement('div');
28812 d.innerHTML = this.caption;
28814 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
28816 var iw = this.align == 'center' ? this.width : '100%';
28819 contenteditable : 'false',
28820 src : this.image_src,
28821 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28824 maxWidth : iw + ' !important', // this is not getting rendered?
28830 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28832 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
28837 if (this.href.length > 0) {
28841 contenteditable : 'true',
28849 if (this.video_url.length > 0) {
28854 allowfullscreen : true,
28855 width : 420, // these are for video tricks - that we replace the outer
28857 src : this.video_url,
28863 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28864 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28869 'data-block' : 'Figure',
28870 'data-width' : this.width,
28871 contenteditable : 'false',
28875 float : this.align ,
28876 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28877 width : this.align == 'center' ? '100%' : this.width,
28879 padding: this.align == 'center' ? '0' : '0 10px' ,
28880 textAlign : this.align // seems to work for email..
28885 align : this.align,
28891 'data-display' : this.caption_display,
28893 textAlign : 'left',
28895 lineHeight : '24px',
28896 display : this.caption_display,
28897 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
28899 width: this.align == 'center' ? this.width : '100%'
28903 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
28908 marginTop : '16px',
28914 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
28916 contenteditable : true,
28932 readElement : function(node)
28934 // this should not really come from the link...
28935 this.video_url = this.getVal(node, 'div', 'src');
28936 this.cls = this.getVal(node, 'div', 'class');
28937 this.href = this.getVal(node, 'a', 'href');
28940 this.image_src = this.getVal(node, 'img', 'src');
28942 this.align = this.getVal(node, 'figure', 'align');
28943 var figcaption = this.getVal(node, 'figcaption', false);
28944 if (figcaption !== '') {
28945 this.caption = this.getVal(figcaption, 'i', 'html');
28949 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28950 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28951 this.width = this.getVal(node, true, 'data-width');
28952 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28955 removeNode : function()
28972 * @class Roo.htmleditor.BlockTable
28973 * Block that manages a table
28976 * Create a new Filter.
28977 * @param {Object} config Configuration options
28980 Roo.htmleditor.BlockTable = function(cfg)
28983 this.readElement(cfg.node);
28984 this.updateElement(cfg.node);
28986 Roo.apply(this, cfg);
28989 for(var r = 0; r < this.no_row; r++) {
28991 for(var c = 0; c < this.no_col; c++) {
28992 this.rows[r][c] = this.emptyCell();
28999 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29008 // used by context menu
29009 friendly_name : 'Table',
29010 deleteTitle : 'Delete Table',
29011 // context menu is drawn once..
29013 contextMenu : function(toolbar)
29016 var block = function() {
29017 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29021 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29023 var syncValue = toolbar.editorcore.syncValue;
29029 xtype : 'TextItem',
29031 xns : rooui.Toolbar //Boostrap?
29034 xtype : 'ComboBox',
29035 allowBlank : false,
29036 displayField : 'val',
29039 triggerAction : 'all',
29041 valueField : 'val',
29045 select : function (combo, r, index)
29047 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29049 b.width = r.get('val');
29052 toolbar.editorcore.onEditorEvent();
29057 xtype : 'SimpleStore',
29069 xtype : 'TextItem',
29070 text : "Columns: ",
29071 xns : rooui.Toolbar //Boostrap?
29078 click : function (_self, e)
29080 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29081 block().removeColumn();
29083 toolbar.editorcore.onEditorEvent();
29086 xns : rooui.Toolbar
29092 click : function (_self, e)
29094 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29095 block().addColumn();
29097 toolbar.editorcore.onEditorEvent();
29100 xns : rooui.Toolbar
29104 xtype : 'TextItem',
29106 xns : rooui.Toolbar //Boostrap?
29113 click : function (_self, e)
29115 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29116 block().removeRow();
29118 toolbar.editorcore.onEditorEvent();
29121 xns : rooui.Toolbar
29127 click : function (_self, e)
29131 toolbar.editorcore.onEditorEvent();
29134 xns : rooui.Toolbar
29139 text: 'Reset Column Widths',
29142 click : function (_self, e)
29144 block().resetWidths();
29146 toolbar.editorcore.onEditorEvent();
29149 xns : rooui.Toolbar
29160 * create a DomHelper friendly object - for use with
29161 * Roo.DomHelper.markup / overwrite / etc..
29162 * ?? should it be called with option to hide all editing features?
29164 toObject : function()
29169 contenteditable : 'false', // this stops cell selection from picking the table.
29170 'data-block' : 'Table',
29173 border : 'solid 1px #000', // ??? hard coded?
29174 'border-collapse' : 'collapse'
29177 { tag : 'tbody' , cn : [] }
29181 // do we have a head = not really
29183 Roo.each(this.rows, function( row ) {
29188 border : 'solid 1px #000',
29194 ret.cn[0].cn.push(tr);
29195 // does the row have any properties? ?? height?
29197 Roo.each(row, function( cell ) {
29201 contenteditable : 'true',
29202 'data-block' : 'Td',
29206 if (cell.colspan > 1) {
29207 td.colspan = cell.colspan ;
29208 nc += cell.colspan;
29212 if (cell.rowspan > 1) {
29213 td.rowspan = cell.rowspan ;
29222 ncols = Math.max(nc, ncols);
29226 // add the header row..
29235 readElement : function(node)
29237 node = node ? node : this.node ;
29238 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29242 var trs = Array.from(node.rows);
29243 trs.forEach(function(tr) {
29245 this.rows.push(row);
29249 Array.from(tr.cells).forEach(function(td) {
29252 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29253 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29254 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29255 html : td.innerHTML
29257 no_column += add.colspan;
29264 this.no_col = Math.max(this.no_col, no_column);
29271 normalizeRows: function()
29275 this.rows.forEach(function(row) {
29278 row = this.normalizeRow(row);
29280 row.forEach(function(c) {
29281 while (typeof(ret[rid][cid]) != 'undefined') {
29284 if (typeof(ret[rid]) == 'undefined') {
29290 if (c.rowspan < 2) {
29294 for(var i = 1 ;i < c.rowspan; i++) {
29295 if (typeof(ret[rid+i]) == 'undefined') {
29298 ret[rid+i][cid] = c;
29306 normalizeRow: function(row)
29309 row.forEach(function(c) {
29310 if (c.colspan < 2) {
29314 for(var i =0 ;i < c.colspan; i++) {
29322 deleteColumn : function(sel)
29324 if (!sel || sel.type != 'col') {
29327 if (this.no_col < 2) {
29331 this.rows.forEach(function(row) {
29332 var cols = this.normalizeRow(row);
29333 var col = cols[sel.col];
29334 if (col.colspan > 1) {
29344 removeColumn : function()
29346 this.deleteColumn({
29348 col : this.no_col-1
29350 this.updateElement();
29354 addColumn : function()
29357 this.rows.forEach(function(row) {
29358 row.push(this.emptyCell());
29361 this.updateElement();
29364 deleteRow : function(sel)
29366 if (!sel || sel.type != 'row') {
29370 if (this.no_row < 2) {
29374 var rows = this.normalizeRows();
29377 rows[sel.row].forEach(function(col) {
29378 if (col.rowspan > 1) {
29381 col.remove = 1; // flage it as removed.
29386 this.rows.forEach(function(row) {
29388 row.forEach(function(c) {
29389 if (typeof(c.remove) == 'undefined') {
29394 if (newrow.length > 0) {
29398 this.rows = newrows;
29403 this.updateElement();
29406 removeRow : function()
29410 row : this.no_row-1
29416 addRow : function()
29420 for (var i = 0; i < this.no_col; i++ ) {
29422 row.push(this.emptyCell());
29425 this.rows.push(row);
29426 this.updateElement();
29430 // the default cell object... at present...
29431 emptyCell : function() {
29432 return (new Roo.htmleditor.BlockTd({})).toObject();
29437 removeNode : function()
29444 resetWidths : function()
29446 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29447 var nn = Roo.htmleditor.Block.factory(n);
29449 nn.updateElement(n);
29462 * since selections really work on the table cell, then editing really should work from there
29464 * The original plan was to support merging etc... - but that may not be needed yet..
29466 * So this simple version will support:
29468 * adjust the width +/-
29469 * reset the width...
29478 * @class Roo.htmleditor.BlockTable
29479 * Block that manages a table
29482 * Create a new Filter.
29483 * @param {Object} config Configuration options
29486 Roo.htmleditor.BlockTd = function(cfg)
29489 this.readElement(cfg.node);
29490 this.updateElement(cfg.node);
29492 Roo.apply(this, cfg);
29497 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29502 textAlign : 'left',
29509 // used by context menu
29510 friendly_name : 'Table Cell',
29511 deleteTitle : false, // use our customer delete
29513 // context menu is drawn once..
29515 contextMenu : function(toolbar)
29518 var cell = function() {
29519 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29522 var table = function() {
29523 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29527 var saveSel = function()
29529 lr = toolbar.editorcore.getSelection().getRangeAt(0);
29531 var restoreSel = function()
29535 toolbar.editorcore.focus();
29536 var cr = toolbar.editorcore.getSelection();
29537 cr.removeAllRanges();
29539 toolbar.editorcore.onEditorEvent();
29540 }).defer(10, this);
29546 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29548 var syncValue = toolbar.editorcore.syncValue;
29555 text : 'Edit Table',
29557 click : function() {
29558 var t = toolbar.tb.selectedNode.closest('table');
29559 toolbar.editorcore.selectNode(t);
29560 toolbar.editorcore.onEditorEvent();
29569 xtype : 'TextItem',
29570 text : "Column Width: ",
29571 xns : rooui.Toolbar
29578 click : function (_self, e)
29580 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29581 cell().shrinkColumn();
29583 toolbar.editorcore.onEditorEvent();
29586 xns : rooui.Toolbar
29592 click : function (_self, e)
29594 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29595 cell().growColumn();
29597 toolbar.editorcore.onEditorEvent();
29600 xns : rooui.Toolbar
29604 xtype : 'TextItem',
29605 text : "Vertical Align: ",
29606 xns : rooui.Toolbar //Boostrap?
29609 xtype : 'ComboBox',
29610 allowBlank : false,
29611 displayField : 'val',
29614 triggerAction : 'all',
29616 valueField : 'val',
29620 select : function (combo, r, index)
29622 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29624 b.valign = r.get('val');
29627 toolbar.editorcore.onEditorEvent();
29632 xtype : 'SimpleStore',
29636 ['bottom'] // there are afew more...
29644 xtype : 'TextItem',
29645 text : "Merge Cells: ",
29646 xns : rooui.Toolbar
29655 click : function (_self, e)
29657 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29658 cell().mergeRight();
29659 //block().growColumn();
29661 toolbar.editorcore.onEditorEvent();
29664 xns : rooui.Toolbar
29671 click : function (_self, e)
29673 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29674 cell().mergeBelow();
29675 //block().growColumn();
29677 toolbar.editorcore.onEditorEvent();
29680 xns : rooui.Toolbar
29683 xtype : 'TextItem',
29685 xns : rooui.Toolbar
29693 click : function (_self, e)
29695 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29698 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29699 toolbar.editorcore.onEditorEvent();
29703 xns : rooui.Toolbar
29707 xns : rooui.Toolbar
29716 xns : rooui.Toolbar,
29725 click : function (_self, e)
29729 cell().deleteColumn();
29731 toolbar.editorcore.selectNode(t.node);
29732 toolbar.editorcore.onEditorEvent();
29741 click : function (_self, e)
29744 cell().deleteRow();
29747 toolbar.editorcore.selectNode(t.node);
29748 toolbar.editorcore.onEditorEvent();
29755 xtype : 'Separator',
29762 click : function (_self, e)
29765 var nn = t.node.nextSibling || t.node.previousSibling;
29766 t.node.parentNode.removeChild(t.node);
29768 toolbar.editorcore.selectNode(nn, true);
29770 toolbar.editorcore.onEditorEvent();
29780 // align... << fixme
29788 * create a DomHelper friendly object - for use with
29789 * Roo.DomHelper.markup / overwrite / etc..
29790 * ?? should it be called with option to hide all editing features?
29793 * create a DomHelper friendly object - for use with
29794 * Roo.DomHelper.markup / overwrite / etc..
29795 * ?? should it be called with option to hide all editing features?
29797 toObject : function()
29801 contenteditable : 'true', // this stops cell selection from picking the table.
29802 'data-block' : 'Td',
29803 valign : this.valign,
29805 'text-align' : this.textAlign,
29806 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29807 'border-collapse' : 'collapse',
29808 padding : '6px', // 8 for desktop / 4 for mobile
29809 'vertical-align': this.valign
29813 if (this.width != '') {
29814 ret.width = this.width;
29815 ret.style.width = this.width;
29819 if (this.colspan > 1) {
29820 ret.colspan = this.colspan ;
29822 if (this.rowspan > 1) {
29823 ret.rowspan = this.rowspan ;
29832 readElement : function(node)
29834 node = node ? node : this.node ;
29835 this.width = node.style.width;
29836 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29837 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29838 this.html = node.innerHTML;
29839 if (node.style.textAlign != '') {
29840 this.textAlign = node.style.textAlign;
29846 // the default cell object... at present...
29847 emptyCell : function() {
29851 textAlign : 'left',
29852 html : " " // is this going to be editable now?
29857 removeNode : function()
29859 return this.node.closest('table');
29867 toTableArray : function()
29870 var tab = this.node.closest('tr').closest('table');
29871 Array.from(tab.rows).forEach(function(r, ri){
29875 this.colWidths = [];
29876 var all_auto = true;
29877 Array.from(tab.rows).forEach(function(r, ri){
29880 Array.from(r.cells).forEach(function(ce, ci){
29885 colspan : ce.colSpan,
29886 rowspan : ce.rowSpan
29888 if (ce.isEqualNode(this.node)) {
29891 // if we have been filled up by a row?
29892 if (typeof(ret[rn][cn]) != 'undefined') {
29893 while(typeof(ret[rn][cn]) != 'undefined') {
29899 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29900 this.colWidths[cn] = ce.style.width;
29901 if (this.colWidths[cn] != '') {
29907 if (c.colspan < 2 && c.rowspan < 2 ) {
29912 for(var j = 0; j < c.rowspan; j++) {
29913 if (typeof(ret[rn+j]) == 'undefined') {
29914 continue; // we have a problem..
29917 for(var i = 0; i < c.colspan; i++) {
29918 ret[rn+j][cn+i] = c;
29927 // initalize widths.?
29928 // either all widths or no widths..
29930 this.colWidths[0] = false; // no widths flag.
29941 mergeRight: function()
29944 // get the contents of the next cell along..
29945 var tr = this.node.closest('tr');
29946 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29947 if (i >= tr.childNodes.length - 1) {
29948 return; // no cells on right to merge with.
29950 var table = this.toTableArray();
29952 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29953 return; // nothing right?
29955 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29956 // right cell - must be same rowspan and on the same row.
29957 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29958 return; // right hand side is not same rowspan.
29963 this.node.innerHTML += ' ' + rc.cell.innerHTML;
29964 tr.removeChild(rc.cell);
29965 this.colspan += rc.colspan;
29966 this.node.setAttribute('colspan', this.colspan);
29968 var table = this.toTableArray();
29969 this.normalizeWidths(table);
29970 this.updateWidths(table);
29974 mergeBelow : function()
29976 var table = this.toTableArray();
29977 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
29978 return; // no row below
29980 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
29981 return; // nothing right?
29983 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
29985 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
29986 return; // right hand side is not same rowspan.
29988 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
29989 rc.cell.parentNode.removeChild(rc.cell);
29990 this.rowspan += rc.rowspan;
29991 this.node.setAttribute('rowspan', this.rowspan);
29996 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
29999 var table = this.toTableArray();
30000 var cd = this.cellData;
30004 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30007 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30008 if (r == cd.row && c == cd.col) {
30009 this.node.removeAttribute('rowspan');
30010 this.node.removeAttribute('colspan');
30013 var ntd = this.node.cloneNode(); // which col/row should be 0..
30014 ntd.removeAttribute('id');
30015 ntd.style.width = this.colWidths[c];
30016 ntd.innerHTML = '';
30017 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30021 this.redrawAllCells(table);
30027 redrawAllCells: function(table)
30031 var tab = this.node.closest('tr').closest('table');
30032 var ctr = tab.rows[0].parentNode;
30033 Array.from(tab.rows).forEach(function(r, ri){
30035 Array.from(r.cells).forEach(function(ce, ci){
30036 ce.parentNode.removeChild(ce);
30038 r.parentNode.removeChild(r);
30040 for(var r = 0 ; r < table.length; r++) {
30041 var re = tab.rows[r];
30043 var re = tab.ownerDocument.createElement('tr');
30044 ctr.appendChild(re);
30045 for(var c = 0 ; c < table[r].length; c++) {
30046 if (table[r][c].cell === false) {
30050 re.appendChild(table[r][c].cell);
30052 table[r][c].cell = false;
30057 updateWidths : function(table)
30059 for(var r = 0 ; r < table.length; r++) {
30061 for(var c = 0 ; c < table[r].length; c++) {
30062 if (table[r][c].cell === false) {
30066 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30067 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30068 el.width = Math.floor(this.colWidths[c]) +'%';
30069 el.updateElement(el.node);
30071 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30072 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30074 for(var i = 0; i < table[r][c].colspan; i ++) {
30075 width += Math.floor(this.colWidths[c + i]);
30077 el.width = width +'%';
30078 el.updateElement(el.node);
30080 table[r][c].cell = false; // done
30084 normalizeWidths : function(table)
30086 if (this.colWidths[0] === false) {
30087 var nw = 100.0 / this.colWidths.length;
30088 this.colWidths.forEach(function(w,i) {
30089 this.colWidths[i] = nw;
30094 var t = 0, missing = [];
30096 this.colWidths.forEach(function(w,i) {
30098 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30099 var add = this.colWidths[i];
30108 var nc = this.colWidths.length;
30109 if (missing.length) {
30110 var mult = (nc - missing.length) / (1.0 * nc);
30112 var ew = (100 -t) / (1.0 * missing.length);
30113 this.colWidths.forEach(function(w,i) {
30115 this.colWidths[i] = w * mult;
30119 this.colWidths[i] = ew;
30121 // have to make up numbers..
30124 // now we should have all the widths..
30129 shrinkColumn : function()
30131 var table = this.toTableArray();
30132 this.normalizeWidths(table);
30133 var col = this.cellData.col;
30134 var nw = this.colWidths[col] * 0.8;
30138 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30139 this.colWidths.forEach(function(w,i) {
30141 this.colWidths[i] = nw;
30144 this.colWidths[i] += otherAdd
30146 this.updateWidths(table);
30149 growColumn : function()
30151 var table = this.toTableArray();
30152 this.normalizeWidths(table);
30153 var col = this.cellData.col;
30154 var nw = this.colWidths[col] * 1.2;
30158 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30159 this.colWidths.forEach(function(w,i) {
30161 this.colWidths[i] = nw;
30164 this.colWidths[i] -= otherSub
30166 this.updateWidths(table);
30169 deleteRow : function()
30171 // delete this rows 'tr'
30172 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30173 // then reduce the rowspan.
30174 var table = this.toTableArray();
30175 // this.cellData.row;
30176 for (var i =0;i< table[this.cellData.row].length ; i++) {
30177 var c = table[this.cellData.row][i];
30178 if (c.row != this.cellData.row) {
30181 c.cell.setAttribute('rowspan', c.rowspan);
30184 if (c.rowspan > 1) {
30186 c.cell.setAttribute('rowspan', c.rowspan);
30189 table.splice(this.cellData.row,1);
30190 this.redrawAllCells(table);
30193 deleteColumn : function()
30195 var table = this.toTableArray();
30197 for (var i =0;i< table.length ; i++) {
30198 var c = table[i][this.cellData.col];
30199 if (c.col != this.cellData.col) {
30200 table[i][this.cellData.col].colspan--;
30201 } else if (c.colspan > 1) {
30203 c.cell.setAttribute('colspan', c.colspan);
30205 table[i].splice(this.cellData.col,1);
30208 this.redrawAllCells(table);
30216 //<script type="text/javascript">
30219 * Based Ext JS Library 1.1.1
30220 * Copyright(c) 2006-2007, Ext JS, LLC.
30226 * @class Roo.HtmlEditorCore
30227 * @extends Roo.Component
30228 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30230 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30233 Roo.HtmlEditorCore = function(config){
30236 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30241 * @event initialize
30242 * Fires when the editor is fully initialized (including the iframe)
30243 * @param {Roo.HtmlEditorCore} this
30248 * Fires when the editor is first receives the focus. Any insertion must wait
30249 * until after this event.
30250 * @param {Roo.HtmlEditorCore} this
30254 * @event beforesync
30255 * Fires before the textarea is updated with content from the editor iframe. Return false
30256 * to cancel the sync.
30257 * @param {Roo.HtmlEditorCore} this
30258 * @param {String} html
30262 * @event beforepush
30263 * Fires before the iframe editor is updated with content from the textarea. Return false
30264 * to cancel the push.
30265 * @param {Roo.HtmlEditorCore} this
30266 * @param {String} html
30271 * Fires when the textarea is updated with content from the editor iframe.
30272 * @param {Roo.HtmlEditorCore} this
30273 * @param {String} html
30278 * Fires when the iframe editor is updated with content from the textarea.
30279 * @param {Roo.HtmlEditorCore} this
30280 * @param {String} html
30285 * @event editorevent
30286 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30287 * @param {Roo.HtmlEditorCore} this
30294 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30296 // defaults : white / black...
30297 this.applyBlacklists();
30304 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30308 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30314 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
30319 * @cfg {Number} height (in pixels)
30323 * @cfg {Number} width (in pixels)
30327 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30328 * if you are doing an email editor, this probably needs disabling, it's designed
30333 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30335 enableBlocks : true,
30337 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30340 stylesheets: false,
30342 * @cfg {String} language default en - language of text (usefull for rtl languages)
30348 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30349 * - by default they are stripped - if you are editing email you may need this.
30351 allowComments: false,
30355 // private properties
30356 validationEvent : false,
30358 initialized : false,
30360 sourceEditMode : false,
30361 onFocus : Roo.emptyFn,
30363 hideMode:'offsets',
30367 // blacklist + whitelisted elements..
30374 undoManager : false,
30376 * Protected method that will not generally be called directly. It
30377 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30378 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30380 getDocMarkup : function(){
30384 // inherit styels from page...??
30385 if (this.stylesheets === false) {
30387 Roo.get(document.head).select('style').each(function(node) {
30388 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30391 Roo.get(document.head).select('link').each(function(node) {
30392 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30395 } else if (!this.stylesheets.length) {
30397 st = '<style type="text/css">' +
30398 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30401 for (var i in this.stylesheets) {
30402 if (typeof(this.stylesheets[i]) != 'string') {
30405 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30410 st += '<style type="text/css">' +
30411 'IMG { cursor: pointer } ' +
30414 st += '<meta name="google" content="notranslate">';
30416 var cls = 'notranslate roo-htmleditor-body';
30418 if(this.bodyCls.length){
30419 cls += ' ' + this.bodyCls;
30422 return '<html class="notranslate" translate="no"><head>' + st +
30423 //<style type="text/css">' +
30424 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30426 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30430 onRender : function(ct, position)
30433 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30434 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30437 this.el.dom.style.border = '0 none';
30438 this.el.dom.setAttribute('tabIndex', -1);
30439 this.el.addClass('x-hidden hide');
30443 if(Roo.isIE){ // fix IE 1px bogus margin
30444 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30448 this.frameId = Roo.id();
30452 var iframe = this.owner.wrap.createChild({
30454 cls: 'form-control', // bootstrap..
30456 name: this.frameId,
30457 frameBorder : 'no',
30458 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
30463 this.iframe = iframe.dom;
30465 this.assignDocWin();
30467 this.doc.designMode = 'on';
30470 this.doc.write(this.getDocMarkup());
30474 var task = { // must defer to wait for browser to be ready
30476 //console.log("run task?" + this.doc.readyState);
30477 this.assignDocWin();
30478 if(this.doc.body || this.doc.readyState == 'complete'){
30480 this.doc.designMode="on";
30485 Roo.TaskMgr.stop(task);
30486 this.initEditor.defer(10, this);
30493 Roo.TaskMgr.start(task);
30498 onResize : function(w, h)
30500 Roo.log('resize: ' +w + ',' + h );
30501 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30505 if(typeof w == 'number'){
30507 this.iframe.style.width = w + 'px';
30509 if(typeof h == 'number'){
30511 this.iframe.style.height = h + 'px';
30513 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30520 * Toggles the editor between standard and source edit mode.
30521 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30523 toggleSourceEdit : function(sourceEditMode){
30525 this.sourceEditMode = sourceEditMode === true;
30527 if(this.sourceEditMode){
30529 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
30532 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30533 //this.iframe.className = '';
30536 //this.setSize(this.owner.wrap.getSize());
30537 //this.fireEvent('editmodechange', this, this.sourceEditMode);
30544 * Protected method that will not generally be called directly. If you need/want
30545 * custom HTML cleanup, this is the method you should override.
30546 * @param {String} html The HTML to be cleaned
30547 * return {String} The cleaned HTML
30549 cleanHtml : function(html)
30551 html = String(html);
30552 if(html.length > 5){
30553 if(Roo.isSafari){ // strip safari nonsense
30554 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30557 if(html == ' '){
30564 * HTML Editor -> Textarea
30565 * Protected method that will not generally be called directly. Syncs the contents
30566 * of the editor iframe with the textarea.
30568 syncValue : function()
30570 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30571 if(this.initialized){
30573 if (this.undoManager) {
30574 this.undoManager.addEvent();
30578 var bd = (this.doc.body || this.doc.documentElement);
30581 var sel = this.win.getSelection();
30583 var div = document.createElement('div');
30584 div.innerHTML = bd.innerHTML;
30585 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30586 if (gtx.length > 0) {
30587 var rm = gtx.item(0).parentNode;
30588 rm.parentNode.removeChild(rm);
30592 if (this.enableBlocks) {
30593 new Roo.htmleditor.FilterBlock({ node : div });
30596 var html = div.innerHTML;
30599 if (this.autoClean) {
30601 new Roo.htmleditor.FilterAttributes({
30622 attrib_clean : ['href', 'src' ]
30625 var tidy = new Roo.htmleditor.TidySerializer({
30628 html = tidy.serialize(div);
30634 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30635 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30637 html = '<div style="'+m[0]+'">' + html + '</div>';
30640 html = this.cleanHtml(html);
30641 // fix up the special chars.. normaly like back quotes in word...
30642 // however we do not want to do this with chinese..
30643 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30645 var cc = match.charCodeAt();
30647 // Get the character value, handling surrogate pairs
30648 if (match.length == 2) {
30649 // It's a surrogate pair, calculate the Unicode code point
30650 var high = match.charCodeAt(0) - 0xD800;
30651 var low = match.charCodeAt(1) - 0xDC00;
30652 cc = (high * 0x400) + low + 0x10000;
30654 (cc >= 0x4E00 && cc < 0xA000 ) ||
30655 (cc >= 0x3400 && cc < 0x4E00 ) ||
30656 (cc >= 0xf900 && cc < 0xfb00 )
30661 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30662 return "&#" + cc + ";";
30669 if(this.owner.fireEvent('beforesync', this, html) !== false){
30670 this.el.dom.value = html;
30671 this.owner.fireEvent('sync', this, html);
30677 * TEXTAREA -> EDITABLE
30678 * Protected method that will not generally be called directly. Pushes the value of the textarea
30679 * into the iframe editor.
30681 pushValue : function()
30683 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30684 if(this.initialized){
30685 var v = this.el.dom.value.trim();
30688 if(this.owner.fireEvent('beforepush', this, v) !== false){
30689 var d = (this.doc.body || this.doc.documentElement);
30692 this.el.dom.value = d.innerHTML;
30693 this.owner.fireEvent('push', this, v);
30695 if (this.autoClean) {
30696 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30697 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30699 if (this.enableBlocks) {
30700 Roo.htmleditor.Block.initAll(this.doc.body);
30703 this.updateLanguage();
30705 var lc = this.doc.body.lastChild;
30706 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30707 // add an extra line at the end.
30708 this.doc.body.appendChild(this.doc.createElement('br'));
30716 deferFocus : function(){
30717 this.focus.defer(10, this);
30721 focus : function(){
30722 if(this.win && !this.sourceEditMode){
30729 assignDocWin: function()
30731 var iframe = this.iframe;
30734 this.doc = iframe.contentWindow.document;
30735 this.win = iframe.contentWindow;
30737 // if (!Roo.get(this.frameId)) {
30740 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30741 // this.win = Roo.get(this.frameId).dom.contentWindow;
30743 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30747 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30748 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30753 initEditor : function(){
30754 //console.log("INIT EDITOR");
30755 this.assignDocWin();
30759 this.doc.designMode="on";
30761 this.doc.write(this.getDocMarkup());
30764 var dbody = (this.doc.body || this.doc.documentElement);
30765 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30766 // this copies styles from the containing element into thsi one..
30767 // not sure why we need all of this..
30768 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30770 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30771 //ss['background-attachment'] = 'fixed'; // w3c
30772 dbody.bgProperties = 'fixed'; // ie
30773 dbody.setAttribute("translate", "no");
30775 //Roo.DomHelper.applyStyles(dbody, ss);
30776 Roo.EventManager.on(this.doc, {
30778 'mouseup': this.onEditorEvent,
30779 'dblclick': this.onEditorEvent,
30780 'click': this.onEditorEvent,
30781 'keyup': this.onEditorEvent,
30786 Roo.EventManager.on(this.doc, {
30787 'paste': this.onPasteEvent,
30791 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30794 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30795 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30797 this.initialized = true;
30800 // initialize special key events - enter
30801 new Roo.htmleditor.KeyEnter({core : this});
30805 this.owner.fireEvent('initialize', this);
30808 // this is to prevent a href clicks resulting in a redirect?
30810 onPasteEvent : function(e,v)
30812 // I think we better assume paste is going to be a dirty load of rubish from word..
30814 // even pasting into a 'email version' of this widget will have to clean up that mess.
30815 var cd = (e.browserEvent.clipboardData || window.clipboardData);
30817 // check what type of paste - if it's an image, then handle it differently.
30818 if (cd.files && cd.files.length > 0) {
30820 var urlAPI = (window.createObjectURL && window) ||
30821 (window.URL && URL.revokeObjectURL && URL) ||
30822 (window.webkitURL && webkitURL);
30824 var url = urlAPI.createObjectURL( cd.files[0]);
30825 this.insertAtCursor('<img src=" + url + ">');
30828 if (cd.types.indexOf('text/html') < 0 ) {
30832 var html = cd.getData('text/html'); // clipboard event
30833 if (cd.types.indexOf('text/rtf') > -1) {
30834 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30835 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30840 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30841 .map(function(g) { return g.toDataURL(); })
30842 .filter(function(g) { return g != 'about:blank'; });
30845 html = this.cleanWordChars(html);
30847 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30850 var sn = this.getParentElement();
30851 // check if d contains a table, and prevent nesting??
30852 //Roo.log(d.getElementsByTagName('table'));
30854 //Roo.log(sn.closest('table'));
30855 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30856 e.preventDefault();
30857 this.insertAtCursor("You can not nest tables");
30858 //Roo.log("prevent?"); // fixme -
30864 if (images.length > 0) {
30865 // replace all v:imagedata - with img.
30866 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30867 Roo.each(ar, function(node) {
30868 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30869 node.parentNode.removeChild(node);
30873 Roo.each(d.getElementsByTagName('img'), function(img, i) {
30874 img.setAttribute('src', images[i]);
30877 if (this.autoClean) {
30878 new Roo.htmleditor.FilterWord({ node : d });
30880 new Roo.htmleditor.FilterStyleToTag({ node : d });
30881 new Roo.htmleditor.FilterAttributes({
30883 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30884 attrib_clean : ['href', 'src' ]
30886 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30887 // should be fonts..
30888 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30889 new Roo.htmleditor.FilterParagraph({ node : d });
30890 new Roo.htmleditor.FilterSpan({ node : d });
30891 new Roo.htmleditor.FilterLongBr({ node : d });
30892 new Roo.htmleditor.FilterComment({ node : d });
30896 if (this.enableBlocks) {
30898 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30899 if (img.closest('figure')) { // assume!! that it's aready
30902 var fig = new Roo.htmleditor.BlockFigure({
30903 image_src : img.src
30905 fig.updateElement(img); // replace it..
30911 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
30912 if (this.enableBlocks) {
30913 Roo.htmleditor.Block.initAll(this.doc.body);
30917 e.preventDefault();
30919 // default behaveiour should be our local cleanup paste? (optional?)
30920 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30921 //this.owner.fireEvent('paste', e, v);
30924 onDestroy : function(){
30930 //for (var i =0; i < this.toolbars.length;i++) {
30931 // // fixme - ask toolbars for heights?
30932 // this.toolbars[i].onDestroy();
30935 //this.wrap.dom.innerHTML = '';
30936 //this.wrap.remove();
30941 onFirstFocus : function(){
30943 this.assignDocWin();
30944 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30946 this.activated = true;
30949 if(Roo.isGecko){ // prevent silly gecko errors
30951 var s = this.win.getSelection();
30952 if(!s.focusNode || s.focusNode.nodeType != 3){
30953 var r = s.getRangeAt(0);
30954 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30959 this.execCmd('useCSS', true);
30960 this.execCmd('styleWithCSS', false);
30963 this.owner.fireEvent('activate', this);
30967 adjustFont: function(btn){
30968 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
30969 //if(Roo.isSafari){ // safari
30972 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
30973 if(Roo.isSafari){ // safari
30974 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
30975 v = (v < 10) ? 10 : v;
30976 v = (v > 48) ? 48 : v;
30977 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
30982 v = Math.max(1, v+adjust);
30984 this.execCmd('FontSize', v );
30987 onEditorEvent : function(e)
30991 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
30992 return; // we do not handle this.. (undo manager does..)
30994 // in theory this detects if the last element is not a br, then we try and do that.
30995 // its so clicking in space at bottom triggers adding a br and moving the cursor.
30997 e.target.nodeName == 'BODY' &&
30998 e.type == "mouseup" &&
30999 this.doc.body.lastChild
31001 var lc = this.doc.body.lastChild;
31002 // gtx-trans is google translate plugin adding crap.
31003 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31004 lc = lc.previousSibling;
31006 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31007 // if last element is <BR> - then dont do anything.
31009 var ns = this.doc.createElement('br');
31010 this.doc.body.appendChild(ns);
31011 range = this.doc.createRange();
31012 range.setStartAfter(ns);
31013 range.collapse(true);
31014 var sel = this.win.getSelection();
31015 sel.removeAllRanges();
31016 sel.addRange(range);
31022 this.fireEditorEvent(e);
31023 // this.updateToolbar();
31024 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31027 fireEditorEvent: function(e)
31029 this.owner.fireEvent('editorevent', this, e);
31032 insertTag : function(tg)
31034 // could be a bit smarter... -> wrap the current selected tRoo..
31035 if (tg.toLowerCase() == 'span' ||
31036 tg.toLowerCase() == 'code' ||
31037 tg.toLowerCase() == 'sup' ||
31038 tg.toLowerCase() == 'sub'
31041 range = this.createRange(this.getSelection());
31042 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31043 wrappingNode.appendChild(range.extractContents());
31044 range.insertNode(wrappingNode);
31051 this.execCmd("formatblock", tg);
31052 this.undoManager.addEvent();
31055 insertText : function(txt)
31059 var range = this.createRange();
31060 range.deleteContents();
31061 //alert(Sender.getAttribute('label'));
31063 range.insertNode(this.doc.createTextNode(txt));
31064 this.undoManager.addEvent();
31070 * Executes a Midas editor command on the editor document and performs necessary focus and
31071 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31072 * @param {String} cmd The Midas command
31073 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31075 relayCmd : function(cmd, value)
31079 case 'justifyleft':
31080 case 'justifyright':
31081 case 'justifycenter':
31082 // if we are in a cell, then we will adjust the
31083 var n = this.getParentElement();
31084 var td = n.closest('td');
31086 var bl = Roo.htmleditor.Block.factory(td);
31087 bl.textAlign = cmd.replace('justify','');
31088 bl.updateElement();
31089 this.owner.fireEvent('editorevent', this);
31092 this.execCmd('styleWithCSS', true); //
31096 // if there is no selection, then we insert, and set the curson inside it..
31097 this.execCmd('styleWithCSS', false);
31107 this.execCmd(cmd, value);
31108 this.owner.fireEvent('editorevent', this);
31109 //this.updateToolbar();
31110 this.owner.deferFocus();
31114 * Executes a Midas editor command directly on the editor document.
31115 * For visual commands, you should use {@link #relayCmd} instead.
31116 * <b>This should only be called after the editor is initialized.</b>
31117 * @param {String} cmd The Midas command
31118 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31120 execCmd : function(cmd, value){
31121 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31128 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31130 * @param {String} text | dom node..
31132 insertAtCursor : function(text)
31135 if(!this.activated){
31139 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31143 // from jquery ui (MIT licenced)
31145 var win = this.win;
31147 if (win.getSelection && win.getSelection().getRangeAt) {
31149 // delete the existing?
31151 this.createRange(this.getSelection()).deleteContents();
31152 range = win.getSelection().getRangeAt(0);
31153 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31154 range.insertNode(node);
31155 range = range.cloneRange();
31156 range.collapse(false);
31158 win.getSelection().removeAllRanges();
31159 win.getSelection().addRange(range);
31163 } else if (win.document.selection && win.document.selection.createRange) {
31164 // no firefox support
31165 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31166 win.document.selection.createRange().pasteHTML(txt);
31169 // no firefox support
31170 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31171 this.execCmd('InsertHTML', txt);
31179 mozKeyPress : function(e){
31181 var c = e.getCharCode(), cmd;
31184 c = String.fromCharCode(c).toLowerCase();
31198 // this.cleanUpPaste.defer(100, this);
31204 this.relayCmd(cmd);
31205 //this.win.focus();
31206 //this.execCmd(cmd);
31207 //this.deferFocus();
31208 e.preventDefault();
31216 fixKeys : function(){ // load time branching for fastest keydown performance
31220 return function(e){
31221 var k = e.getKey(), r;
31224 r = this.doc.selection.createRange();
31227 r.pasteHTML('    ');
31232 /// this is handled by Roo.htmleditor.KeyEnter
31235 r = this.doc.selection.createRange();
31237 var target = r.parentElement();
31238 if(!target || target.tagName.toLowerCase() != 'li'){
31240 r.pasteHTML('<br/>');
31247 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31248 // this.cleanUpPaste.defer(100, this);
31254 }else if(Roo.isOpera){
31255 return function(e){
31256 var k = e.getKey();
31260 this.execCmd('InsertHTML','    ');
31264 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31265 // this.cleanUpPaste.defer(100, this);
31270 }else if(Roo.isSafari){
31271 return function(e){
31272 var k = e.getKey();
31276 this.execCmd('InsertText','\t');
31280 this.mozKeyPress(e);
31282 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31283 // this.cleanUpPaste.defer(100, this);
31291 getAllAncestors: function()
31293 var p = this.getSelectedNode();
31296 a.push(p); // push blank onto stack..
31297 p = this.getParentElement();
31301 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31305 a.push(this.doc.body);
31309 lastSelNode : false,
31312 getSelection : function()
31314 this.assignDocWin();
31315 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31318 * Select a dom node
31319 * @param {DomElement} node the node to select
31321 selectNode : function(node, collapse)
31323 var nodeRange = node.ownerDocument.createRange();
31325 nodeRange.selectNode(node);
31327 nodeRange.selectNodeContents(node);
31329 if (collapse === true) {
31330 nodeRange.collapse(true);
31333 var s = this.win.getSelection();
31334 s.removeAllRanges();
31335 s.addRange(nodeRange);
31338 getSelectedNode: function()
31340 // this may only work on Gecko!!!
31342 // should we cache this!!!!
31346 var range = this.createRange(this.getSelection()).cloneRange();
31349 var parent = range.parentElement();
31351 var testRange = range.duplicate();
31352 testRange.moveToElementText(parent);
31353 if (testRange.inRange(range)) {
31356 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31359 parent = parent.parentElement;
31364 // is ancestor a text element.
31365 var ac = range.commonAncestorContainer;
31366 if (ac.nodeType == 3) {
31367 ac = ac.parentNode;
31370 var ar = ac.childNodes;
31373 var other_nodes = [];
31374 var has_other_nodes = false;
31375 for (var i=0;i<ar.length;i++) {
31376 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31379 // fullly contained node.
31381 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31386 // probably selected..
31387 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31388 other_nodes.push(ar[i]);
31392 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31397 has_other_nodes = true;
31399 if (!nodes.length && other_nodes.length) {
31400 nodes= other_nodes;
31402 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31410 createRange: function(sel)
31412 // this has strange effects when using with
31413 // top toolbar - not sure if it's a great idea.
31414 //this.editor.contentWindow.focus();
31415 if (typeof sel != "undefined") {
31417 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31419 return this.doc.createRange();
31422 return this.doc.createRange();
31425 getParentElement: function()
31428 this.assignDocWin();
31429 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31431 var range = this.createRange(sel);
31434 var p = range.commonAncestorContainer;
31435 while (p.nodeType == 3) { // text node
31446 * Range intersection.. the hard stuff...
31450 * [ -- selected range --- ]
31454 * if end is before start or hits it. fail.
31455 * if start is after end or hits it fail.
31457 * if either hits (but other is outside. - then it's not
31463 // @see http://www.thismuchiknow.co.uk/?p=64.
31464 rangeIntersectsNode : function(range, node)
31466 var nodeRange = node.ownerDocument.createRange();
31468 nodeRange.selectNode(node);
31470 nodeRange.selectNodeContents(node);
31473 var rangeStartRange = range.cloneRange();
31474 rangeStartRange.collapse(true);
31476 var rangeEndRange = range.cloneRange();
31477 rangeEndRange.collapse(false);
31479 var nodeStartRange = nodeRange.cloneRange();
31480 nodeStartRange.collapse(true);
31482 var nodeEndRange = nodeRange.cloneRange();
31483 nodeEndRange.collapse(false);
31485 return rangeStartRange.compareBoundaryPoints(
31486 Range.START_TO_START, nodeEndRange) == -1 &&
31487 rangeEndRange.compareBoundaryPoints(
31488 Range.START_TO_START, nodeStartRange) == 1;
31492 rangeCompareNode : function(range, node)
31494 var nodeRange = node.ownerDocument.createRange();
31496 nodeRange.selectNode(node);
31498 nodeRange.selectNodeContents(node);
31502 range.collapse(true);
31504 nodeRange.collapse(true);
31506 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31507 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
31509 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31511 var nodeIsBefore = ss == 1;
31512 var nodeIsAfter = ee == -1;
31514 if (nodeIsBefore && nodeIsAfter) {
31517 if (!nodeIsBefore && nodeIsAfter) {
31518 return 1; //right trailed.
31521 if (nodeIsBefore && !nodeIsAfter) {
31522 return 2; // left trailed.
31528 cleanWordChars : function(input) {// change the chars to hex code
31531 [ 8211, "–" ],
31532 [ 8212, "—" ],
31540 var output = input;
31541 Roo.each(swapCodes, function(sw) {
31542 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31544 output = output.replace(swapper, sw[1]);
31554 cleanUpChild : function (node)
31557 new Roo.htmleditor.FilterComment({node : node});
31558 new Roo.htmleditor.FilterAttributes({
31560 attrib_black : this.ablack,
31561 attrib_clean : this.aclean,
31562 style_white : this.cwhite,
31563 style_black : this.cblack
31565 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31566 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31572 * Clean up MS wordisms...
31573 * @deprecated - use filter directly
31575 cleanWord : function(node)
31577 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31578 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31585 * @deprecated - use filters
31587 cleanTableWidths : function(node)
31589 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31596 applyBlacklists : function()
31598 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
31599 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
31601 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
31602 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
31603 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
31607 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31608 if (b.indexOf(tag) > -1) {
31611 this.white.push(tag);
31615 Roo.each(w, function(tag) {
31616 if (b.indexOf(tag) > -1) {
31619 if (this.white.indexOf(tag) > -1) {
31622 this.white.push(tag);
31627 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31628 if (w.indexOf(tag) > -1) {
31631 this.black.push(tag);
31635 Roo.each(b, function(tag) {
31636 if (w.indexOf(tag) > -1) {
31639 if (this.black.indexOf(tag) > -1) {
31642 this.black.push(tag);
31647 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
31648 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
31652 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31653 if (b.indexOf(tag) > -1) {
31656 this.cwhite.push(tag);
31660 Roo.each(w, function(tag) {
31661 if (b.indexOf(tag) > -1) {
31664 if (this.cwhite.indexOf(tag) > -1) {
31667 this.cwhite.push(tag);
31672 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31673 if (w.indexOf(tag) > -1) {
31676 this.cblack.push(tag);
31680 Roo.each(b, function(tag) {
31681 if (w.indexOf(tag) > -1) {
31684 if (this.cblack.indexOf(tag) > -1) {
31687 this.cblack.push(tag);
31692 setStylesheets : function(stylesheets)
31694 if(typeof(stylesheets) == 'string'){
31695 Roo.get(this.iframe.contentDocument.head).createChild({
31697 rel : 'stylesheet',
31706 Roo.each(stylesheets, function(s) {
31711 Roo.get(_this.iframe.contentDocument.head).createChild({
31713 rel : 'stylesheet',
31723 updateLanguage : function()
31725 if (!this.iframe || !this.iframe.contentDocument) {
31728 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31732 removeStylesheets : function()
31736 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31741 setStyle : function(style)
31743 Roo.get(this.iframe.contentDocument.head).createChild({
31752 // hide stuff that is not compatible
31766 * @event specialkey
31770 * @cfg {String} fieldClass @hide
31773 * @cfg {String} focusClass @hide
31776 * @cfg {String} autoCreate @hide
31779 * @cfg {String} inputType @hide
31782 * @cfg {String} invalidClass @hide
31785 * @cfg {String} invalidText @hide
31788 * @cfg {String} msgFx @hide
31791 * @cfg {String} validateOnBlur @hide
31795 Roo.HtmlEditorCore.white = [
31796 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31798 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
31799 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
31800 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
31801 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
31802 'TABLE', 'UL', 'XMP',
31804 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
31807 'DIR', 'MENU', 'OL', 'UL', 'DL',
31813 Roo.HtmlEditorCore.black = [
31814 // 'embed', 'object', // enable - backend responsiblity to clean thiese
31816 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
31817 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
31818 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
31819 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
31820 //'FONT' // CLEAN LATER..
31821 'COLGROUP', 'COL' // messy tables.
31825 Roo.HtmlEditorCore.clean = [ // ?? needed???
31826 'SCRIPT', 'STYLE', 'TITLE', 'XML'
31828 Roo.HtmlEditorCore.tag_remove = [
31833 Roo.HtmlEditorCore.ablack = [
31837 Roo.HtmlEditorCore.aclean = [
31838 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
31842 Roo.HtmlEditorCore.pwhite= [
31843 'http', 'https', 'mailto'
31846 // white listed style attributes.
31847 Roo.HtmlEditorCore.cwhite= [
31848 // 'text-align', /// default is to allow most things..
31854 // black listed style attributes.
31855 Roo.HtmlEditorCore.cblack= [
31856 // 'font-size' -- this can be set by the project
31870 * @class Roo.bootstrap.form.HtmlEditor
31871 * @extends Roo.bootstrap.form.TextArea
31872 * Bootstrap HtmlEditor class
31875 * Create a new HtmlEditor
31876 * @param {Object} config The config object
31879 Roo.bootstrap.form.HtmlEditor = function(config){
31880 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31881 if (!this.toolbars) {
31882 this.toolbars = [];
31885 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31888 * @event initialize
31889 * Fires when the editor is fully initialized (including the iframe)
31890 * @param {HtmlEditor} this
31895 * Fires when the editor is first receives the focus. Any insertion must wait
31896 * until after this event.
31897 * @param {HtmlEditor} this
31901 * @event beforesync
31902 * Fires before the textarea is updated with content from the editor iframe. Return false
31903 * to cancel the sync.
31904 * @param {HtmlEditor} this
31905 * @param {String} html
31909 * @event beforepush
31910 * Fires before the iframe editor is updated with content from the textarea. Return false
31911 * to cancel the push.
31912 * @param {HtmlEditor} this
31913 * @param {String} html
31918 * Fires when the textarea is updated with content from the editor iframe.
31919 * @param {HtmlEditor} this
31920 * @param {String} html
31925 * Fires when the iframe editor is updated with content from the textarea.
31926 * @param {HtmlEditor} this
31927 * @param {String} html
31931 * @event editmodechange
31932 * Fires when the editor switches edit modes
31933 * @param {HtmlEditor} this
31934 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31936 editmodechange: true,
31938 * @event editorevent
31939 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31940 * @param {HtmlEditor} this
31944 * @event firstfocus
31945 * Fires when on first focus - needed by toolbars..
31946 * @param {HtmlEditor} this
31951 * Auto save the htmlEditor value as a file into Events
31952 * @param {HtmlEditor} this
31956 * @event savedpreview
31957 * preview the saved version of htmlEditor
31958 * @param {HtmlEditor} this
31965 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
31969 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
31974 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
31979 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
31984 * @cfg {Number} height (in pixels)
31988 * @cfg {Number} width (in pixels)
31993 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
31996 stylesheets: false,
32001 // private properties
32002 validationEvent : false,
32004 initialized : false,
32007 onFocus : Roo.emptyFn,
32009 hideMode:'offsets',
32011 tbContainer : false,
32015 toolbarContainer :function() {
32016 return this.wrap.select('.x-html-editor-tb',true).first();
32020 * Protected method that will not generally be called directly. It
32021 * is called when the editor creates its toolbar. Override this method if you need to
32022 * add custom toolbar buttons.
32023 * @param {HtmlEditor} editor
32025 createToolbar : function(){
32026 Roo.log('renewing');
32027 Roo.log("create toolbars");
32029 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32030 this.toolbars[0].render(this.toolbarContainer());
32034 // if (!editor.toolbars || !editor.toolbars.length) {
32035 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32038 // for (var i =0 ; i < editor.toolbars.length;i++) {
32039 // editor.toolbars[i] = Roo.factory(
32040 // typeof(editor.toolbars[i]) == 'string' ?
32041 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
32042 // Roo.bootstrap.form.HtmlEditor);
32043 // editor.toolbars[i].init(editor);
32049 onRender : function(ct, position)
32051 // Roo.log("Call onRender: " + this.xtype);
32053 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32055 this.wrap = this.inputEl().wrap({
32056 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32059 this.editorcore.onRender(ct, position);
32061 if (this.resizable) {
32062 this.resizeEl = new Roo.Resizable(this.wrap, {
32066 minHeight : this.height,
32067 height: this.height,
32068 handles : this.resizable,
32071 resize : function(r, w, h) {
32072 _t.onResize(w,h); // -something
32078 this.createToolbar(this);
32081 if(!this.width && this.resizable){
32082 this.setSize(this.wrap.getSize());
32084 if (this.resizeEl) {
32085 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32086 // should trigger onReize..
32092 onResize : function(w, h)
32094 Roo.log('resize: ' +w + ',' + h );
32095 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32099 if(this.inputEl() ){
32100 if(typeof w == 'number'){
32101 var aw = w - this.wrap.getFrameWidth('lr');
32102 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32105 if(typeof h == 'number'){
32106 var tbh = -11; // fixme it needs to tool bar size!
32107 for (var i =0; i < this.toolbars.length;i++) {
32108 // fixme - ask toolbars for heights?
32109 tbh += this.toolbars[i].el.getHeight();
32110 //if (this.toolbars[i].footer) {
32111 // tbh += this.toolbars[i].footer.el.getHeight();
32119 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32120 ah -= 5; // knock a few pixes off for look..
32121 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32125 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32126 this.editorcore.onResize(ew,eh);
32131 * Toggles the editor between standard and source edit mode.
32132 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32134 toggleSourceEdit : function(sourceEditMode)
32136 this.editorcore.toggleSourceEdit(sourceEditMode);
32138 if(this.editorcore.sourceEditMode){
32139 Roo.log('editor - showing textarea');
32142 // Roo.log(this.syncValue());
32144 this.inputEl().removeClass(['hide', 'x-hidden']);
32145 this.inputEl().dom.removeAttribute('tabIndex');
32146 this.inputEl().focus();
32148 Roo.log('editor - hiding textarea');
32150 // Roo.log(this.pushValue());
32153 this.inputEl().addClass(['hide', 'x-hidden']);
32154 this.inputEl().dom.setAttribute('tabIndex', -1);
32155 //this.deferFocus();
32158 if(this.resizable){
32159 this.setSize(this.wrap.getSize());
32162 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32165 // private (for BoxComponent)
32166 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32168 // private (for BoxComponent)
32169 getResizeEl : function(){
32173 // private (for BoxComponent)
32174 getPositionEl : function(){
32179 initEvents : function(){
32180 this.originalValue = this.getValue();
32184 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32187 // markInvalid : Roo.emptyFn,
32189 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32192 // clearInvalid : Roo.emptyFn,
32194 setValue : function(v){
32195 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32196 this.editorcore.pushValue();
32201 deferFocus : function(){
32202 this.focus.defer(10, this);
32206 focus : function(){
32207 this.editorcore.focus();
32213 onDestroy : function(){
32219 for (var i =0; i < this.toolbars.length;i++) {
32220 // fixme - ask toolbars for heights?
32221 this.toolbars[i].onDestroy();
32224 this.wrap.dom.innerHTML = '';
32225 this.wrap.remove();
32230 onFirstFocus : function(){
32231 //Roo.log("onFirstFocus");
32232 this.editorcore.onFirstFocus();
32233 for (var i =0; i < this.toolbars.length;i++) {
32234 this.toolbars[i].onFirstFocus();
32240 syncValue : function()
32242 this.editorcore.syncValue();
32245 pushValue : function()
32247 this.editorcore.pushValue();
32251 // hide stuff that is not compatible
32265 * @event specialkey
32269 * @cfg {String} fieldClass @hide
32272 * @cfg {String} focusClass @hide
32275 * @cfg {String} autoCreate @hide
32278 * @cfg {String} inputType @hide
32282 * @cfg {String} invalidText @hide
32285 * @cfg {String} msgFx @hide
32288 * @cfg {String} validateOnBlur @hide
32297 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32299 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32300 * @parent Roo.bootstrap.form.HtmlEditor
32301 * @extends Roo.bootstrap.nav.Simplebar
32307 new Roo.bootstrap.form.HtmlEditor({
32310 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32311 disable : { fonts: 1 , format: 1, ..., ... , ...],
32317 * @cfg {Object} disable List of elements to disable..
32318 * @cfg {Array} btns List of additional buttons.
32322 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32325 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32328 Roo.apply(this, config);
32330 // default disabled, based on 'good practice'..
32331 this.disable = this.disable || {};
32332 Roo.applyIf(this.disable, {
32335 specialElements : true
32337 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32339 this.editor = config.editor;
32340 this.editorcore = config.editor.editorcore;
32342 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32344 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32345 // dont call parent... till later.
32347 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
32352 editorcore : false,
32357 "h1","h2","h3","h4","h5","h6",
32359 "abbr", "acronym", "address", "cite", "samp", "var",
32363 onRender : function(ct, position)
32365 // Roo.log("Call onRender: " + this.xtype);
32367 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32369 this.el.dom.style.marginBottom = '0';
32371 var editorcore = this.editorcore;
32372 var editor= this.editor;
32375 var btn = function(id,cmd , toggle, handler, html){
32377 var event = toggle ? 'toggle' : 'click';
32382 xns: Roo.bootstrap,
32386 enableToggle:toggle !== false,
32388 pressed : toggle ? false : null,
32391 a.listeners[toggle ? 'toggle' : 'click'] = function() {
32392 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
32398 // var cb_box = function...
32403 xns: Roo.bootstrap,
32408 xns: Roo.bootstrap,
32412 Roo.each(this.formats, function(f) {
32413 style.menu.items.push({
32415 xns: Roo.bootstrap,
32416 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32421 editorcore.insertTag(this.tagname);
32428 children.push(style);
32430 btn('bold',false,true);
32431 btn('italic',false,true);
32432 btn('align-left', 'justifyleft',true);
32433 btn('align-center', 'justifycenter',true);
32434 btn('align-right' , 'justifyright',true);
32435 btn('link', false, false, function(btn) {
32436 //Roo.log("create link?");
32437 var url = prompt(this.createLinkText, this.defaultLinkValue);
32438 if(url && url != 'http:/'+'/'){
32439 this.editorcore.relayCmd('createlink', url);
32442 btn('list','insertunorderedlist',true);
32443 btn('pencil', false,true, function(btn){
32445 this.toggleSourceEdit(btn.pressed);
32448 if (this.editor.btns.length > 0) {
32449 for (var i = 0; i<this.editor.btns.length; i++) {
32450 children.push(this.editor.btns[i]);
32458 xns: Roo.bootstrap,
32463 xns: Roo.bootstrap,
32468 cog.menu.items.push({
32470 xns: Roo.bootstrap,
32471 html : Clean styles,
32476 editorcore.insertTag(this.tagname);
32485 this.xtype = 'NavSimplebar';
32487 for(var i=0;i< children.length;i++) {
32489 this.buttons.add(this.addxtypeChild(children[i]));
32493 editor.on('editorevent', this.updateToolbar, this);
32495 onBtnClick : function(id)
32497 this.editorcore.relayCmd(id);
32498 this.editorcore.focus();
32502 * Protected method that will not generally be called directly. It triggers
32503 * a toolbar update by reading the markup state of the current selection in the editor.
32505 updateToolbar: function(){
32507 if(!this.editorcore.activated){
32508 this.editor.onFirstFocus(); // is this neeed?
32512 var btns = this.buttons;
32513 var doc = this.editorcore.doc;
32514 btns.get('bold').setActive(doc.queryCommandState('bold'));
32515 btns.get('italic').setActive(doc.queryCommandState('italic'));
32516 //btns.get('underline').setActive(doc.queryCommandState('underline'));
32518 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32519 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32520 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32522 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32523 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32526 var ans = this.editorcore.getAllAncestors();
32527 if (this.formatCombo) {
32530 var store = this.formatCombo.store;
32531 this.formatCombo.setValue("");
32532 for (var i =0; i < ans.length;i++) {
32533 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32535 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32543 // hides menus... - so this cant be on a menu...
32544 Roo.bootstrap.MenuMgr.hideAll();
32546 Roo.bootstrap.menu.Manager.hideAll();
32547 //this.editorsyncValue();
32549 onFirstFocus: function() {
32550 this.buttons.each(function(item){
32554 toggleSourceEdit : function(sourceEditMode){
32557 if(sourceEditMode){
32558 Roo.log("disabling buttons");
32559 this.buttons.each( function(item){
32560 if(item.cmd != 'pencil'){
32566 Roo.log("enabling buttons");
32567 if(this.editorcore.initialized){
32568 this.buttons.each( function(item){
32574 Roo.log("calling toggole on editor");
32575 // tell the editor that it's been pressed..
32576 this.editor.toggleSourceEdit(sourceEditMode);
32590 * @class Roo.bootstrap.form.Markdown
32591 * @extends Roo.bootstrap.form.TextArea
32592 * Bootstrap Showdown editable area
32593 * @cfg {string} content
32596 * Create a new Showdown
32599 Roo.bootstrap.form.Markdown = function(config){
32600 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32604 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
32608 initEvents : function()
32611 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32612 this.markdownEl = this.el.createChild({
32613 cls : 'roo-markdown-area'
32615 this.inputEl().addClass('d-none');
32616 if (this.getValue() == '') {
32617 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32620 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32622 this.markdownEl.on('click', this.toggleTextEdit, this);
32623 this.on('blur', this.toggleTextEdit, this);
32624 this.on('specialkey', this.resizeTextArea, this);
32627 toggleTextEdit : function()
32629 var sh = this.markdownEl.getHeight();
32630 this.inputEl().addClass('d-none');
32631 this.markdownEl.addClass('d-none');
32632 if (!this.editing) {
32634 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32635 this.inputEl().removeClass('d-none');
32636 this.inputEl().focus();
32637 this.editing = true;
32640 // show showdown...
32641 this.updateMarkdown();
32642 this.markdownEl.removeClass('d-none');
32643 this.editing = false;
32646 updateMarkdown : function()
32648 if (this.getValue() == '') {
32649 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32653 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32656 resizeTextArea: function () {
32659 Roo.log([sh, this.getValue().split("\n").length * 30]);
32660 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32662 setValue : function(val)
32664 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32665 if (!this.editing) {
32666 this.updateMarkdown();
32672 if (!this.editing) {
32673 this.toggleTextEdit();
32681 * Ext JS Library 1.1.1
32682 * Copyright(c) 2006-2007, Ext JS, LLC.
32684 * Originally Released Under LGPL - original licence link has changed is not relivant.
32687 * <script type="text/javascript">
32691 * @class Roo.bootstrap.PagingToolbar
32692 * @extends Roo.bootstrap.nav.Simplebar
32693 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32695 * Create a new PagingToolbar
32696 * @param {Object} config The config object
32697 * @param {Roo.data.Store} store
32699 Roo.bootstrap.PagingToolbar = function(config)
32701 // old args format still supported... - xtype is prefered..
32702 // created from xtype...
32704 this.ds = config.dataSource;
32706 if (config.store && !this.ds) {
32707 this.store= Roo.factory(config.store, Roo.data);
32708 this.ds = this.store;
32709 this.ds.xmodule = this.xmodule || false;
32712 this.toolbarItems = [];
32713 if (config.items) {
32714 this.toolbarItems = config.items;
32717 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32722 this.bind(this.ds);
32725 if (Roo.bootstrap.version == 4) {
32726 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32728 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32733 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32735 * @cfg {Roo.bootstrap.Button} buttons[]
32736 * Buttons for the toolbar
32739 * @cfg {Roo.data.Store} store
32740 * The underlying data store providing the paged data
32743 * @cfg {String/HTMLElement/Element} container
32744 * container The id or element that will contain the toolbar
32747 * @cfg {Boolean} displayInfo
32748 * True to display the displayMsg (defaults to false)
32751 * @cfg {Number} pageSize
32752 * The number of records to display per page (defaults to 20)
32756 * @cfg {String} displayMsg
32757 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32759 displayMsg : 'Displaying {0} - {1} of {2}',
32761 * @cfg {String} emptyMsg
32762 * The message to display when no records are found (defaults to "No data to display")
32764 emptyMsg : 'No data to display',
32766 * Customizable piece of the default paging text (defaults to "Page")
32769 beforePageText : "Page",
32771 * Customizable piece of the default paging text (defaults to "of %0")
32774 afterPageText : "of {0}",
32776 * Customizable piece of the default paging text (defaults to "First Page")
32779 firstText : "First Page",
32781 * Customizable piece of the default paging text (defaults to "Previous Page")
32784 prevText : "Previous Page",
32786 * Customizable piece of the default paging text (defaults to "Next Page")
32789 nextText : "Next Page",
32791 * Customizable piece of the default paging text (defaults to "Last Page")
32794 lastText : "Last Page",
32796 * Customizable piece of the default paging text (defaults to "Refresh")
32799 refreshText : "Refresh",
32803 onRender : function(ct, position)
32805 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32806 this.navgroup.parentId = this.id;
32807 this.navgroup.onRender(this.el, null);
32808 // add the buttons to the navgroup
32810 if(this.displayInfo){
32811 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32812 this.displayEl = this.el.select('.x-paging-info', true).first();
32813 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32814 // this.displayEl = navel.el.select('span',true).first();
32820 Roo.each(_this.buttons, function(e){ // this might need to use render????
32821 Roo.factory(e).render(_this.el);
32825 Roo.each(_this.toolbarItems, function(e) {
32826 _this.navgroup.addItem(e);
32830 this.first = this.navgroup.addItem({
32831 tooltip: this.firstText,
32832 cls: "prev btn-outline-secondary",
32833 html : ' <i class="fa fa-step-backward"></i>',
32835 preventDefault: true,
32836 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32839 this.prev = this.navgroup.addItem({
32840 tooltip: this.prevText,
32841 cls: "prev btn-outline-secondary",
32842 html : ' <i class="fa fa-backward"></i>',
32844 preventDefault: true,
32845 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
32847 //this.addSeparator();
32850 var field = this.navgroup.addItem( {
32852 cls : 'x-paging-position btn-outline-secondary',
32854 html : this.beforePageText +
32855 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32856 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
32859 this.field = field.el.select('input', true).first();
32860 this.field.on("keydown", this.onPagingKeydown, this);
32861 this.field.on("focus", function(){this.dom.select();});
32864 this.afterTextEl = field.el.select('.x-paging-after',true).first();
32865 //this.field.setHeight(18);
32866 //this.addSeparator();
32867 this.next = this.navgroup.addItem({
32868 tooltip: this.nextText,
32869 cls: "next btn-outline-secondary",
32870 html : ' <i class="fa fa-forward"></i>',
32872 preventDefault: true,
32873 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
32875 this.last = this.navgroup.addItem({
32876 tooltip: this.lastText,
32877 html : ' <i class="fa fa-step-forward"></i>',
32878 cls: "next btn-outline-secondary",
32880 preventDefault: true,
32881 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
32883 //this.addSeparator();
32884 this.loading = this.navgroup.addItem({
32885 tooltip: this.refreshText,
32886 cls: "btn-outline-secondary",
32887 html : ' <i class="fa fa-refresh"></i>',
32888 preventDefault: true,
32889 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32895 updateInfo : function(){
32896 if(this.displayEl){
32897 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32898 var msg = count == 0 ?
32902 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32904 this.displayEl.update(msg);
32909 onLoad : function(ds, r, o)
32911 this.cursor = o.params && o.params.start ? o.params.start : 0;
32913 var d = this.getPageData(),
32918 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32919 this.field.dom.value = ap;
32920 this.first.setDisabled(ap == 1);
32921 this.prev.setDisabled(ap == 1);
32922 this.next.setDisabled(ap == ps);
32923 this.last.setDisabled(ap == ps);
32924 this.loading.enable();
32929 getPageData : function(){
32930 var total = this.ds.getTotalCount();
32933 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32934 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32939 onLoadError : function(proxy, o){
32940 this.loading.enable();
32941 if (this.ds.events.loadexception.listeners.length < 2) {
32942 // nothing has been assigned to loadexception except this...
32944 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32950 onPagingKeydown : function(e){
32951 var k = e.getKey();
32952 var d = this.getPageData();
32954 var v = this.field.dom.value, pageNum;
32955 if(!v || isNaN(pageNum = parseInt(v, 10))){
32956 this.field.dom.value = d.activePage;
32959 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32960 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32963 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))
32965 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
32966 this.field.dom.value = pageNum;
32967 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
32970 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
32972 var v = this.field.dom.value, pageNum;
32973 var increment = (e.shiftKey) ? 10 : 1;
32974 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
32977 if(!v || isNaN(pageNum = parseInt(v, 10))) {
32978 this.field.dom.value = d.activePage;
32981 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
32983 this.field.dom.value = parseInt(v, 10) + increment;
32984 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
32985 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
32992 beforeLoad : function(){
32994 this.loading.disable();
32999 onClick : function(which){
33008 ds.load({params:{start: 0, limit: this.pageSize}});
33011 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33014 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33017 var total = ds.getTotalCount();
33018 var extra = total % this.pageSize;
33019 var lastStart = extra ? (total - extra) : total-this.pageSize;
33020 ds.load({params:{start: lastStart, limit: this.pageSize}});
33023 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33029 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33030 * @param {Roo.data.Store} store The data store to unbind
33032 unbind : function(ds){
33033 ds.un("beforeload", this.beforeLoad, this);
33034 ds.un("load", this.onLoad, this);
33035 ds.un("loadexception", this.onLoadError, this);
33036 ds.un("remove", this.updateInfo, this);
33037 ds.un("add", this.updateInfo, this);
33038 this.ds = undefined;
33042 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33043 * @param {Roo.data.Store} store The data store to bind
33045 bind : function(ds){
33046 ds.on("beforeload", this.beforeLoad, this);
33047 ds.on("load", this.onLoad, this);
33048 ds.on("loadexception", this.onLoadError, this);
33049 ds.on("remove", this.updateInfo, this);
33050 ds.on("add", this.updateInfo, this);
33061 * @class Roo.bootstrap.MessageBar
33062 * @extends Roo.bootstrap.Component
33063 * Bootstrap MessageBar class
33064 * @cfg {String} html contents of the MessageBar
33065 * @cfg {String} weight (info | success | warning | danger) default info
33066 * @cfg {String} beforeClass insert the bar before the given class
33067 * @cfg {Boolean} closable (true | false) default false
33068 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33071 * Create a new Element
33072 * @param {Object} config The config object
33075 Roo.bootstrap.MessageBar = function(config){
33076 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33079 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33085 beforeClass: 'bootstrap-sticky-wrap',
33087 getAutoCreate : function(){
33091 cls: 'alert alert-dismissable alert-' + this.weight,
33096 html: this.html || ''
33102 cfg.cls += ' alert-messages-fixed';
33116 onRender : function(ct, position)
33118 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33121 var cfg = Roo.apply({}, this.getAutoCreate());
33125 cfg.cls += ' ' + this.cls;
33128 cfg.style = this.style;
33130 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33132 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33135 this.el.select('>button.close').on('click', this.hide, this);
33141 if (!this.rendered) {
33147 this.fireEvent('show', this);
33153 if (!this.rendered) {
33159 this.fireEvent('hide', this);
33162 update : function()
33164 // var e = this.el.dom.firstChild;
33166 // if(this.closable){
33167 // e = e.nextSibling;
33170 // e.data = this.html || '';
33172 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33188 * @class Roo.bootstrap.Graph
33189 * @extends Roo.bootstrap.Component
33190 * Bootstrap Graph class
33194 @cfg {String} graphtype bar | vbar | pie
33195 @cfg {number} g_x coodinator | centre x (pie)
33196 @cfg {number} g_y coodinator | centre y (pie)
33197 @cfg {number} g_r radius (pie)
33198 @cfg {number} g_height height of the chart (respected by all elements in the set)
33199 @cfg {number} g_width width of the chart (respected by all elements in the set)
33200 @cfg {Object} title The title of the chart
33203 -opts (object) options for the chart
33205 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33206 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33208 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.
33209 o stacked (boolean) whether or not to tread values as in a stacked bar chart
33211 o stretch (boolean)
33213 -opts (object) options for the pie
33216 o startAngle (number)
33217 o endAngle (number)
33221 * Create a new Input
33222 * @param {Object} config The config object
33225 Roo.bootstrap.Graph = function(config){
33226 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33232 * The img click event for the img.
33233 * @param {Roo.EventObject} e
33239 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
33250 //g_colors: this.colors,
33257 getAutoCreate : function(){
33268 onRender : function(ct,position){
33271 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33273 if (typeof(Raphael) == 'undefined') {
33274 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33278 this.raphael = Raphael(this.el.dom);
33280 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33281 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33282 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33283 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33285 r.text(160, 10, "Single Series Chart").attr(txtattr);
33286 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33287 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33288 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33290 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33291 r.barchart(330, 10, 300, 220, data1);
33292 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33293 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33296 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33297 // r.barchart(30, 30, 560, 250, xdata, {
33298 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33299 // axis : "0 0 1 1",
33300 // axisxlabels : xdata
33301 // //yvalues : cols,
33304 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33306 // this.load(null,xdata,{
33307 // axis : "0 0 1 1",
33308 // axisxlabels : xdata
33313 load : function(graphtype,xdata,opts)
33315 this.raphael.clear();
33317 graphtype = this.graphtype;
33322 var r = this.raphael,
33323 fin = function () {
33324 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33326 fout = function () {
33327 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33329 pfin = function() {
33330 this.sector.stop();
33331 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33334 this.label[0].stop();
33335 this.label[0].attr({ r: 7.5 });
33336 this.label[1].attr({ "font-weight": 800 });
33339 pfout = function() {
33340 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33343 this.label[0].animate({ r: 5 }, 500, "bounce");
33344 this.label[1].attr({ "font-weight": 400 });
33350 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33353 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33356 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
33357 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33359 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33366 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33371 setTitle: function(o)
33376 initEvents: function() {
33379 this.el.on('click', this.onClick, this);
33383 onClick : function(e)
33385 Roo.log('img onclick');
33386 this.fireEvent('click', this, e);
33392 Roo.bootstrap.dash = {};/*
33398 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33401 * @class Roo.bootstrap.dash.NumberBox
33402 * @extends Roo.bootstrap.Component
33403 * Bootstrap NumberBox class
33404 * @cfg {String} headline Box headline
33405 * @cfg {String} content Box content
33406 * @cfg {String} icon Box icon
33407 * @cfg {String} footer Footer text
33408 * @cfg {String} fhref Footer href
33411 * Create a new NumberBox
33412 * @param {Object} config The config object
33416 Roo.bootstrap.dash.NumberBox = function(config){
33417 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33421 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
33430 getAutoCreate : function(){
33434 cls : 'small-box ',
33442 cls : 'roo-headline',
33443 html : this.headline
33447 cls : 'roo-content',
33448 html : this.content
33462 cls : 'ion ' + this.icon
33471 cls : 'small-box-footer',
33472 href : this.fhref || '#',
33476 cfg.cn.push(footer);
33483 onRender : function(ct,position){
33484 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33491 setHeadline: function (value)
33493 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33496 setFooter: function (value, href)
33498 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33501 this.el.select('a.small-box-footer',true).first().attr('href', href);
33506 setContent: function (value)
33508 this.el.select('.roo-content',true).first().dom.innerHTML = value;
33511 initEvents: function()
33525 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33528 * @class Roo.bootstrap.dash.TabBox
33529 * @extends Roo.bootstrap.Component
33530 * @children Roo.bootstrap.dash.TabPane
33531 * Bootstrap TabBox class
33532 * @cfg {String} title Title of the TabBox
33533 * @cfg {String} icon Icon of the TabBox
33534 * @cfg {Boolean} showtabs (true|false) show the tabs default true
33535 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33538 * Create a new TabBox
33539 * @param {Object} config The config object
33543 Roo.bootstrap.dash.TabBox = function(config){
33544 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33549 * When a pane is added
33550 * @param {Roo.bootstrap.dash.TabPane} pane
33554 * @event activatepane
33555 * When a pane is activated
33556 * @param {Roo.bootstrap.dash.TabPane} pane
33558 "activatepane" : true
33566 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
33571 tabScrollable : false,
33573 getChildContainer : function()
33575 return this.el.select('.tab-content', true).first();
33578 getAutoCreate : function(){
33582 cls: 'pull-left header',
33590 cls: 'fa ' + this.icon
33596 cls: 'nav nav-tabs pull-right',
33602 if(this.tabScrollable){
33609 cls: 'nav nav-tabs pull-right',
33620 cls: 'nav-tabs-custom',
33625 cls: 'tab-content no-padding',
33633 initEvents : function()
33635 //Roo.log('add add pane handler');
33636 this.on('addpane', this.onAddPane, this);
33639 * Updates the box title
33640 * @param {String} html to set the title to.
33642 setTitle : function(value)
33644 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33646 onAddPane : function(pane)
33648 this.panes.push(pane);
33649 //Roo.log('addpane');
33651 // tabs are rendere left to right..
33652 if(!this.showtabs){
33656 var ctr = this.el.select('.nav-tabs', true).first();
33659 var existing = ctr.select('.nav-tab',true);
33660 var qty = existing.getCount();;
33663 var tab = ctr.createChild({
33665 cls : 'nav-tab' + (qty ? '' : ' active'),
33673 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33676 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33678 pane.el.addClass('active');
33683 onTabClick : function(ev,un,ob,pane)
33685 //Roo.log('tab - prev default');
33686 ev.preventDefault();
33689 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33690 pane.tab.addClass('active');
33691 //Roo.log(pane.title);
33692 this.getChildContainer().select('.tab-pane',true).removeClass('active');
33693 // technically we should have a deactivate event.. but maybe add later.
33694 // and it should not de-activate the selected tab...
33695 this.fireEvent('activatepane', pane);
33696 pane.el.addClass('active');
33697 pane.fireEvent('activate');
33702 getActivePane : function()
33705 Roo.each(this.panes, function(p) {
33706 if(p.el.hasClass('active')){
33727 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33729 * @class Roo.bootstrap.TabPane
33730 * @extends Roo.bootstrap.Component
33731 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
33732 * Bootstrap TabPane class
33733 * @cfg {Boolean} active (false | true) Default false
33734 * @cfg {String} title title of panel
33738 * Create a new TabPane
33739 * @param {Object} config The config object
33742 Roo.bootstrap.dash.TabPane = function(config){
33743 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33749 * When a pane is activated
33750 * @param {Roo.bootstrap.dash.TabPane} pane
33757 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
33762 // the tabBox that this is attached to.
33765 getAutoCreate : function()
33773 cfg.cls += ' active';
33778 initEvents : function()
33780 //Roo.log('trigger add pane handler');
33781 this.parent().fireEvent('addpane', this)
33785 * Updates the tab title
33786 * @param {String} html to set the title to.
33788 setTitle: function(str)
33794 this.tab.select('a', true).first().dom.innerHTML = str;
33813 * @class Roo.bootstrap.Tooltip
33814 * Bootstrap Tooltip class
33815 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33816 * to determine which dom element triggers the tooltip.
33818 * It needs to add support for additional attributes like tooltip-position
33821 * Create a new Toolti
33822 * @param {Object} config The config object
33825 Roo.bootstrap.Tooltip = function(config){
33826 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33828 this.alignment = Roo.bootstrap.Tooltip.alignment;
33830 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33831 this.alignment = config.alignment;
33836 Roo.apply(Roo.bootstrap.Tooltip, {
33838 * @function init initialize tooltip monitoring.
33842 currentTip : false,
33843 currentRegion : false,
33849 Roo.get(document).on('mouseover', this.enter ,this);
33850 Roo.get(document).on('mouseout', this.leave, this);
33853 this.currentTip = new Roo.bootstrap.Tooltip();
33856 enter : function(ev)
33858 var dom = ev.getTarget();
33860 //Roo.log(['enter',dom]);
33861 var el = Roo.fly(dom);
33862 if (this.currentEl) {
33864 //Roo.log(this.currentEl);
33865 //Roo.log(this.currentEl.contains(dom));
33866 if (this.currentEl == el) {
33869 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33875 if (this.currentTip.el) {
33876 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33880 if(!el || el.dom == document){
33886 if (!el.attr('tooltip')) {
33887 pel = el.findParent("[tooltip]");
33889 bindEl = Roo.get(pel);
33895 // you can not look for children, as if el is the body.. then everythign is the child..
33896 if (!pel && !el.attr('tooltip')) { //
33897 if (!el.select("[tooltip]").elements.length) {
33900 // is the mouse over this child...?
33901 bindEl = el.select("[tooltip]").first();
33902 var xy = ev.getXY();
33903 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33904 //Roo.log("not in region.");
33907 //Roo.log("child element over..");
33910 this.currentEl = el;
33911 this.currentTip.bind(bindEl);
33912 this.currentRegion = Roo.lib.Region.getRegion(dom);
33913 this.currentTip.enter();
33916 leave : function(ev)
33918 var dom = ev.getTarget();
33919 //Roo.log(['leave',dom]);
33920 if (!this.currentEl) {
33925 if (dom != this.currentEl.dom) {
33928 var xy = ev.getXY();
33929 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
33932 // only activate leave if mouse cursor is outside... bounding box..
33937 if (this.currentTip) {
33938 this.currentTip.leave();
33940 //Roo.log('clear currentEl');
33941 this.currentEl = false;
33946 'left' : ['r-l', [-2,0], 'right'],
33947 'right' : ['l-r', [2,0], 'left'],
33948 'bottom' : ['t-b', [0,2], 'top'],
33949 'top' : [ 'b-t', [0,-2], 'bottom']
33955 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
33960 delay : null, // can be { show : 300 , hide: 500}
33964 hoverState : null, //???
33966 placement : 'bottom',
33970 getAutoCreate : function(){
33977 cls : 'tooltip-arrow arrow'
33980 cls : 'tooltip-inner'
33987 bind : function(el)
33992 initEvents : function()
33994 this.arrowEl = this.el.select('.arrow', true).first();
33995 this.innerEl = this.el.select('.tooltip-inner', true).first();
33998 enter : function () {
34000 if (this.timeout != null) {
34001 clearTimeout(this.timeout);
34004 this.hoverState = 'in';
34005 //Roo.log("enter - show");
34006 if (!this.delay || !this.delay.show) {
34011 this.timeout = setTimeout(function () {
34012 if (_t.hoverState == 'in') {
34015 }, this.delay.show);
34019 clearTimeout(this.timeout);
34021 this.hoverState = 'out';
34022 if (!this.delay || !this.delay.hide) {
34028 this.timeout = setTimeout(function () {
34029 //Roo.log("leave - timeout");
34031 if (_t.hoverState == 'out') {
34033 Roo.bootstrap.Tooltip.currentEl = false;
34038 show : function (msg)
34041 this.render(document.body);
34044 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34046 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34048 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34050 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34051 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34053 if(this.bindEl.attr('tooltip-class')) {
34054 this.el.addClass(this.bindEl.attr('tooltip-class'));
34057 var placement = typeof this.placement == 'function' ?
34058 this.placement.call(this, this.el, on_el) :
34061 if(this.bindEl.attr('tooltip-placement')) {
34062 placement = this.bindEl.attr('tooltip-placement');
34065 var autoToken = /\s?auto?\s?/i;
34066 var autoPlace = autoToken.test(placement);
34068 placement = placement.replace(autoToken, '') || 'top';
34072 //this.el.setXY([0,0]);
34074 //this.el.dom.style.display='block';
34076 //this.el.appendTo(on_el);
34078 var p = this.getPosition();
34079 var box = this.el.getBox();
34085 var align = this.alignment[placement];
34087 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34089 if(placement == 'top' || placement == 'bottom'){
34091 placement = 'right';
34094 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34095 placement = 'left';
34098 var scroll = Roo.select('body', true).first().getScroll();
34100 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34104 align = this.alignment[placement];
34106 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34110 var elems = document.getElementsByTagName('div');
34111 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34112 for (var i = 0; i < elems.length; i++) {
34113 var zindex = Number.parseInt(
34114 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34117 if (zindex > highest) {
34124 this.el.dom.style.zIndex = highest;
34126 this.el.alignTo(this.bindEl, align[0],align[1]);
34127 //var arrow = this.el.select('.arrow',true).first();
34128 //arrow.set(align[2],
34130 this.el.addClass(placement);
34131 this.el.addClass("bs-tooltip-"+ placement);
34133 this.el.addClass('in fade show');
34135 this.hoverState = null;
34137 if (this.el.hasClass('fade')) {
34152 //this.el.setXY([0,0]);
34153 if(this.bindEl.attr('tooltip-class')) {
34154 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34156 this.el.removeClass(['show', 'in']);
34172 * @class Roo.bootstrap.LocationPicker
34173 * @extends Roo.bootstrap.Component
34174 * Bootstrap LocationPicker class
34175 * @cfg {Number} latitude Position when init default 0
34176 * @cfg {Number} longitude Position when init default 0
34177 * @cfg {Number} zoom default 15
34178 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34179 * @cfg {Boolean} mapTypeControl default false
34180 * @cfg {Boolean} disableDoubleClickZoom default false
34181 * @cfg {Boolean} scrollwheel default true
34182 * @cfg {Boolean} streetViewControl default false
34183 * @cfg {Number} radius default 0
34184 * @cfg {String} locationName
34185 * @cfg {Boolean} draggable default true
34186 * @cfg {Boolean} enableAutocomplete default false
34187 * @cfg {Boolean} enableReverseGeocode default true
34188 * @cfg {String} markerTitle
34191 * Create a new LocationPicker
34192 * @param {Object} config The config object
34196 Roo.bootstrap.LocationPicker = function(config){
34198 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34203 * Fires when the picker initialized.
34204 * @param {Roo.bootstrap.LocationPicker} this
34205 * @param {Google Location} location
34209 * @event positionchanged
34210 * Fires when the picker position changed.
34211 * @param {Roo.bootstrap.LocationPicker} this
34212 * @param {Google Location} location
34214 positionchanged : true,
34217 * Fires when the map resize.
34218 * @param {Roo.bootstrap.LocationPicker} this
34223 * Fires when the map show.
34224 * @param {Roo.bootstrap.LocationPicker} this
34229 * Fires when the map hide.
34230 * @param {Roo.bootstrap.LocationPicker} this
34235 * Fires when click the map.
34236 * @param {Roo.bootstrap.LocationPicker} this
34237 * @param {Map event} e
34241 * @event mapRightClick
34242 * Fires when right click the map.
34243 * @param {Roo.bootstrap.LocationPicker} this
34244 * @param {Map event} e
34246 mapRightClick : true,
34248 * @event markerClick
34249 * Fires when click the marker.
34250 * @param {Roo.bootstrap.LocationPicker} this
34251 * @param {Map event} e
34253 markerClick : true,
34255 * @event markerRightClick
34256 * Fires when right click the marker.
34257 * @param {Roo.bootstrap.LocationPicker} this
34258 * @param {Map event} e
34260 markerRightClick : true,
34262 * @event OverlayViewDraw
34263 * Fires when OverlayView Draw
34264 * @param {Roo.bootstrap.LocationPicker} this
34266 OverlayViewDraw : true,
34268 * @event OverlayViewOnAdd
34269 * Fires when OverlayView Draw
34270 * @param {Roo.bootstrap.LocationPicker} this
34272 OverlayViewOnAdd : true,
34274 * @event OverlayViewOnRemove
34275 * Fires when OverlayView Draw
34276 * @param {Roo.bootstrap.LocationPicker} this
34278 OverlayViewOnRemove : true,
34280 * @event OverlayViewShow
34281 * Fires when OverlayView Draw
34282 * @param {Roo.bootstrap.LocationPicker} this
34283 * @param {Pixel} cpx
34285 OverlayViewShow : true,
34287 * @event OverlayViewHide
34288 * Fires when OverlayView Draw
34289 * @param {Roo.bootstrap.LocationPicker} this
34291 OverlayViewHide : true,
34293 * @event loadexception
34294 * Fires when load google lib failed.
34295 * @param {Roo.bootstrap.LocationPicker} this
34297 loadexception : true
34302 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
34304 gMapContext: false,
34310 mapTypeControl: false,
34311 disableDoubleClickZoom: false,
34313 streetViewControl: false,
34317 enableAutocomplete: false,
34318 enableReverseGeocode: true,
34321 getAutoCreate: function()
34326 cls: 'roo-location-picker'
34332 initEvents: function(ct, position)
34334 if(!this.el.getWidth() || this.isApplied()){
34338 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34343 initial: function()
34345 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34346 this.fireEvent('loadexception', this);
34350 if(!this.mapTypeId){
34351 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34354 this.gMapContext = this.GMapContext();
34356 this.initOverlayView();
34358 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34362 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34363 _this.setPosition(_this.gMapContext.marker.position);
34366 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34367 _this.fireEvent('mapClick', this, event);
34371 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34372 _this.fireEvent('mapRightClick', this, event);
34376 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34377 _this.fireEvent('markerClick', this, event);
34381 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34382 _this.fireEvent('markerRightClick', this, event);
34386 this.setPosition(this.gMapContext.location);
34388 this.fireEvent('initial', this, this.gMapContext.location);
34391 initOverlayView: function()
34395 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34399 _this.fireEvent('OverlayViewDraw', _this);
34404 _this.fireEvent('OverlayViewOnAdd', _this);
34407 onRemove: function()
34409 _this.fireEvent('OverlayViewOnRemove', _this);
34412 show: function(cpx)
34414 _this.fireEvent('OverlayViewShow', _this, cpx);
34419 _this.fireEvent('OverlayViewHide', _this);
34425 fromLatLngToContainerPixel: function(event)
34427 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34430 isApplied: function()
34432 return this.getGmapContext() == false ? false : true;
34435 getGmapContext: function()
34437 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34440 GMapContext: function()
34442 var position = new google.maps.LatLng(this.latitude, this.longitude);
34444 var _map = new google.maps.Map(this.el.dom, {
34447 mapTypeId: this.mapTypeId,
34448 mapTypeControl: this.mapTypeControl,
34449 disableDoubleClickZoom: this.disableDoubleClickZoom,
34450 scrollwheel: this.scrollwheel,
34451 streetViewControl: this.streetViewControl,
34452 locationName: this.locationName,
34453 draggable: this.draggable,
34454 enableAutocomplete: this.enableAutocomplete,
34455 enableReverseGeocode: this.enableReverseGeocode
34458 var _marker = new google.maps.Marker({
34459 position: position,
34461 title: this.markerTitle,
34462 draggable: this.draggable
34469 location: position,
34470 radius: this.radius,
34471 locationName: this.locationName,
34472 addressComponents: {
34473 formatted_address: null,
34474 addressLine1: null,
34475 addressLine2: null,
34477 streetNumber: null,
34481 stateOrProvince: null
34484 domContainer: this.el.dom,
34485 geodecoder: new google.maps.Geocoder()
34489 drawCircle: function(center, radius, options)
34491 if (this.gMapContext.circle != null) {
34492 this.gMapContext.circle.setMap(null);
34496 options = Roo.apply({}, options, {
34497 strokeColor: "#0000FF",
34498 strokeOpacity: .35,
34500 fillColor: "#0000FF",
34504 options.map = this.gMapContext.map;
34505 options.radius = radius;
34506 options.center = center;
34507 this.gMapContext.circle = new google.maps.Circle(options);
34508 return this.gMapContext.circle;
34514 setPosition: function(location)
34516 this.gMapContext.location = location;
34517 this.gMapContext.marker.setPosition(location);
34518 this.gMapContext.map.panTo(location);
34519 this.drawCircle(location, this.gMapContext.radius, {});
34523 if (this.gMapContext.settings.enableReverseGeocode) {
34524 this.gMapContext.geodecoder.geocode({
34525 latLng: this.gMapContext.location
34526 }, function(results, status) {
34528 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34529 _this.gMapContext.locationName = results[0].formatted_address;
34530 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34532 _this.fireEvent('positionchanged', this, location);
34539 this.fireEvent('positionchanged', this, location);
34544 google.maps.event.trigger(this.gMapContext.map, "resize");
34546 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34548 this.fireEvent('resize', this);
34551 setPositionByLatLng: function(latitude, longitude)
34553 this.setPosition(new google.maps.LatLng(latitude, longitude));
34556 getCurrentPosition: function()
34559 latitude: this.gMapContext.location.lat(),
34560 longitude: this.gMapContext.location.lng()
34564 getAddressName: function()
34566 return this.gMapContext.locationName;
34569 getAddressComponents: function()
34571 return this.gMapContext.addressComponents;
34574 address_component_from_google_geocode: function(address_components)
34578 for (var i = 0; i < address_components.length; i++) {
34579 var component = address_components[i];
34580 if (component.types.indexOf("postal_code") >= 0) {
34581 result.postalCode = component.short_name;
34582 } else if (component.types.indexOf("street_number") >= 0) {
34583 result.streetNumber = component.short_name;
34584 } else if (component.types.indexOf("route") >= 0) {
34585 result.streetName = component.short_name;
34586 } else if (component.types.indexOf("neighborhood") >= 0) {
34587 result.city = component.short_name;
34588 } else if (component.types.indexOf("locality") >= 0) {
34589 result.city = component.short_name;
34590 } else if (component.types.indexOf("sublocality") >= 0) {
34591 result.district = component.short_name;
34592 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34593 result.stateOrProvince = component.short_name;
34594 } else if (component.types.indexOf("country") >= 0) {
34595 result.country = component.short_name;
34599 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34600 result.addressLine2 = "";
34604 setZoomLevel: function(zoom)
34606 this.gMapContext.map.setZoom(zoom);
34619 this.fireEvent('show', this);
34630 this.fireEvent('hide', this);
34635 Roo.apply(Roo.bootstrap.LocationPicker, {
34637 OverlayView : function(map, options)
34639 options = options || {};
34646 * @class Roo.bootstrap.Alert
34647 * @extends Roo.bootstrap.Component
34648 * Bootstrap Alert class - shows an alert area box
34650 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34651 Enter a valid email address
34654 * @cfg {String} title The title of alert
34655 * @cfg {String} html The content of alert
34656 * @cfg {String} weight (success|info|warning|danger) Weight of the message
34657 * @cfg {String} fa font-awesomeicon
34658 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34659 * @cfg {Boolean} close true to show a x closer
34663 * Create a new alert
34664 * @param {Object} config The config object
34668 Roo.bootstrap.Alert = function(config){
34669 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34673 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
34679 faicon: false, // BC
34683 getAutoCreate : function()
34695 style : this.close ? '' : 'display:none'
34699 cls : 'roo-alert-icon'
34704 cls : 'roo-alert-title',
34709 cls : 'roo-alert-text',
34716 cfg.cn[0].cls += ' fa ' + this.faicon;
34719 cfg.cn[0].cls += ' fa ' + this.fa;
34723 cfg.cls += ' alert-' + this.weight;
34729 initEvents: function()
34731 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34732 this.titleEl = this.el.select('.roo-alert-title',true).first();
34733 this.iconEl = this.el.select('.roo-alert-icon',true).first();
34734 this.htmlEl = this.el.select('.roo-alert-text',true).first();
34735 if (this.seconds > 0) {
34736 this.hide.defer(this.seconds, this);
34740 * Set the Title Message HTML
34741 * @param {String} html
34743 setTitle : function(str)
34745 this.titleEl.dom.innerHTML = str;
34749 * Set the Body Message HTML
34750 * @param {String} html
34752 setHtml : function(str)
34754 this.htmlEl.dom.innerHTML = str;
34757 * Set the Weight of the alert
34758 * @param {String} (success|info|warning|danger) weight
34761 setWeight : function(weight)
34764 this.el.removeClass('alert-' + this.weight);
34767 this.weight = weight;
34769 this.el.addClass('alert-' + this.weight);
34772 * Set the Icon of the alert
34773 * @param {String} see fontawsome names (name without the 'fa-' bit)
34775 setIcon : function(icon)
34778 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34781 this.faicon = icon;
34783 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34808 * @class Roo.bootstrap.UploadCropbox
34809 * @extends Roo.bootstrap.Component
34810 * Bootstrap UploadCropbox class
34811 * @cfg {String} emptyText show when image has been loaded
34812 * @cfg {String} rotateNotify show when image too small to rotate
34813 * @cfg {Number} errorTimeout default 3000
34814 * @cfg {Number} minWidth default 300
34815 * @cfg {Number} minHeight default 300
34816 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34817 * @cfg {Boolean} isDocument (true|false) default false
34818 * @cfg {String} url action url
34819 * @cfg {String} paramName default 'imageUpload'
34820 * @cfg {String} method default POST
34821 * @cfg {Boolean} loadMask (true|false) default true
34822 * @cfg {Boolean} loadingText default 'Loading...'
34825 * Create a new UploadCropbox
34826 * @param {Object} config The config object
34829 Roo.bootstrap.UploadCropbox = function(config){
34830 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34834 * @event beforeselectfile
34835 * Fire before select file
34836 * @param {Roo.bootstrap.UploadCropbox} this
34838 "beforeselectfile" : true,
34841 * Fire after initEvent
34842 * @param {Roo.bootstrap.UploadCropbox} this
34847 * Fire after initEvent
34848 * @param {Roo.bootstrap.UploadCropbox} this
34849 * @param {String} data
34854 * Fire when preparing the file data
34855 * @param {Roo.bootstrap.UploadCropbox} this
34856 * @param {Object} file
34861 * Fire when get exception
34862 * @param {Roo.bootstrap.UploadCropbox} this
34863 * @param {XMLHttpRequest} xhr
34865 "exception" : true,
34867 * @event beforeloadcanvas
34868 * Fire before load the canvas
34869 * @param {Roo.bootstrap.UploadCropbox} this
34870 * @param {String} src
34872 "beforeloadcanvas" : true,
34875 * Fire when trash image
34876 * @param {Roo.bootstrap.UploadCropbox} this
34881 * Fire when download the image
34882 * @param {Roo.bootstrap.UploadCropbox} this
34886 * @event footerbuttonclick
34887 * Fire when footerbuttonclick
34888 * @param {Roo.bootstrap.UploadCropbox} this
34889 * @param {String} type
34891 "footerbuttonclick" : true,
34895 * @param {Roo.bootstrap.UploadCropbox} this
34900 * Fire when rotate the image
34901 * @param {Roo.bootstrap.UploadCropbox} this
34902 * @param {String} pos
34907 * Fire when inspect the file
34908 * @param {Roo.bootstrap.UploadCropbox} this
34909 * @param {Object} file
34914 * Fire when xhr upload the file
34915 * @param {Roo.bootstrap.UploadCropbox} this
34916 * @param {Object} data
34921 * Fire when arrange the file data
34922 * @param {Roo.bootstrap.UploadCropbox} this
34923 * @param {Object} formData
34928 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34931 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
34933 emptyText : 'Click to upload image',
34934 rotateNotify : 'Image is too small to rotate',
34935 errorTimeout : 3000,
34949 cropType : 'image/jpeg',
34951 canvasLoaded : false,
34952 isDocument : false,
34954 paramName : 'imageUpload',
34956 loadingText : 'Loading...',
34959 getAutoCreate : function()
34963 cls : 'roo-upload-cropbox',
34967 cls : 'roo-upload-cropbox-selector',
34972 cls : 'roo-upload-cropbox-body',
34973 style : 'cursor:pointer',
34977 cls : 'roo-upload-cropbox-preview'
34981 cls : 'roo-upload-cropbox-thumb'
34985 cls : 'roo-upload-cropbox-empty-notify',
34986 html : this.emptyText
34990 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
34991 html : this.rotateNotify
34997 cls : 'roo-upload-cropbox-footer',
35000 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35010 onRender : function(ct, position)
35012 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35014 if (this.buttons.length) {
35016 Roo.each(this.buttons, function(bb) {
35018 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35020 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35026 this.maskEl = this.el;
35030 initEvents : function()
35032 this.urlAPI = (window.createObjectURL && window) ||
35033 (window.URL && URL.revokeObjectURL && URL) ||
35034 (window.webkitURL && webkitURL);
35036 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35037 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35039 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35040 this.selectorEl.hide();
35042 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35043 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35045 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35046 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35047 this.thumbEl.hide();
35049 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35050 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35052 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35053 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35054 this.errorEl.hide();
35056 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35057 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35058 this.footerEl.hide();
35060 this.setThumbBoxSize();
35066 this.fireEvent('initial', this);
35073 window.addEventListener("resize", function() { _this.resize(); } );
35075 this.bodyEl.on('click', this.beforeSelectFile, this);
35078 this.bodyEl.on('touchstart', this.onTouchStart, this);
35079 this.bodyEl.on('touchmove', this.onTouchMove, this);
35080 this.bodyEl.on('touchend', this.onTouchEnd, this);
35084 this.bodyEl.on('mousedown', this.onMouseDown, this);
35085 this.bodyEl.on('mousemove', this.onMouseMove, this);
35086 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35087 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35088 Roo.get(document).on('mouseup', this.onMouseUp, this);
35091 this.selectorEl.on('change', this.onFileSelected, this);
35097 this.baseScale = 1;
35099 this.baseRotate = 1;
35100 this.dragable = false;
35101 this.pinching = false;
35104 this.cropData = false;
35105 this.notifyEl.dom.innerHTML = this.emptyText;
35107 this.selectorEl.dom.value = '';
35111 resize : function()
35113 if(this.fireEvent('resize', this) != false){
35114 this.setThumbBoxPosition();
35115 this.setCanvasPosition();
35119 onFooterButtonClick : function(e, el, o, type)
35122 case 'rotate-left' :
35123 this.onRotateLeft(e);
35125 case 'rotate-right' :
35126 this.onRotateRight(e);
35129 this.beforeSelectFile(e);
35144 this.fireEvent('footerbuttonclick', this, type);
35147 beforeSelectFile : function(e)
35149 e.preventDefault();
35151 if(this.fireEvent('beforeselectfile', this) != false){
35152 this.selectorEl.dom.click();
35156 onFileSelected : function(e)
35158 e.preventDefault();
35160 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35164 var file = this.selectorEl.dom.files[0];
35166 if(this.fireEvent('inspect', this, file) != false){
35167 this.prepare(file);
35172 trash : function(e)
35174 this.fireEvent('trash', this);
35177 download : function(e)
35179 this.fireEvent('download', this);
35182 loadCanvas : function(src)
35184 if(this.fireEvent('beforeloadcanvas', this, src) != false){
35188 this.imageEl = document.createElement('img');
35192 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35194 this.imageEl.src = src;
35198 onLoadCanvas : function()
35200 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35201 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35203 this.bodyEl.un('click', this.beforeSelectFile, this);
35205 this.notifyEl.hide();
35206 this.thumbEl.show();
35207 this.footerEl.show();
35209 this.baseRotateLevel();
35211 if(this.isDocument){
35212 this.setThumbBoxSize();
35215 this.setThumbBoxPosition();
35217 this.baseScaleLevel();
35223 this.canvasLoaded = true;
35226 this.maskEl.unmask();
35231 setCanvasPosition : function()
35233 if(!this.canvasEl){
35237 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35238 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35240 this.previewEl.setLeft(pw);
35241 this.previewEl.setTop(ph);
35245 onMouseDown : function(e)
35249 this.dragable = true;
35250 this.pinching = false;
35252 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35253 this.dragable = false;
35257 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35258 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35262 onMouseMove : function(e)
35266 if(!this.canvasLoaded){
35270 if (!this.dragable){
35274 var minX = Math.ceil(this.thumbEl.getLeft(true));
35275 var minY = Math.ceil(this.thumbEl.getTop(true));
35277 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35278 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35280 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35281 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35283 x = x - this.mouseX;
35284 y = y - this.mouseY;
35286 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35287 var bgY = Math.ceil(y + this.previewEl.getTop(true));
35289 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35290 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35292 this.previewEl.setLeft(bgX);
35293 this.previewEl.setTop(bgY);
35295 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35296 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35299 onMouseUp : function(e)
35303 this.dragable = false;
35306 onMouseWheel : function(e)
35310 this.startScale = this.scale;
35312 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35314 if(!this.zoomable()){
35315 this.scale = this.startScale;
35324 zoomable : function()
35326 var minScale = this.thumbEl.getWidth() / this.minWidth;
35328 if(this.minWidth < this.minHeight){
35329 minScale = this.thumbEl.getHeight() / this.minHeight;
35332 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35333 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35337 (this.rotate == 0 || this.rotate == 180) &&
35339 width > this.imageEl.OriginWidth ||
35340 height > this.imageEl.OriginHeight ||
35341 (width < this.minWidth && height < this.minHeight)
35349 (this.rotate == 90 || this.rotate == 270) &&
35351 width > this.imageEl.OriginWidth ||
35352 height > this.imageEl.OriginHeight ||
35353 (width < this.minHeight && height < this.minWidth)
35360 !this.isDocument &&
35361 (this.rotate == 0 || this.rotate == 180) &&
35363 width < this.minWidth ||
35364 width > this.imageEl.OriginWidth ||
35365 height < this.minHeight ||
35366 height > this.imageEl.OriginHeight
35373 !this.isDocument &&
35374 (this.rotate == 90 || this.rotate == 270) &&
35376 width < this.minHeight ||
35377 width > this.imageEl.OriginWidth ||
35378 height < this.minWidth ||
35379 height > this.imageEl.OriginHeight
35389 onRotateLeft : function(e)
35391 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35393 var minScale = this.thumbEl.getWidth() / this.minWidth;
35395 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35396 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35398 this.startScale = this.scale;
35400 while (this.getScaleLevel() < minScale){
35402 this.scale = this.scale + 1;
35404 if(!this.zoomable()){
35409 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35410 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35415 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35422 this.scale = this.startScale;
35424 this.onRotateFail();
35429 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35431 if(this.isDocument){
35432 this.setThumbBoxSize();
35433 this.setThumbBoxPosition();
35434 this.setCanvasPosition();
35439 this.fireEvent('rotate', this, 'left');
35443 onRotateRight : function(e)
35445 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35447 var minScale = this.thumbEl.getWidth() / this.minWidth;
35449 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35450 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35452 this.startScale = this.scale;
35454 while (this.getScaleLevel() < minScale){
35456 this.scale = this.scale + 1;
35458 if(!this.zoomable()){
35463 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35464 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35469 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35476 this.scale = this.startScale;
35478 this.onRotateFail();
35483 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35485 if(this.isDocument){
35486 this.setThumbBoxSize();
35487 this.setThumbBoxPosition();
35488 this.setCanvasPosition();
35493 this.fireEvent('rotate', this, 'right');
35496 onRotateFail : function()
35498 this.errorEl.show(true);
35502 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35507 this.previewEl.dom.innerHTML = '';
35509 var canvasEl = document.createElement("canvas");
35511 var contextEl = canvasEl.getContext("2d");
35513 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35514 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35515 var center = this.imageEl.OriginWidth / 2;
35517 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35518 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35519 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35520 center = this.imageEl.OriginHeight / 2;
35523 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35525 contextEl.translate(center, center);
35526 contextEl.rotate(this.rotate * Math.PI / 180);
35528 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35530 this.canvasEl = document.createElement("canvas");
35532 this.contextEl = this.canvasEl.getContext("2d");
35534 switch (this.rotate) {
35537 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35538 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35540 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35545 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35546 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35548 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35549 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);
35553 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35558 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35559 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35561 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35562 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);
35566 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);
35571 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35572 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35574 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35575 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35579 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);
35586 this.previewEl.appendChild(this.canvasEl);
35588 this.setCanvasPosition();
35593 if(!this.canvasLoaded){
35597 var imageCanvas = document.createElement("canvas");
35599 var imageContext = imageCanvas.getContext("2d");
35601 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35602 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35604 var center = imageCanvas.width / 2;
35606 imageContext.translate(center, center);
35608 imageContext.rotate(this.rotate * Math.PI / 180);
35610 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35612 var canvas = document.createElement("canvas");
35614 var context = canvas.getContext("2d");
35616 canvas.width = this.minWidth;
35617 canvas.height = this.minHeight;
35619 switch (this.rotate) {
35622 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35623 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35625 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35626 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35628 var targetWidth = this.minWidth - 2 * x;
35629 var targetHeight = this.minHeight - 2 * y;
35633 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35634 scale = targetWidth / width;
35637 if(x > 0 && y == 0){
35638 scale = targetHeight / height;
35641 if(x > 0 && y > 0){
35642 scale = targetWidth / width;
35644 if(width < height){
35645 scale = targetHeight / height;
35649 context.scale(scale, scale);
35651 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35652 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35654 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35655 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35657 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35662 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35663 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35665 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35666 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35668 var targetWidth = this.minWidth - 2 * x;
35669 var targetHeight = this.minHeight - 2 * y;
35673 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35674 scale = targetWidth / width;
35677 if(x > 0 && y == 0){
35678 scale = targetHeight / height;
35681 if(x > 0 && y > 0){
35682 scale = targetWidth / width;
35684 if(width < height){
35685 scale = targetHeight / height;
35689 context.scale(scale, scale);
35691 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35692 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35694 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35695 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35697 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35699 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35704 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35705 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35707 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35708 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35710 var targetWidth = this.minWidth - 2 * x;
35711 var targetHeight = this.minHeight - 2 * y;
35715 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35716 scale = targetWidth / width;
35719 if(x > 0 && y == 0){
35720 scale = targetHeight / height;
35723 if(x > 0 && y > 0){
35724 scale = targetWidth / width;
35726 if(width < height){
35727 scale = targetHeight / height;
35731 context.scale(scale, scale);
35733 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35734 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35736 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35737 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35739 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35740 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35742 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35747 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35748 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35750 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35751 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35753 var targetWidth = this.minWidth - 2 * x;
35754 var targetHeight = this.minHeight - 2 * y;
35758 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35759 scale = targetWidth / width;
35762 if(x > 0 && y == 0){
35763 scale = targetHeight / height;
35766 if(x > 0 && y > 0){
35767 scale = targetWidth / width;
35769 if(width < height){
35770 scale = targetHeight / height;
35774 context.scale(scale, scale);
35776 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35777 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35779 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35780 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35782 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35784 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35791 this.cropData = canvas.toDataURL(this.cropType);
35793 if(this.fireEvent('crop', this, this.cropData) !== false){
35794 this.process(this.file, this.cropData);
35801 setThumbBoxSize : function()
35805 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35806 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35807 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35809 this.minWidth = width;
35810 this.minHeight = height;
35812 if(this.rotate == 90 || this.rotate == 270){
35813 this.minWidth = height;
35814 this.minHeight = width;
35819 width = Math.ceil(this.minWidth * height / this.minHeight);
35821 if(this.minWidth > this.minHeight){
35823 height = Math.ceil(this.minHeight * width / this.minWidth);
35826 this.thumbEl.setStyle({
35827 width : width + 'px',
35828 height : height + 'px'
35835 setThumbBoxPosition : function()
35837 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35838 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35840 this.thumbEl.setLeft(x);
35841 this.thumbEl.setTop(y);
35845 baseRotateLevel : function()
35847 this.baseRotate = 1;
35850 typeof(this.exif) != 'undefined' &&
35851 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35852 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35854 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35857 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35861 baseScaleLevel : function()
35865 if(this.isDocument){
35867 if(this.baseRotate == 6 || this.baseRotate == 8){
35869 height = this.thumbEl.getHeight();
35870 this.baseScale = height / this.imageEl.OriginWidth;
35872 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35873 width = this.thumbEl.getWidth();
35874 this.baseScale = width / this.imageEl.OriginHeight;
35880 height = this.thumbEl.getHeight();
35881 this.baseScale = height / this.imageEl.OriginHeight;
35883 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35884 width = this.thumbEl.getWidth();
35885 this.baseScale = width / this.imageEl.OriginWidth;
35891 if(this.baseRotate == 6 || this.baseRotate == 8){
35893 width = this.thumbEl.getHeight();
35894 this.baseScale = width / this.imageEl.OriginHeight;
35896 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35897 height = this.thumbEl.getWidth();
35898 this.baseScale = height / this.imageEl.OriginHeight;
35901 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35902 height = this.thumbEl.getWidth();
35903 this.baseScale = height / this.imageEl.OriginHeight;
35905 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35906 width = this.thumbEl.getHeight();
35907 this.baseScale = width / this.imageEl.OriginWidth;
35914 width = this.thumbEl.getWidth();
35915 this.baseScale = width / this.imageEl.OriginWidth;
35917 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35918 height = this.thumbEl.getHeight();
35919 this.baseScale = height / this.imageEl.OriginHeight;
35922 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35924 height = this.thumbEl.getHeight();
35925 this.baseScale = height / this.imageEl.OriginHeight;
35927 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35928 width = this.thumbEl.getWidth();
35929 this.baseScale = width / this.imageEl.OriginWidth;
35937 getScaleLevel : function()
35939 return this.baseScale * Math.pow(1.1, this.scale);
35942 onTouchStart : function(e)
35944 if(!this.canvasLoaded){
35945 this.beforeSelectFile(e);
35949 var touches = e.browserEvent.touches;
35955 if(touches.length == 1){
35956 this.onMouseDown(e);
35960 if(touches.length != 2){
35966 for(var i = 0, finger; finger = touches[i]; i++){
35967 coords.push(finger.pageX, finger.pageY);
35970 var x = Math.pow(coords[0] - coords[2], 2);
35971 var y = Math.pow(coords[1] - coords[3], 2);
35973 this.startDistance = Math.sqrt(x + y);
35975 this.startScale = this.scale;
35977 this.pinching = true;
35978 this.dragable = false;
35982 onTouchMove : function(e)
35984 if(!this.pinching && !this.dragable){
35988 var touches = e.browserEvent.touches;
35995 this.onMouseMove(e);
36001 for(var i = 0, finger; finger = touches[i]; i++){
36002 coords.push(finger.pageX, finger.pageY);
36005 var x = Math.pow(coords[0] - coords[2], 2);
36006 var y = Math.pow(coords[1] - coords[3], 2);
36008 this.endDistance = Math.sqrt(x + y);
36010 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36012 if(!this.zoomable()){
36013 this.scale = this.startScale;
36021 onTouchEnd : function(e)
36023 this.pinching = false;
36024 this.dragable = false;
36028 process : function(file, crop)
36031 this.maskEl.mask(this.loadingText);
36034 this.xhr = new XMLHttpRequest();
36036 file.xhr = this.xhr;
36038 this.xhr.open(this.method, this.url, true);
36041 "Accept": "application/json",
36042 "Cache-Control": "no-cache",
36043 "X-Requested-With": "XMLHttpRequest"
36046 for (var headerName in headers) {
36047 var headerValue = headers[headerName];
36049 this.xhr.setRequestHeader(headerName, headerValue);
36055 this.xhr.onload = function()
36057 _this.xhrOnLoad(_this.xhr);
36060 this.xhr.onerror = function()
36062 _this.xhrOnError(_this.xhr);
36065 var formData = new FormData();
36067 formData.append('returnHTML', 'NO');
36070 formData.append('crop', crop);
36073 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36074 formData.append(this.paramName, file, file.name);
36077 if(typeof(file.filename) != 'undefined'){
36078 formData.append('filename', file.filename);
36081 if(typeof(file.mimetype) != 'undefined'){
36082 formData.append('mimetype', file.mimetype);
36085 if(this.fireEvent('arrange', this, formData) != false){
36086 this.xhr.send(formData);
36090 xhrOnLoad : function(xhr)
36093 this.maskEl.unmask();
36096 if (xhr.readyState !== 4) {
36097 this.fireEvent('exception', this, xhr);
36101 var response = Roo.decode(xhr.responseText);
36103 if(!response.success){
36104 this.fireEvent('exception', this, xhr);
36108 var response = Roo.decode(xhr.responseText);
36110 this.fireEvent('upload', this, response);
36114 xhrOnError : function()
36117 this.maskEl.unmask();
36120 Roo.log('xhr on error');
36122 var response = Roo.decode(xhr.responseText);
36128 prepare : function(file)
36131 this.maskEl.mask(this.loadingText);
36137 if(typeof(file) === 'string'){
36138 this.loadCanvas(file);
36142 if(!file || !this.urlAPI){
36147 this.cropType = file.type;
36151 if(this.fireEvent('prepare', this, this.file) != false){
36153 var reader = new FileReader();
36155 reader.onload = function (e) {
36156 if (e.target.error) {
36157 Roo.log(e.target.error);
36161 var buffer = e.target.result,
36162 dataView = new DataView(buffer),
36164 maxOffset = dataView.byteLength - 4,
36168 if (dataView.getUint16(0) === 0xffd8) {
36169 while (offset < maxOffset) {
36170 markerBytes = dataView.getUint16(offset);
36172 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36173 markerLength = dataView.getUint16(offset + 2) + 2;
36174 if (offset + markerLength > dataView.byteLength) {
36175 Roo.log('Invalid meta data: Invalid segment size.');
36179 if(markerBytes == 0xffe1){
36180 _this.parseExifData(
36187 offset += markerLength;
36197 var url = _this.urlAPI.createObjectURL(_this.file);
36199 _this.loadCanvas(url);
36204 reader.readAsArrayBuffer(this.file);
36210 parseExifData : function(dataView, offset, length)
36212 var tiffOffset = offset + 10,
36216 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36217 // No Exif data, might be XMP data instead
36221 // Check for the ASCII code for "Exif" (0x45786966):
36222 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36223 // No Exif data, might be XMP data instead
36226 if (tiffOffset + 8 > dataView.byteLength) {
36227 Roo.log('Invalid Exif data: Invalid segment size.');
36230 // Check for the two null bytes:
36231 if (dataView.getUint16(offset + 8) !== 0x0000) {
36232 Roo.log('Invalid Exif data: Missing byte alignment offset.');
36235 // Check the byte alignment:
36236 switch (dataView.getUint16(tiffOffset)) {
36238 littleEndian = true;
36241 littleEndian = false;
36244 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36247 // Check for the TIFF tag marker (0x002A):
36248 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36249 Roo.log('Invalid Exif data: Missing TIFF marker.');
36252 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36253 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36255 this.parseExifTags(
36258 tiffOffset + dirOffset,
36263 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36268 if (dirOffset + 6 > dataView.byteLength) {
36269 Roo.log('Invalid Exif data: Invalid directory offset.');
36272 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36273 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36274 if (dirEndOffset + 4 > dataView.byteLength) {
36275 Roo.log('Invalid Exif data: Invalid directory size.');
36278 for (i = 0; i < tagsNumber; i += 1) {
36282 dirOffset + 2 + 12 * i, // tag offset
36286 // Return the offset to the next directory:
36287 return dataView.getUint32(dirEndOffset, littleEndian);
36290 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
36292 var tag = dataView.getUint16(offset, littleEndian);
36294 this.exif[tag] = this.getExifValue(
36298 dataView.getUint16(offset + 2, littleEndian), // tag type
36299 dataView.getUint32(offset + 4, littleEndian), // tag length
36304 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36306 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36315 Roo.log('Invalid Exif data: Invalid tag type.');
36319 tagSize = tagType.size * length;
36320 // Determine if the value is contained in the dataOffset bytes,
36321 // or if the value at the dataOffset is a pointer to the actual data:
36322 dataOffset = tagSize > 4 ?
36323 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36324 if (dataOffset + tagSize > dataView.byteLength) {
36325 Roo.log('Invalid Exif data: Invalid data offset.');
36328 if (length === 1) {
36329 return tagType.getValue(dataView, dataOffset, littleEndian);
36332 for (i = 0; i < length; i += 1) {
36333 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36336 if (tagType.ascii) {
36338 // Concatenate the chars:
36339 for (i = 0; i < values.length; i += 1) {
36341 // Ignore the terminating NULL byte(s):
36342 if (c === '\u0000') {
36354 Roo.apply(Roo.bootstrap.UploadCropbox, {
36356 'Orientation': 0x0112
36360 1: 0, //'top-left',
36362 3: 180, //'bottom-right',
36363 // 4: 'bottom-left',
36365 6: 90, //'right-top',
36366 // 7: 'right-bottom',
36367 8: 270 //'left-bottom'
36371 // byte, 8-bit unsigned int:
36373 getValue: function (dataView, dataOffset) {
36374 return dataView.getUint8(dataOffset);
36378 // ascii, 8-bit byte:
36380 getValue: function (dataView, dataOffset) {
36381 return String.fromCharCode(dataView.getUint8(dataOffset));
36386 // short, 16 bit int:
36388 getValue: function (dataView, dataOffset, littleEndian) {
36389 return dataView.getUint16(dataOffset, littleEndian);
36393 // long, 32 bit int:
36395 getValue: function (dataView, dataOffset, littleEndian) {
36396 return dataView.getUint32(dataOffset, littleEndian);
36400 // rational = two long values, first is numerator, second is denominator:
36402 getValue: function (dataView, dataOffset, littleEndian) {
36403 return dataView.getUint32(dataOffset, littleEndian) /
36404 dataView.getUint32(dataOffset + 4, littleEndian);
36408 // slong, 32 bit signed int:
36410 getValue: function (dataView, dataOffset, littleEndian) {
36411 return dataView.getInt32(dataOffset, littleEndian);
36415 // srational, two slongs, first is numerator, second is denominator:
36417 getValue: function (dataView, dataOffset, littleEndian) {
36418 return dataView.getInt32(dataOffset, littleEndian) /
36419 dataView.getInt32(dataOffset + 4, littleEndian);
36429 cls : 'btn-group roo-upload-cropbox-rotate-left',
36430 action : 'rotate-left',
36434 cls : 'btn btn-default',
36435 html : '<i class="fa fa-undo"></i>'
36441 cls : 'btn-group roo-upload-cropbox-picture',
36442 action : 'picture',
36446 cls : 'btn btn-default',
36447 html : '<i class="fa fa-picture-o"></i>'
36453 cls : 'btn-group roo-upload-cropbox-rotate-right',
36454 action : 'rotate-right',
36458 cls : 'btn btn-default',
36459 html : '<i class="fa fa-repeat"></i>'
36467 cls : 'btn-group roo-upload-cropbox-rotate-left',
36468 action : 'rotate-left',
36472 cls : 'btn btn-default',
36473 html : '<i class="fa fa-undo"></i>'
36479 cls : 'btn-group roo-upload-cropbox-download',
36480 action : 'download',
36484 cls : 'btn btn-default',
36485 html : '<i class="fa fa-download"></i>'
36491 cls : 'btn-group roo-upload-cropbox-crop',
36496 cls : 'btn btn-default',
36497 html : '<i class="fa fa-crop"></i>'
36503 cls : 'btn-group roo-upload-cropbox-trash',
36508 cls : 'btn btn-default',
36509 html : '<i class="fa fa-trash"></i>'
36515 cls : 'btn-group roo-upload-cropbox-rotate-right',
36516 action : 'rotate-right',
36520 cls : 'btn btn-default',
36521 html : '<i class="fa fa-repeat"></i>'
36529 cls : 'btn-group roo-upload-cropbox-rotate-left',
36530 action : 'rotate-left',
36534 cls : 'btn btn-default',
36535 html : '<i class="fa fa-undo"></i>'
36541 cls : 'btn-group roo-upload-cropbox-rotate-right',
36542 action : 'rotate-right',
36546 cls : 'btn btn-default',
36547 html : '<i class="fa fa-repeat"></i>'
36560 * @class Roo.bootstrap.DocumentManager
36561 * @extends Roo.bootstrap.Component
36562 * Bootstrap DocumentManager class
36563 * @cfg {String} paramName default 'imageUpload'
36564 * @cfg {String} toolTipName default 'filename'
36565 * @cfg {String} method default POST
36566 * @cfg {String} url action url
36567 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36568 * @cfg {Boolean} multiple multiple upload default true
36569 * @cfg {Number} thumbSize default 300
36570 * @cfg {String} fieldLabel
36571 * @cfg {Number} labelWidth default 4
36572 * @cfg {String} labelAlign (left|top) default left
36573 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36574 * @cfg {Number} labellg set the width of label (1-12)
36575 * @cfg {Number} labelmd set the width of label (1-12)
36576 * @cfg {Number} labelsm set the width of label (1-12)
36577 * @cfg {Number} labelxs set the width of label (1-12)
36580 * Create a new DocumentManager
36581 * @param {Object} config The config object
36584 Roo.bootstrap.DocumentManager = function(config){
36585 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36588 this.delegates = [];
36593 * Fire when initial the DocumentManager
36594 * @param {Roo.bootstrap.DocumentManager} this
36599 * inspect selected file
36600 * @param {Roo.bootstrap.DocumentManager} this
36601 * @param {File} file
36606 * Fire when xhr load exception
36607 * @param {Roo.bootstrap.DocumentManager} this
36608 * @param {XMLHttpRequest} xhr
36610 "exception" : true,
36612 * @event afterupload
36613 * Fire when xhr load exception
36614 * @param {Roo.bootstrap.DocumentManager} this
36615 * @param {XMLHttpRequest} xhr
36617 "afterupload" : true,
36620 * prepare the form data
36621 * @param {Roo.bootstrap.DocumentManager} this
36622 * @param {Object} formData
36627 * Fire when remove the file
36628 * @param {Roo.bootstrap.DocumentManager} this
36629 * @param {Object} file
36634 * Fire after refresh the file
36635 * @param {Roo.bootstrap.DocumentManager} this
36640 * Fire after click the image
36641 * @param {Roo.bootstrap.DocumentManager} this
36642 * @param {Object} file
36647 * Fire when upload a image and editable set to true
36648 * @param {Roo.bootstrap.DocumentManager} this
36649 * @param {Object} file
36653 * @event beforeselectfile
36654 * Fire before select file
36655 * @param {Roo.bootstrap.DocumentManager} this
36657 "beforeselectfile" : true,
36660 * Fire before process file
36661 * @param {Roo.bootstrap.DocumentManager} this
36662 * @param {Object} file
36666 * @event previewrendered
36667 * Fire when preview rendered
36668 * @param {Roo.bootstrap.DocumentManager} this
36669 * @param {Object} file
36671 "previewrendered" : true,
36674 "previewResize" : true
36679 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
36688 paramName : 'imageUpload',
36689 toolTipName : 'filename',
36692 labelAlign : 'left',
36702 getAutoCreate : function()
36704 var managerWidget = {
36706 cls : 'roo-document-manager',
36710 cls : 'roo-document-manager-selector',
36715 cls : 'roo-document-manager-uploader',
36719 cls : 'roo-document-manager-upload-btn',
36720 html : '<i class="fa fa-plus"></i>'
36731 cls : 'column col-md-12',
36736 if(this.fieldLabel.length){
36741 cls : 'column col-md-12',
36742 html : this.fieldLabel
36746 cls : 'column col-md-12',
36751 if(this.labelAlign == 'left'){
36756 html : this.fieldLabel
36765 if(this.labelWidth > 12){
36766 content[0].style = "width: " + this.labelWidth + 'px';
36769 if(this.labelWidth < 13 && this.labelmd == 0){
36770 this.labelmd = this.labelWidth;
36773 if(this.labellg > 0){
36774 content[0].cls += ' col-lg-' + this.labellg;
36775 content[1].cls += ' col-lg-' + (12 - this.labellg);
36778 if(this.labelmd > 0){
36779 content[0].cls += ' col-md-' + this.labelmd;
36780 content[1].cls += ' col-md-' + (12 - this.labelmd);
36783 if(this.labelsm > 0){
36784 content[0].cls += ' col-sm-' + this.labelsm;
36785 content[1].cls += ' col-sm-' + (12 - this.labelsm);
36788 if(this.labelxs > 0){
36789 content[0].cls += ' col-xs-' + this.labelxs;
36790 content[1].cls += ' col-xs-' + (12 - this.labelxs);
36798 cls : 'row clearfix',
36806 initEvents : function()
36808 this.managerEl = this.el.select('.roo-document-manager', true).first();
36809 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36811 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36812 this.selectorEl.hide();
36815 this.selectorEl.attr('multiple', 'multiple');
36818 this.selectorEl.on('change', this.onFileSelected, this);
36820 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36821 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36823 this.uploader.on('click', this.onUploaderClick, this);
36825 this.renderProgressDialog();
36829 window.addEventListener("resize", function() { _this.refresh(); } );
36831 this.fireEvent('initial', this);
36834 renderProgressDialog : function()
36838 this.progressDialog = new Roo.bootstrap.Modal({
36839 cls : 'roo-document-manager-progress-dialog',
36840 allow_close : false,
36851 btnclick : function() {
36852 _this.uploadCancel();
36858 this.progressDialog.render(Roo.get(document.body));
36860 this.progress = new Roo.bootstrap.Progress({
36861 cls : 'roo-document-manager-progress',
36866 this.progress.render(this.progressDialog.getChildContainer());
36868 this.progressBar = new Roo.bootstrap.ProgressBar({
36869 cls : 'roo-document-manager-progress-bar',
36872 aria_valuemax : 12,
36876 this.progressBar.render(this.progress.getChildContainer());
36879 onUploaderClick : function(e)
36881 e.preventDefault();
36883 if(this.fireEvent('beforeselectfile', this) != false){
36884 this.selectorEl.dom.click();
36889 onFileSelected : function(e)
36891 e.preventDefault();
36893 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36897 Roo.each(this.selectorEl.dom.files, function(file){
36898 if(this.fireEvent('inspect', this, file) != false){
36899 this.files.push(file);
36909 this.selectorEl.dom.value = '';
36911 if(!this.files || !this.files.length){
36915 if(this.boxes > 0 && this.files.length > this.boxes){
36916 this.files = this.files.slice(0, this.boxes);
36919 this.uploader.show();
36921 if(this.boxes > 0 && this.files.length > this.boxes - 1){
36922 this.uploader.hide();
36931 Roo.each(this.files, function(file){
36933 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36934 var f = this.renderPreview(file);
36939 if(file.type.indexOf('image') != -1){
36940 this.delegates.push(
36942 _this.process(file);
36943 }).createDelegate(this)
36951 _this.process(file);
36952 }).createDelegate(this)
36957 this.files = files;
36959 this.delegates = this.delegates.concat(docs);
36961 if(!this.delegates.length){
36966 this.progressBar.aria_valuemax = this.delegates.length;
36973 arrange : function()
36975 if(!this.delegates.length){
36976 this.progressDialog.hide();
36981 var delegate = this.delegates.shift();
36983 this.progressDialog.show();
36985 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
36987 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
36992 refresh : function()
36994 this.uploader.show();
36996 if(this.boxes > 0 && this.files.length > this.boxes - 1){
36997 this.uploader.hide();
37000 Roo.isTouch ? this.closable(false) : this.closable(true);
37002 this.fireEvent('refresh', this);
37005 onRemove : function(e, el, o)
37007 e.preventDefault();
37009 this.fireEvent('remove', this, o);
37013 remove : function(o)
37017 Roo.each(this.files, function(file){
37018 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37027 this.files = files;
37034 Roo.each(this.files, function(file){
37039 file.target.remove();
37048 onClick : function(e, el, o)
37050 e.preventDefault();
37052 this.fireEvent('click', this, o);
37056 closable : function(closable)
37058 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37060 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37072 xhrOnLoad : function(xhr)
37074 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37078 if (xhr.readyState !== 4) {
37080 this.fireEvent('exception', this, xhr);
37084 var response = Roo.decode(xhr.responseText);
37086 if(!response.success){
37088 this.fireEvent('exception', this, xhr);
37092 var file = this.renderPreview(response.data);
37094 this.files.push(file);
37098 this.fireEvent('afterupload', this, xhr);
37102 xhrOnError : function(xhr)
37104 Roo.log('xhr on error');
37106 var response = Roo.decode(xhr.responseText);
37113 process : function(file)
37115 if(this.fireEvent('process', this, file) !== false){
37116 if(this.editable && file.type.indexOf('image') != -1){
37117 this.fireEvent('edit', this, file);
37121 this.uploadStart(file, false);
37128 uploadStart : function(file, crop)
37130 this.xhr = new XMLHttpRequest();
37132 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37137 file.xhr = this.xhr;
37139 this.managerEl.createChild({
37141 cls : 'roo-document-manager-loading',
37145 tooltip : file.name,
37146 cls : 'roo-document-manager-thumb',
37147 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37153 this.xhr.open(this.method, this.url, true);
37156 "Accept": "application/json",
37157 "Cache-Control": "no-cache",
37158 "X-Requested-With": "XMLHttpRequest"
37161 for (var headerName in headers) {
37162 var headerValue = headers[headerName];
37164 this.xhr.setRequestHeader(headerName, headerValue);
37170 this.xhr.onload = function()
37172 _this.xhrOnLoad(_this.xhr);
37175 this.xhr.onerror = function()
37177 _this.xhrOnError(_this.xhr);
37180 var formData = new FormData();
37182 formData.append('returnHTML', 'NO');
37185 formData.append('crop', crop);
37188 formData.append(this.paramName, file, file.name);
37195 if(this.fireEvent('prepare', this, formData, options) != false){
37197 if(options.manually){
37201 this.xhr.send(formData);
37205 this.uploadCancel();
37208 uploadCancel : function()
37214 this.delegates = [];
37216 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37223 renderPreview : function(file)
37225 if(typeof(file.target) != 'undefined' && file.target){
37229 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37231 var previewEl = this.managerEl.createChild({
37233 cls : 'roo-document-manager-preview',
37237 tooltip : file[this.toolTipName],
37238 cls : 'roo-document-manager-thumb',
37239 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37244 html : '<i class="fa fa-times-circle"></i>'
37249 var close = previewEl.select('button.close', true).first();
37251 close.on('click', this.onRemove, this, file);
37253 file.target = previewEl;
37255 var image = previewEl.select('img', true).first();
37259 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37261 image.on('click', this.onClick, this, file);
37263 this.fireEvent('previewrendered', this, file);
37269 onPreviewLoad : function(file, image)
37271 if(typeof(file.target) == 'undefined' || !file.target){
37275 var width = image.dom.naturalWidth || image.dom.width;
37276 var height = image.dom.naturalHeight || image.dom.height;
37278 if(!this.previewResize) {
37282 if(width > height){
37283 file.target.addClass('wide');
37287 file.target.addClass('tall');
37292 uploadFromSource : function(file, crop)
37294 this.xhr = new XMLHttpRequest();
37296 this.managerEl.createChild({
37298 cls : 'roo-document-manager-loading',
37302 tooltip : file.name,
37303 cls : 'roo-document-manager-thumb',
37304 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37310 this.xhr.open(this.method, this.url, true);
37313 "Accept": "application/json",
37314 "Cache-Control": "no-cache",
37315 "X-Requested-With": "XMLHttpRequest"
37318 for (var headerName in headers) {
37319 var headerValue = headers[headerName];
37321 this.xhr.setRequestHeader(headerName, headerValue);
37327 this.xhr.onload = function()
37329 _this.xhrOnLoad(_this.xhr);
37332 this.xhr.onerror = function()
37334 _this.xhrOnError(_this.xhr);
37337 var formData = new FormData();
37339 formData.append('returnHTML', 'NO');
37341 formData.append('crop', crop);
37343 if(typeof(file.filename) != 'undefined'){
37344 formData.append('filename', file.filename);
37347 if(typeof(file.mimetype) != 'undefined'){
37348 formData.append('mimetype', file.mimetype);
37353 if(this.fireEvent('prepare', this, formData) != false){
37354 this.xhr.send(formData);
37364 * @class Roo.bootstrap.DocumentViewer
37365 * @extends Roo.bootstrap.Component
37366 * Bootstrap DocumentViewer class
37367 * @cfg {Boolean} showDownload (true|false) show download button (default true)
37368 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37371 * Create a new DocumentViewer
37372 * @param {Object} config The config object
37375 Roo.bootstrap.DocumentViewer = function(config){
37376 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37381 * Fire after initEvent
37382 * @param {Roo.bootstrap.DocumentViewer} this
37388 * @param {Roo.bootstrap.DocumentViewer} this
37393 * Fire after download button
37394 * @param {Roo.bootstrap.DocumentViewer} this
37399 * Fire after trash button
37400 * @param {Roo.bootstrap.DocumentViewer} this
37407 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
37409 showDownload : true,
37413 getAutoCreate : function()
37417 cls : 'roo-document-viewer',
37421 cls : 'roo-document-viewer-body',
37425 cls : 'roo-document-viewer-thumb',
37429 cls : 'roo-document-viewer-image'
37437 cls : 'roo-document-viewer-footer',
37440 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37444 cls : 'btn-group roo-document-viewer-download',
37448 cls : 'btn btn-default',
37449 html : '<i class="fa fa-download"></i>'
37455 cls : 'btn-group roo-document-viewer-trash',
37459 cls : 'btn btn-default',
37460 html : '<i class="fa fa-trash"></i>'
37473 initEvents : function()
37475 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37476 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37478 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37479 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37481 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37482 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37484 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37485 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37487 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37488 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37490 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37491 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37493 this.bodyEl.on('click', this.onClick, this);
37494 this.downloadBtn.on('click', this.onDownload, this);
37495 this.trashBtn.on('click', this.onTrash, this);
37497 this.downloadBtn.hide();
37498 this.trashBtn.hide();
37500 if(this.showDownload){
37501 this.downloadBtn.show();
37504 if(this.showTrash){
37505 this.trashBtn.show();
37508 if(!this.showDownload && !this.showTrash) {
37509 this.footerEl.hide();
37514 initial : function()
37516 this.fireEvent('initial', this);
37520 onClick : function(e)
37522 e.preventDefault();
37524 this.fireEvent('click', this);
37527 onDownload : function(e)
37529 e.preventDefault();
37531 this.fireEvent('download', this);
37534 onTrash : function(e)
37536 e.preventDefault();
37538 this.fireEvent('trash', this);
37550 * @class Roo.bootstrap.form.FieldLabel
37551 * @extends Roo.bootstrap.Component
37552 * Bootstrap FieldLabel class
37553 * @cfg {String} html contents of the element
37554 * @cfg {String} tag tag of the element default label
37555 * @cfg {String} cls class of the element
37556 * @cfg {String} target label target
37557 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37558 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37559 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37560 * @cfg {String} iconTooltip default "This field is required"
37561 * @cfg {String} indicatorpos (left|right) default left
37564 * Create a new FieldLabel
37565 * @param {Object} config The config object
37568 Roo.bootstrap.form.FieldLabel = function(config){
37569 Roo.bootstrap.Element.superclass.constructor.call(this, config);
37574 * Fires after the field has been marked as invalid.
37575 * @param {Roo.form.FieldLabel} this
37576 * @param {String} msg The validation message
37581 * Fires after the field has been validated with no errors.
37582 * @param {Roo.form.FieldLabel} this
37588 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
37595 invalidClass : 'has-warning',
37596 validClass : 'has-success',
37597 iconTooltip : 'This field is required',
37598 indicatorpos : 'left',
37600 getAutoCreate : function(){
37603 if (!this.allowBlank) {
37609 cls : 'roo-bootstrap-field-label ' + this.cls,
37614 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37615 tooltip : this.iconTooltip
37624 if(this.indicatorpos == 'right'){
37627 cls : 'roo-bootstrap-field-label ' + this.cls,
37636 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37637 tooltip : this.iconTooltip
37646 initEvents: function()
37648 Roo.bootstrap.Element.superclass.initEvents.call(this);
37650 this.indicator = this.indicatorEl();
37652 if(this.indicator){
37653 this.indicator.removeClass('visible');
37654 this.indicator.addClass('invisible');
37657 Roo.bootstrap.form.FieldLabel.register(this);
37660 indicatorEl : function()
37662 var indicator = this.el.select('i.roo-required-indicator',true).first();
37673 * Mark this field as valid
37675 markValid : function()
37677 if(this.indicator){
37678 this.indicator.removeClass('visible');
37679 this.indicator.addClass('invisible');
37681 if (Roo.bootstrap.version == 3) {
37682 this.el.removeClass(this.invalidClass);
37683 this.el.addClass(this.validClass);
37685 this.el.removeClass('is-invalid');
37686 this.el.addClass('is-valid');
37690 this.fireEvent('valid', this);
37694 * Mark this field as invalid
37695 * @param {String} msg The validation message
37697 markInvalid : function(msg)
37699 if(this.indicator){
37700 this.indicator.removeClass('invisible');
37701 this.indicator.addClass('visible');
37703 if (Roo.bootstrap.version == 3) {
37704 this.el.removeClass(this.validClass);
37705 this.el.addClass(this.invalidClass);
37707 this.el.removeClass('is-valid');
37708 this.el.addClass('is-invalid');
37712 this.fireEvent('invalid', this, msg);
37718 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37723 * register a FieldLabel Group
37724 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37726 register : function(label)
37728 if(this.groups.hasOwnProperty(label.target)){
37732 this.groups[label.target] = label;
37736 * fetch a FieldLabel Group based on the target
37737 * @param {string} target
37738 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37740 get: function(target) {
37741 if (typeof(this.groups[target]) == 'undefined') {
37745 return this.groups[target] ;
37754 * page DateSplitField.
37760 * @class Roo.bootstrap.form.DateSplitField
37761 * @extends Roo.bootstrap.Component
37762 * Bootstrap DateSplitField class
37763 * @cfg {string} fieldLabel - the label associated
37764 * @cfg {Number} labelWidth set the width of label (0-12)
37765 * @cfg {String} labelAlign (top|left)
37766 * @cfg {Boolean} dayAllowBlank (true|false) default false
37767 * @cfg {Boolean} monthAllowBlank (true|false) default false
37768 * @cfg {Boolean} yearAllowBlank (true|false) default false
37769 * @cfg {string} dayPlaceholder
37770 * @cfg {string} monthPlaceholder
37771 * @cfg {string} yearPlaceholder
37772 * @cfg {string} dayFormat default 'd'
37773 * @cfg {string} monthFormat default 'm'
37774 * @cfg {string} yearFormat default 'Y'
37775 * @cfg {Number} labellg set the width of label (1-12)
37776 * @cfg {Number} labelmd set the width of label (1-12)
37777 * @cfg {Number} labelsm set the width of label (1-12)
37778 * @cfg {Number} labelxs set the width of label (1-12)
37782 * Create a new DateSplitField
37783 * @param {Object} config The config object
37786 Roo.bootstrap.form.DateSplitField = function(config){
37787 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37793 * getting the data of years
37794 * @param {Roo.bootstrap.form.DateSplitField} this
37795 * @param {Object} years
37800 * getting the data of days
37801 * @param {Roo.bootstrap.form.DateSplitField} this
37802 * @param {Object} days
37807 * Fires after the field has been marked as invalid.
37808 * @param {Roo.form.Field} this
37809 * @param {String} msg The validation message
37814 * Fires after the field has been validated with no errors.
37815 * @param {Roo.form.Field} this
37821 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
37824 labelAlign : 'top',
37826 dayAllowBlank : false,
37827 monthAllowBlank : false,
37828 yearAllowBlank : false,
37829 dayPlaceholder : '',
37830 monthPlaceholder : '',
37831 yearPlaceholder : '',
37835 isFormField : true,
37841 getAutoCreate : function()
37845 cls : 'row roo-date-split-field-group',
37850 cls : 'form-hidden-field roo-date-split-field-group-value',
37856 var labelCls = 'col-md-12';
37857 var contentCls = 'col-md-4';
37859 if(this.fieldLabel){
37863 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37867 html : this.fieldLabel
37872 if(this.labelAlign == 'left'){
37874 if(this.labelWidth > 12){
37875 label.style = "width: " + this.labelWidth + 'px';
37878 if(this.labelWidth < 13 && this.labelmd == 0){
37879 this.labelmd = this.labelWidth;
37882 if(this.labellg > 0){
37883 labelCls = ' col-lg-' + this.labellg;
37884 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37887 if(this.labelmd > 0){
37888 labelCls = ' col-md-' + this.labelmd;
37889 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37892 if(this.labelsm > 0){
37893 labelCls = ' col-sm-' + this.labelsm;
37894 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37897 if(this.labelxs > 0){
37898 labelCls = ' col-xs-' + this.labelxs;
37899 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37903 label.cls += ' ' + labelCls;
37905 cfg.cn.push(label);
37908 Roo.each(['day', 'month', 'year'], function(t){
37911 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37918 inputEl: function ()
37920 return this.el.select('.roo-date-split-field-group-value', true).first();
37923 onRender : function(ct, position)
37927 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37929 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37931 this.dayField = new Roo.bootstrap.form.ComboBox({
37932 allowBlank : this.dayAllowBlank,
37933 alwaysQuery : true,
37934 displayField : 'value',
37937 forceSelection : true,
37939 placeholder : this.dayPlaceholder,
37940 selectOnFocus : true,
37941 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37942 triggerAction : 'all',
37944 valueField : 'value',
37945 store : new Roo.data.SimpleStore({
37946 data : (function() {
37948 _this.fireEvent('days', _this, days);
37951 fields : [ 'value' ]
37954 select : function (_self, record, index)
37956 _this.setValue(_this.getValue());
37961 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
37963 this.monthField = new Roo.bootstrap.form.MonthField({
37964 after : '<i class=\"fa fa-calendar\"></i>',
37965 allowBlank : this.monthAllowBlank,
37966 placeholder : this.monthPlaceholder,
37969 render : function (_self)
37971 this.el.select('span.input-group-addon', true).first().on('click', function(e){
37972 e.preventDefault();
37976 select : function (_self, oldvalue, newvalue)
37978 _this.setValue(_this.getValue());
37983 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
37985 this.yearField = new Roo.bootstrap.form.ComboBox({
37986 allowBlank : this.yearAllowBlank,
37987 alwaysQuery : true,
37988 displayField : 'value',
37991 forceSelection : true,
37993 placeholder : this.yearPlaceholder,
37994 selectOnFocus : true,
37995 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37996 triggerAction : 'all',
37998 valueField : 'value',
37999 store : new Roo.data.SimpleStore({
38000 data : (function() {
38002 _this.fireEvent('years', _this, years);
38005 fields : [ 'value' ]
38008 select : function (_self, record, index)
38010 _this.setValue(_this.getValue());
38015 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38018 setValue : function(v, format)
38020 this.inputEl.dom.value = v;
38022 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38024 var d = Date.parseDate(v, f);
38031 this.setDay(d.format(this.dayFormat));
38032 this.setMonth(d.format(this.monthFormat));
38033 this.setYear(d.format(this.yearFormat));
38040 setDay : function(v)
38042 this.dayField.setValue(v);
38043 this.inputEl.dom.value = this.getValue();
38048 setMonth : function(v)
38050 this.monthField.setValue(v, true);
38051 this.inputEl.dom.value = this.getValue();
38056 setYear : function(v)
38058 this.yearField.setValue(v);
38059 this.inputEl.dom.value = this.getValue();
38064 getDay : function()
38066 return this.dayField.getValue();
38069 getMonth : function()
38071 return this.monthField.getValue();
38074 getYear : function()
38076 return this.yearField.getValue();
38079 getValue : function()
38081 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38083 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38093 this.inputEl.dom.value = '';
38098 validate : function()
38100 var d = this.dayField.validate();
38101 var m = this.monthField.validate();
38102 var y = this.yearField.validate();
38107 (!this.dayAllowBlank && !d) ||
38108 (!this.monthAllowBlank && !m) ||
38109 (!this.yearAllowBlank && !y)
38114 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38123 this.markInvalid();
38128 markValid : function()
38131 var label = this.el.select('label', true).first();
38132 var icon = this.el.select('i.fa-star', true).first();
38138 this.fireEvent('valid', this);
38142 * Mark this field as invalid
38143 * @param {String} msg The validation message
38145 markInvalid : function(msg)
38148 var label = this.el.select('label', true).first();
38149 var icon = this.el.select('i.fa-star', true).first();
38151 if(label && !icon){
38152 this.el.select('.roo-date-split-field-label', true).createChild({
38154 cls : 'text-danger fa fa-lg fa-star',
38155 tooltip : 'This field is required',
38156 style : 'margin-right:5px;'
38160 this.fireEvent('invalid', this, msg);
38163 clearInvalid : function()
38165 var label = this.el.select('label', true).first();
38166 var icon = this.el.select('i.fa-star', true).first();
38172 this.fireEvent('valid', this);
38175 getName: function()
38185 * @class Roo.bootstrap.LayoutMasonry
38186 * @extends Roo.bootstrap.Component
38187 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38188 * Bootstrap Layout Masonry class
38191 * http://masonry.desandro.com
38193 * The idea is to render all the bricks based on vertical width...
38195 * The original code extends 'outlayer' - we might need to use that....
38198 * Create a new Element
38199 * @param {Object} config The config object
38202 Roo.bootstrap.LayoutMasonry = function(config){
38204 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38208 Roo.bootstrap.LayoutMasonry.register(this);
38214 * Fire after layout the items
38215 * @param {Roo.bootstrap.LayoutMasonry} this
38216 * @param {Roo.EventObject} e
38223 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
38226 * @cfg {Boolean} isLayoutInstant = no animation?
38228 isLayoutInstant : false, // needed?
38231 * @cfg {Number} boxWidth width of the columns
38236 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
38241 * @cfg {Number} padWidth padding below box..
38246 * @cfg {Number} gutter gutter width..
38251 * @cfg {Number} maxCols maximum number of columns
38257 * @cfg {Boolean} isAutoInitial defalut true
38259 isAutoInitial : true,
38264 * @cfg {Boolean} isHorizontal defalut false
38266 isHorizontal : false,
38268 currentSize : null,
38274 bricks: null, //CompositeElement
38278 _isLayoutInited : false,
38280 // isAlternative : false, // only use for vertical layout...
38283 * @cfg {Number} alternativePadWidth padding below box..
38285 alternativePadWidth : 50,
38287 selectedBrick : [],
38289 getAutoCreate : function(){
38291 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38295 cls: 'blog-masonary-wrapper ' + this.cls,
38297 cls : 'mas-boxes masonary'
38304 getChildContainer: function( )
38306 if (this.boxesEl) {
38307 return this.boxesEl;
38310 this.boxesEl = this.el.select('.mas-boxes').first();
38312 return this.boxesEl;
38316 initEvents : function()
38320 if(this.isAutoInitial){
38321 Roo.log('hook children rendered');
38322 this.on('childrenrendered', function() {
38323 Roo.log('children rendered');
38329 initial : function()
38331 this.selectedBrick = [];
38333 this.currentSize = this.el.getBox(true);
38335 Roo.EventManager.onWindowResize(this.resize, this);
38337 if(!this.isAutoInitial){
38345 //this.layout.defer(500,this);
38349 resize : function()
38351 var cs = this.el.getBox(true);
38354 this.currentSize.width == cs.width &&
38355 this.currentSize.x == cs.x &&
38356 this.currentSize.height == cs.height &&
38357 this.currentSize.y == cs.y
38359 Roo.log("no change in with or X or Y");
38363 this.currentSize = cs;
38369 layout : function()
38371 this._resetLayout();
38373 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38375 this.layoutItems( isInstant );
38377 this._isLayoutInited = true;
38379 this.fireEvent('layout', this);
38383 _resetLayout : function()
38385 if(this.isHorizontal){
38386 this.horizontalMeasureColumns();
38390 this.verticalMeasureColumns();
38394 verticalMeasureColumns : function()
38396 this.getContainerWidth();
38398 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38399 // this.colWidth = Math.floor(this.containerWidth * 0.8);
38403 var boxWidth = this.boxWidth + this.padWidth;
38405 if(this.containerWidth < this.boxWidth){
38406 boxWidth = this.containerWidth
38409 var containerWidth = this.containerWidth;
38411 var cols = Math.floor(containerWidth / boxWidth);
38413 this.cols = Math.max( cols, 1 );
38415 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38417 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38419 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38421 this.colWidth = boxWidth + avail - this.padWidth;
38423 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38424 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
38427 horizontalMeasureColumns : function()
38429 this.getContainerWidth();
38431 var boxWidth = this.boxWidth;
38433 if(this.containerWidth < boxWidth){
38434 boxWidth = this.containerWidth;
38437 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38439 this.el.setHeight(boxWidth);
38443 getContainerWidth : function()
38445 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38448 layoutItems : function( isInstant )
38450 Roo.log(this.bricks);
38452 var items = Roo.apply([], this.bricks);
38454 if(this.isHorizontal){
38455 this._horizontalLayoutItems( items , isInstant );
38459 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38460 // this._verticalAlternativeLayoutItems( items , isInstant );
38464 this._verticalLayoutItems( items , isInstant );
38468 _verticalLayoutItems : function ( items , isInstant)
38470 if ( !items || !items.length ) {
38475 ['xs', 'xs', 'xs', 'tall'],
38476 ['xs', 'xs', 'tall'],
38477 ['xs', 'xs', 'sm'],
38478 ['xs', 'xs', 'xs'],
38484 ['sm', 'xs', 'xs'],
38488 ['tall', 'xs', 'xs', 'xs'],
38489 ['tall', 'xs', 'xs'],
38501 Roo.each(items, function(item, k){
38503 switch (item.size) {
38504 // these layouts take up a full box,
38515 boxes.push([item]);
38538 var filterPattern = function(box, length)
38546 var pattern = box.slice(0, length);
38550 Roo.each(pattern, function(i){
38551 format.push(i.size);
38554 Roo.each(standard, function(s){
38556 if(String(s) != String(format)){
38565 if(!match && length == 1){
38570 filterPattern(box, length - 1);
38574 queue.push(pattern);
38576 box = box.slice(length, box.length);
38578 filterPattern(box, 4);
38584 Roo.each(boxes, function(box, k){
38590 if(box.length == 1){
38595 filterPattern(box, 4);
38599 this._processVerticalLayoutQueue( queue, isInstant );
38603 // _verticalAlternativeLayoutItems : function( items , isInstant )
38605 // if ( !items || !items.length ) {
38609 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
38613 _horizontalLayoutItems : function ( items , isInstant)
38615 if ( !items || !items.length || items.length < 3) {
38621 var eItems = items.slice(0, 3);
38623 items = items.slice(3, items.length);
38626 ['xs', 'xs', 'xs', 'wide'],
38627 ['xs', 'xs', 'wide'],
38628 ['xs', 'xs', 'sm'],
38629 ['xs', 'xs', 'xs'],
38635 ['sm', 'xs', 'xs'],
38639 ['wide', 'xs', 'xs', 'xs'],
38640 ['wide', 'xs', 'xs'],
38653 Roo.each(items, function(item, k){
38655 switch (item.size) {
38666 boxes.push([item]);
38690 var filterPattern = function(box, length)
38698 var pattern = box.slice(0, length);
38702 Roo.each(pattern, function(i){
38703 format.push(i.size);
38706 Roo.each(standard, function(s){
38708 if(String(s) != String(format)){
38717 if(!match && length == 1){
38722 filterPattern(box, length - 1);
38726 queue.push(pattern);
38728 box = box.slice(length, box.length);
38730 filterPattern(box, 4);
38736 Roo.each(boxes, function(box, k){
38742 if(box.length == 1){
38747 filterPattern(box, 4);
38754 var pos = this.el.getBox(true);
38758 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38760 var hit_end = false;
38762 Roo.each(queue, function(box){
38766 Roo.each(box, function(b){
38768 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38778 Roo.each(box, function(b){
38780 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38783 mx = Math.max(mx, b.x);
38787 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38791 Roo.each(box, function(b){
38793 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38807 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38810 /** Sets position of item in DOM
38811 * @param {Element} item
38812 * @param {Number} x - horizontal position
38813 * @param {Number} y - vertical position
38814 * @param {Boolean} isInstant - disables transitions
38816 _processVerticalLayoutQueue : function( queue, isInstant )
38818 var pos = this.el.getBox(true);
38823 for (var i = 0; i < this.cols; i++){
38827 Roo.each(queue, function(box, k){
38829 var col = k % this.cols;
38831 Roo.each(box, function(b,kk){
38833 b.el.position('absolute');
38835 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38836 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38838 if(b.size == 'md-left' || b.size == 'md-right'){
38839 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38840 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38843 b.el.setWidth(width);
38844 b.el.setHeight(height);
38846 b.el.select('iframe',true).setSize(width,height);
38850 for (var i = 0; i < this.cols; i++){
38852 if(maxY[i] < maxY[col]){
38857 col = Math.min(col, i);
38861 x = pos.x + col * (this.colWidth + this.padWidth);
38865 var positions = [];
38867 switch (box.length){
38869 positions = this.getVerticalOneBoxColPositions(x, y, box);
38872 positions = this.getVerticalTwoBoxColPositions(x, y, box);
38875 positions = this.getVerticalThreeBoxColPositions(x, y, box);
38878 positions = this.getVerticalFourBoxColPositions(x, y, box);
38884 Roo.each(box, function(b,kk){
38886 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38888 var sz = b.el.getSize();
38890 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38898 for (var i = 0; i < this.cols; i++){
38899 mY = Math.max(mY, maxY[i]);
38902 this.el.setHeight(mY - pos.y);
38906 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38908 // var pos = this.el.getBox(true);
38911 // var maxX = pos.right;
38913 // var maxHeight = 0;
38915 // Roo.each(items, function(item, k){
38919 // item.el.position('absolute');
38921 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38923 // item.el.setWidth(width);
38925 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38927 // item.el.setHeight(height);
38930 // item.el.setXY([x, y], isInstant ? false : true);
38932 // item.el.setXY([maxX - width, y], isInstant ? false : true);
38935 // y = y + height + this.alternativePadWidth;
38937 // maxHeight = maxHeight + height + this.alternativePadWidth;
38941 // this.el.setHeight(maxHeight);
38945 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38947 var pos = this.el.getBox(true);
38952 var maxX = pos.right;
38954 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38956 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38958 Roo.each(queue, function(box, k){
38960 Roo.each(box, function(b, kk){
38962 b.el.position('absolute');
38964 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38965 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38967 if(b.size == 'md-left' || b.size == 'md-right'){
38968 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38969 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38972 b.el.setWidth(width);
38973 b.el.setHeight(height);
38981 var positions = [];
38983 switch (box.length){
38985 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
38988 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
38991 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
38994 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39000 Roo.each(box, function(b,kk){
39002 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39004 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39012 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39014 Roo.each(eItems, function(b,k){
39016 b.size = (k == 0) ? 'sm' : 'xs';
39017 b.x = (k == 0) ? 2 : 1;
39018 b.y = (k == 0) ? 2 : 1;
39020 b.el.position('absolute');
39022 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39024 b.el.setWidth(width);
39026 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39028 b.el.setHeight(height);
39032 var positions = [];
39035 x : maxX - this.unitWidth * 2 - this.gutter,
39040 x : maxX - this.unitWidth,
39041 y : minY + (this.unitWidth + this.gutter) * 2
39045 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39049 Roo.each(eItems, function(b,k){
39051 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39057 getVerticalOneBoxColPositions : function(x, y, box)
39061 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39063 if(box[0].size == 'md-left'){
39067 if(box[0].size == 'md-right'){
39072 x : x + (this.unitWidth + this.gutter) * rand,
39079 getVerticalTwoBoxColPositions : function(x, y, box)
39083 if(box[0].size == 'xs'){
39087 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39091 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39105 x : x + (this.unitWidth + this.gutter) * 2,
39106 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39113 getVerticalThreeBoxColPositions : function(x, y, box)
39117 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39125 x : x + (this.unitWidth + this.gutter) * 1,
39130 x : x + (this.unitWidth + this.gutter) * 2,
39138 if(box[0].size == 'xs' && box[1].size == 'xs'){
39147 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39151 x : x + (this.unitWidth + this.gutter) * 1,
39165 x : x + (this.unitWidth + this.gutter) * 2,
39170 x : x + (this.unitWidth + this.gutter) * 2,
39171 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39178 getVerticalFourBoxColPositions : function(x, y, box)
39182 if(box[0].size == 'xs'){
39191 y : y + (this.unitHeight + this.gutter) * 1
39196 y : y + (this.unitHeight + this.gutter) * 2
39200 x : x + (this.unitWidth + this.gutter) * 1,
39214 x : x + (this.unitWidth + this.gutter) * 2,
39219 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39220 y : y + (this.unitHeight + this.gutter) * 1
39224 x : x + (this.unitWidth + this.gutter) * 2,
39225 y : y + (this.unitWidth + this.gutter) * 2
39232 getHorizontalOneBoxColPositions : function(maxX, minY, box)
39236 if(box[0].size == 'md-left'){
39238 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39245 if(box[0].size == 'md-right'){
39247 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39248 y : minY + (this.unitWidth + this.gutter) * 1
39254 var rand = Math.floor(Math.random() * (4 - box[0].y));
39257 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39258 y : minY + (this.unitWidth + this.gutter) * rand
39265 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39269 if(box[0].size == 'xs'){
39272 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39277 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39278 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39286 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39291 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39292 y : minY + (this.unitWidth + this.gutter) * 2
39299 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39303 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39306 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39311 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39312 y : minY + (this.unitWidth + this.gutter) * 1
39316 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39317 y : minY + (this.unitWidth + this.gutter) * 2
39324 if(box[0].size == 'xs' && box[1].size == 'xs'){
39327 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39332 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39337 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39338 y : minY + (this.unitWidth + this.gutter) * 1
39346 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39351 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39352 y : minY + (this.unitWidth + this.gutter) * 2
39356 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39357 y : minY + (this.unitWidth + this.gutter) * 2
39364 getHorizontalFourBoxColPositions : function(maxX, minY, box)
39368 if(box[0].size == 'xs'){
39371 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39376 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39381 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),
39386 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39387 y : minY + (this.unitWidth + this.gutter) * 1
39395 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39400 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39401 y : minY + (this.unitWidth + this.gutter) * 2
39405 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39406 y : minY + (this.unitWidth + this.gutter) * 2
39410 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),
39411 y : minY + (this.unitWidth + this.gutter) * 2
39419 * remove a Masonry Brick
39420 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39422 removeBrick : function(brick_id)
39428 for (var i = 0; i<this.bricks.length; i++) {
39429 if (this.bricks[i].id == brick_id) {
39430 this.bricks.splice(i,1);
39431 this.el.dom.removeChild(Roo.get(brick_id).dom);
39438 * adds a Masonry Brick
39439 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39441 addBrick : function(cfg)
39443 var cn = new Roo.bootstrap.MasonryBrick(cfg);
39444 //this.register(cn);
39445 cn.parentId = this.id;
39446 cn.render(this.el);
39451 * register a Masonry Brick
39452 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39455 register : function(brick)
39457 this.bricks.push(brick);
39458 brick.masonryId = this.id;
39462 * clear all the Masonry Brick
39464 clearAll : function()
39467 //this.getChildContainer().dom.innerHTML = "";
39468 this.el.dom.innerHTML = '';
39471 getSelected : function()
39473 if (!this.selectedBrick) {
39477 return this.selectedBrick;
39481 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39485 * register a Masonry Layout
39486 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39489 register : function(layout)
39491 this.groups[layout.id] = layout;
39494 * fetch a Masonry Layout based on the masonry layout ID
39495 * @param {string} the masonry layout to add
39496 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39499 get: function(layout_id) {
39500 if (typeof(this.groups[layout_id]) == 'undefined') {
39503 return this.groups[layout_id] ;
39515 * http://masonry.desandro.com
39517 * The idea is to render all the bricks based on vertical width...
39519 * The original code extends 'outlayer' - we might need to use that....
39525 * @class Roo.bootstrap.LayoutMasonryAuto
39526 * @extends Roo.bootstrap.Component
39527 * Bootstrap Layout Masonry class
39530 * Create a new Element
39531 * @param {Object} config The config object
39534 Roo.bootstrap.LayoutMasonryAuto = function(config){
39535 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39538 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
39541 * @cfg {Boolean} isFitWidth - resize the width..
39543 isFitWidth : false, // options..
39545 * @cfg {Boolean} isOriginLeft = left align?
39547 isOriginLeft : true,
39549 * @cfg {Boolean} isOriginTop = top align?
39551 isOriginTop : false,
39553 * @cfg {Boolean} isLayoutInstant = no animation?
39555 isLayoutInstant : false, // needed?
39557 * @cfg {Boolean} isResizingContainer = not sure if this is used..
39559 isResizingContainer : true,
39561 * @cfg {Number} columnWidth width of the columns
39567 * @cfg {Number} maxCols maximum number of columns
39572 * @cfg {Number} padHeight padding below box..
39578 * @cfg {Boolean} isAutoInitial defalut true
39581 isAutoInitial : true,
39587 initialColumnWidth : 0,
39588 currentSize : null,
39590 colYs : null, // array.
39597 bricks: null, //CompositeElement
39598 cols : 0, // array?
39599 // element : null, // wrapped now this.el
39600 _isLayoutInited : null,
39603 getAutoCreate : function(){
39607 cls: 'blog-masonary-wrapper ' + this.cls,
39609 cls : 'mas-boxes masonary'
39616 getChildContainer: function( )
39618 if (this.boxesEl) {
39619 return this.boxesEl;
39622 this.boxesEl = this.el.select('.mas-boxes').first();
39624 return this.boxesEl;
39628 initEvents : function()
39632 if(this.isAutoInitial){
39633 Roo.log('hook children rendered');
39634 this.on('childrenrendered', function() {
39635 Roo.log('children rendered');
39642 initial : function()
39644 this.reloadItems();
39646 this.currentSize = this.el.getBox(true);
39648 /// was window resize... - let's see if this works..
39649 Roo.EventManager.onWindowResize(this.resize, this);
39651 if(!this.isAutoInitial){
39656 this.layout.defer(500,this);
39659 reloadItems: function()
39661 this.bricks = this.el.select('.masonry-brick', true);
39663 this.bricks.each(function(b) {
39664 //Roo.log(b.getSize());
39665 if (!b.attr('originalwidth')) {
39666 b.attr('originalwidth', b.getSize().width);
39671 Roo.log(this.bricks.elements.length);
39674 resize : function()
39677 var cs = this.el.getBox(true);
39679 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39680 Roo.log("no change in with or X");
39683 this.currentSize = cs;
39687 layout : function()
39690 this._resetLayout();
39691 //this._manageStamps();
39693 // don't animate first layout
39694 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39695 this.layoutItems( isInstant );
39697 // flag for initalized
39698 this._isLayoutInited = true;
39701 layoutItems : function( isInstant )
39703 //var items = this._getItemsForLayout( this.items );
39704 // original code supports filtering layout items.. we just ignore it..
39706 this._layoutItems( this.bricks , isInstant );
39708 this._postLayout();
39710 _layoutItems : function ( items , isInstant)
39712 //this.fireEvent( 'layout', this, items );
39715 if ( !items || !items.elements.length ) {
39716 // no items, emit event with empty array
39721 items.each(function(item) {
39722 Roo.log("layout item");
39724 // get x/y object from method
39725 var position = this._getItemLayoutPosition( item );
39727 position.item = item;
39728 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39729 queue.push( position );
39732 this._processLayoutQueue( queue );
39734 /** Sets position of item in DOM
39735 * @param {Element} item
39736 * @param {Number} x - horizontal position
39737 * @param {Number} y - vertical position
39738 * @param {Boolean} isInstant - disables transitions
39740 _processLayoutQueue : function( queue )
39742 for ( var i=0, len = queue.length; i < len; i++ ) {
39743 var obj = queue[i];
39744 obj.item.position('absolute');
39745 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39751 * Any logic you want to do after each layout,
39752 * i.e. size the container
39754 _postLayout : function()
39756 this.resizeContainer();
39759 resizeContainer : function()
39761 if ( !this.isResizingContainer ) {
39764 var size = this._getContainerSize();
39766 this.el.setSize(size.width,size.height);
39767 this.boxesEl.setSize(size.width,size.height);
39773 _resetLayout : function()
39775 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39776 this.colWidth = this.el.getWidth();
39777 //this.gutter = this.el.getWidth();
39779 this.measureColumns();
39785 this.colYs.push( 0 );
39791 measureColumns : function()
39793 this.getContainerWidth();
39794 // if columnWidth is 0, default to outerWidth of first item
39795 if ( !this.columnWidth ) {
39796 var firstItem = this.bricks.first();
39797 Roo.log(firstItem);
39798 this.columnWidth = this.containerWidth;
39799 if (firstItem && firstItem.attr('originalwidth') ) {
39800 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39802 // columnWidth fall back to item of first element
39803 Roo.log("set column width?");
39804 this.initialColumnWidth = this.columnWidth ;
39806 // if first elem has no width, default to size of container
39811 if (this.initialColumnWidth) {
39812 this.columnWidth = this.initialColumnWidth;
39817 // column width is fixed at the top - however if container width get's smaller we should
39820 // this bit calcs how man columns..
39822 var columnWidth = this.columnWidth += this.gutter;
39824 // calculate columns
39825 var containerWidth = this.containerWidth + this.gutter;
39827 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39828 // fix rounding errors, typically with gutters
39829 var excess = columnWidth - containerWidth % columnWidth;
39832 // if overshoot is less than a pixel, round up, otherwise floor it
39833 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39834 cols = Math[ mathMethod ]( cols );
39835 this.cols = Math.max( cols, 1 );
39836 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39838 // padding positioning..
39839 var totalColWidth = this.cols * this.columnWidth;
39840 var padavail = this.containerWidth - totalColWidth;
39841 // so for 2 columns - we need 3 'pads'
39843 var padNeeded = (1+this.cols) * this.padWidth;
39845 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39847 this.columnWidth += padExtra
39848 //this.padWidth = Math.floor(padavail / ( this.cols));
39850 // adjust colum width so that padding is fixed??
39852 // we have 3 columns ... total = width * 3
39853 // we have X left over... that should be used by
39855 //if (this.expandC) {
39863 getContainerWidth : function()
39865 /* // container is parent if fit width
39866 var container = this.isFitWidth ? this.element.parentNode : this.element;
39867 // check that this.size and size are there
39868 // IE8 triggers resize on body size change, so they might not be
39870 var size = getSize( container ); //FIXME
39871 this.containerWidth = size && size.innerWidth; //FIXME
39874 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39878 _getItemLayoutPosition : function( item ) // what is item?
39880 // we resize the item to our columnWidth..
39882 item.setWidth(this.columnWidth);
39883 item.autoBoxAdjust = false;
39885 var sz = item.getSize();
39887 // how many columns does this brick span
39888 var remainder = this.containerWidth % this.columnWidth;
39890 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39891 // round if off by 1 pixel, otherwise use ceil
39892 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
39893 colSpan = Math.min( colSpan, this.cols );
39895 // normally this should be '1' as we dont' currently allow multi width columns..
39897 var colGroup = this._getColGroup( colSpan );
39898 // get the minimum Y value from the columns
39899 var minimumY = Math.min.apply( Math, colGroup );
39900 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
39902 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
39904 // position the brick
39906 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39907 y: this.currentSize.y + minimumY + this.padHeight
39911 // apply setHeight to necessary columns
39912 var setHeight = minimumY + sz.height + this.padHeight;
39913 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
39915 var setSpan = this.cols + 1 - colGroup.length;
39916 for ( var i = 0; i < setSpan; i++ ) {
39917 this.colYs[ shortColIndex + i ] = setHeight ;
39924 * @param {Number} colSpan - number of columns the element spans
39925 * @returns {Array} colGroup
39927 _getColGroup : function( colSpan )
39929 if ( colSpan < 2 ) {
39930 // if brick spans only one column, use all the column Ys
39935 // how many different places could this brick fit horizontally
39936 var groupCount = this.cols + 1 - colSpan;
39937 // for each group potential horizontal position
39938 for ( var i = 0; i < groupCount; i++ ) {
39939 // make an array of colY values for that one group
39940 var groupColYs = this.colYs.slice( i, i + colSpan );
39941 // and get the max value of the array
39942 colGroup[i] = Math.max.apply( Math, groupColYs );
39947 _manageStamp : function( stamp )
39949 var stampSize = stamp.getSize();
39950 var offset = stamp.getBox();
39951 // get the columns that this stamp affects
39952 var firstX = this.isOriginLeft ? offset.x : offset.right;
39953 var lastX = firstX + stampSize.width;
39954 var firstCol = Math.floor( firstX / this.columnWidth );
39955 firstCol = Math.max( 0, firstCol );
39957 var lastCol = Math.floor( lastX / this.columnWidth );
39958 // lastCol should not go over if multiple of columnWidth #425
39959 lastCol -= lastX % this.columnWidth ? 0 : 1;
39960 lastCol = Math.min( this.cols - 1, lastCol );
39962 // set colYs to bottom of the stamp
39963 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
39966 for ( var i = firstCol; i <= lastCol; i++ ) {
39967 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
39972 _getContainerSize : function()
39974 this.maxY = Math.max.apply( Math, this.colYs );
39979 if ( this.isFitWidth ) {
39980 size.width = this._getContainerFitWidth();
39986 _getContainerFitWidth : function()
39988 var unusedCols = 0;
39989 // count unused columns
39992 if ( this.colYs[i] !== 0 ) {
39997 // fit container to columns that have been used
39998 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40001 needsResizeLayout : function()
40003 var previousWidth = this.containerWidth;
40004 this.getContainerWidth();
40005 return previousWidth !== this.containerWidth;
40020 * @class Roo.bootstrap.MasonryBrick
40021 * @extends Roo.bootstrap.Component
40022 * Bootstrap MasonryBrick class
40025 * Create a new MasonryBrick
40026 * @param {Object} config The config object
40029 Roo.bootstrap.MasonryBrick = function(config){
40031 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40033 Roo.bootstrap.MasonryBrick.register(this);
40039 * When a MasonryBrick is clcik
40040 * @param {Roo.bootstrap.MasonryBrick} this
40041 * @param {Roo.EventObject} e
40047 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40050 * @cfg {String} title
40054 * @cfg {String} html
40058 * @cfg {String} bgimage
40062 * @cfg {String} videourl
40066 * @cfg {String} cls
40070 * @cfg {String} href
40074 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40079 * @cfg {String} placetitle (center|bottom)
40084 * @cfg {Boolean} isFitContainer defalut true
40086 isFitContainer : true,
40089 * @cfg {Boolean} preventDefault defalut false
40091 preventDefault : false,
40094 * @cfg {Boolean} inverse defalut false
40096 maskInverse : false,
40098 getAutoCreate : function()
40100 if(!this.isFitContainer){
40101 return this.getSplitAutoCreate();
40104 var cls = 'masonry-brick masonry-brick-full';
40106 if(this.href.length){
40107 cls += ' masonry-brick-link';
40110 if(this.bgimage.length){
40111 cls += ' masonry-brick-image';
40114 if(this.maskInverse){
40115 cls += ' mask-inverse';
40118 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40119 cls += ' enable-mask';
40123 cls += ' masonry-' + this.size + '-brick';
40126 if(this.placetitle.length){
40128 switch (this.placetitle) {
40130 cls += ' masonry-center-title';
40133 cls += ' masonry-bottom-title';
40140 if(!this.html.length && !this.bgimage.length){
40141 cls += ' masonry-center-title';
40144 if(!this.html.length && this.bgimage.length){
40145 cls += ' masonry-bottom-title';
40150 cls += ' ' + this.cls;
40154 tag: (this.href.length) ? 'a' : 'div',
40159 cls: 'masonry-brick-mask'
40163 cls: 'masonry-brick-paragraph',
40169 if(this.href.length){
40170 cfg.href = this.href;
40173 var cn = cfg.cn[1].cn;
40175 if(this.title.length){
40178 cls: 'masonry-brick-title',
40183 if(this.html.length){
40186 cls: 'masonry-brick-text',
40191 if (!this.title.length && !this.html.length) {
40192 cfg.cn[1].cls += ' hide';
40195 if(this.bgimage.length){
40198 cls: 'masonry-brick-image-view',
40203 if(this.videourl.length){
40204 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40205 // youtube support only?
40208 cls: 'masonry-brick-image-view',
40211 allowfullscreen : true
40219 getSplitAutoCreate : function()
40221 var cls = 'masonry-brick masonry-brick-split';
40223 if(this.href.length){
40224 cls += ' masonry-brick-link';
40227 if(this.bgimage.length){
40228 cls += ' masonry-brick-image';
40232 cls += ' masonry-' + this.size + '-brick';
40235 switch (this.placetitle) {
40237 cls += ' masonry-center-title';
40240 cls += ' masonry-bottom-title';
40243 if(!this.bgimage.length){
40244 cls += ' masonry-center-title';
40247 if(this.bgimage.length){
40248 cls += ' masonry-bottom-title';
40254 cls += ' ' + this.cls;
40258 tag: (this.href.length) ? 'a' : 'div',
40263 cls: 'masonry-brick-split-head',
40267 cls: 'masonry-brick-paragraph',
40274 cls: 'masonry-brick-split-body',
40280 if(this.href.length){
40281 cfg.href = this.href;
40284 if(this.title.length){
40285 cfg.cn[0].cn[0].cn.push({
40287 cls: 'masonry-brick-title',
40292 if(this.html.length){
40293 cfg.cn[1].cn.push({
40295 cls: 'masonry-brick-text',
40300 if(this.bgimage.length){
40301 cfg.cn[0].cn.push({
40303 cls: 'masonry-brick-image-view',
40308 if(this.videourl.length){
40309 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40310 // youtube support only?
40311 cfg.cn[0].cn.cn.push({
40313 cls: 'masonry-brick-image-view',
40316 allowfullscreen : true
40323 initEvents: function()
40325 switch (this.size) {
40358 this.el.on('touchstart', this.onTouchStart, this);
40359 this.el.on('touchmove', this.onTouchMove, this);
40360 this.el.on('touchend', this.onTouchEnd, this);
40361 this.el.on('contextmenu', this.onContextMenu, this);
40363 this.el.on('mouseenter' ,this.enter, this);
40364 this.el.on('mouseleave', this.leave, this);
40365 this.el.on('click', this.onClick, this);
40368 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40369 this.parent().bricks.push(this);
40374 onClick: function(e, el)
40376 var time = this.endTimer - this.startTimer;
40377 // Roo.log(e.preventDefault());
40380 e.preventDefault();
40385 if(!this.preventDefault){
40389 e.preventDefault();
40391 if (this.activeClass != '') {
40392 this.selectBrick();
40395 this.fireEvent('click', this, e);
40398 enter: function(e, el)
40400 e.preventDefault();
40402 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40406 if(this.bgimage.length && this.html.length){
40407 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40411 leave: function(e, el)
40413 e.preventDefault();
40415 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40419 if(this.bgimage.length && this.html.length){
40420 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40424 onTouchStart: function(e, el)
40426 // e.preventDefault();
40428 this.touchmoved = false;
40430 if(!this.isFitContainer){
40434 if(!this.bgimage.length || !this.html.length){
40438 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40440 this.timer = new Date().getTime();
40444 onTouchMove: function(e, el)
40446 this.touchmoved = true;
40449 onContextMenu : function(e,el)
40451 e.preventDefault();
40452 e.stopPropagation();
40456 onTouchEnd: function(e, el)
40458 // e.preventDefault();
40460 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40467 if(!this.bgimage.length || !this.html.length){
40469 if(this.href.length){
40470 window.location.href = this.href;
40476 if(!this.isFitContainer){
40480 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40482 window.location.href = this.href;
40485 //selection on single brick only
40486 selectBrick : function() {
40488 if (!this.parentId) {
40492 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40493 var index = m.selectedBrick.indexOf(this.id);
40496 m.selectedBrick.splice(index,1);
40497 this.el.removeClass(this.activeClass);
40501 for(var i = 0; i < m.selectedBrick.length; i++) {
40502 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40503 b.el.removeClass(b.activeClass);
40506 m.selectedBrick = [];
40508 m.selectedBrick.push(this.id);
40509 this.el.addClass(this.activeClass);
40513 isSelected : function(){
40514 return this.el.hasClass(this.activeClass);
40519 Roo.apply(Roo.bootstrap.MasonryBrick, {
40522 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40524 * register a Masonry Brick
40525 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40528 register : function(brick)
40530 //this.groups[brick.id] = brick;
40531 this.groups.add(brick.id, brick);
40534 * fetch a masonry brick based on the masonry brick ID
40535 * @param {string} the masonry brick to add
40536 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40539 get: function(brick_id)
40541 // if (typeof(this.groups[brick_id]) == 'undefined') {
40544 // return this.groups[brick_id] ;
40546 if(this.groups.key(brick_id)) {
40547 return this.groups.key(brick_id);
40565 * @class Roo.bootstrap.Brick
40566 * @extends Roo.bootstrap.Component
40567 * Bootstrap Brick class
40570 * Create a new Brick
40571 * @param {Object} config The config object
40574 Roo.bootstrap.Brick = function(config){
40575 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40581 * When a Brick is click
40582 * @param {Roo.bootstrap.Brick} this
40583 * @param {Roo.EventObject} e
40589 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
40592 * @cfg {String} title
40596 * @cfg {String} html
40600 * @cfg {String} bgimage
40604 * @cfg {String} cls
40608 * @cfg {String} href
40612 * @cfg {String} video
40616 * @cfg {Boolean} square
40620 getAutoCreate : function()
40622 var cls = 'roo-brick';
40624 if(this.href.length){
40625 cls += ' roo-brick-link';
40628 if(this.bgimage.length){
40629 cls += ' roo-brick-image';
40632 if(!this.html.length && !this.bgimage.length){
40633 cls += ' roo-brick-center-title';
40636 if(!this.html.length && this.bgimage.length){
40637 cls += ' roo-brick-bottom-title';
40641 cls += ' ' + this.cls;
40645 tag: (this.href.length) ? 'a' : 'div',
40650 cls: 'roo-brick-paragraph',
40656 if(this.href.length){
40657 cfg.href = this.href;
40660 var cn = cfg.cn[0].cn;
40662 if(this.title.length){
40665 cls: 'roo-brick-title',
40670 if(this.html.length){
40673 cls: 'roo-brick-text',
40680 if(this.bgimage.length){
40683 cls: 'roo-brick-image-view',
40691 initEvents: function()
40693 if(this.title.length || this.html.length){
40694 this.el.on('mouseenter' ,this.enter, this);
40695 this.el.on('mouseleave', this.leave, this);
40698 Roo.EventManager.onWindowResize(this.resize, this);
40700 if(this.bgimage.length){
40701 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40702 this.imageEl.on('load', this.onImageLoad, this);
40709 onImageLoad : function()
40714 resize : function()
40716 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40718 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40720 if(this.bgimage.length){
40721 var image = this.el.select('.roo-brick-image-view', true).first();
40723 image.setWidth(paragraph.getWidth());
40726 image.setHeight(paragraph.getWidth());
40729 this.el.setHeight(image.getHeight());
40730 paragraph.setHeight(image.getHeight());
40736 enter: function(e, el)
40738 e.preventDefault();
40740 if(this.bgimage.length){
40741 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40742 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40746 leave: function(e, el)
40748 e.preventDefault();
40750 if(this.bgimage.length){
40751 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40752 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40767 * @class Roo.bootstrap.form.NumberField
40768 * @extends Roo.bootstrap.form.Input
40769 * Bootstrap NumberField class
40775 * Create a new NumberField
40776 * @param {Object} config The config object
40779 Roo.bootstrap.form.NumberField = function(config){
40780 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40783 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40786 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40788 allowDecimals : true,
40790 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40792 decimalSeparator : ".",
40794 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40796 decimalPrecision : 2,
40798 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40800 allowNegative : true,
40803 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40807 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40809 minValue : Number.NEGATIVE_INFINITY,
40811 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40813 maxValue : Number.MAX_VALUE,
40815 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40817 minText : "The minimum value for this field is {0}",
40819 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40821 maxText : "The maximum value for this field is {0}",
40823 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40824 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40826 nanText : "{0} is not a valid number",
40828 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40830 thousandsDelimiter : false,
40832 * @cfg {String} valueAlign alignment of value
40834 valueAlign : "left",
40836 getAutoCreate : function()
40838 var hiddenInput = {
40842 cls: 'hidden-number-input'
40846 hiddenInput.name = this.name;
40851 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40853 this.name = hiddenInput.name;
40855 if(cfg.cn.length > 0) {
40856 cfg.cn.push(hiddenInput);
40863 initEvents : function()
40865 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40867 var allowed = "0123456789";
40869 if(this.allowDecimals){
40870 allowed += this.decimalSeparator;
40873 if(this.allowNegative){
40877 if(this.thousandsDelimiter) {
40881 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40883 var keyPress = function(e){
40885 var k = e.getKey();
40887 var c = e.getCharCode();
40890 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40891 allowed.indexOf(String.fromCharCode(c)) === -1
40897 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40901 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40906 this.el.on("keypress", keyPress, this);
40909 validateValue : function(value)
40912 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40916 var num = this.parseValue(value);
40919 this.markInvalid(String.format(this.nanText, value));
40923 if(num < this.minValue){
40924 this.markInvalid(String.format(this.minText, this.minValue));
40928 if(num > this.maxValue){
40929 this.markInvalid(String.format(this.maxText, this.maxValue));
40936 getValue : function()
40938 var v = this.hiddenEl().getValue();
40940 return this.fixPrecision(this.parseValue(v));
40943 parseValue : function(value)
40945 if(this.thousandsDelimiter) {
40947 r = new RegExp(",", "g");
40948 value = value.replace(r, "");
40951 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40952 return isNaN(value) ? '' : value;
40955 fixPrecision : function(value)
40957 if(this.thousandsDelimiter) {
40959 r = new RegExp(",", "g");
40960 value = value.replace(r, "");
40963 var nan = isNaN(value);
40965 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40966 return nan ? '' : value;
40968 return parseFloat(value).toFixed(this.decimalPrecision);
40971 setValue : function(v)
40973 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
40979 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40981 this.inputEl().dom.value = (v == '') ? '' :
40982 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
40984 if(!this.allowZero && v === '0') {
40985 this.hiddenEl().dom.value = '';
40986 this.inputEl().dom.value = '';
40993 decimalPrecisionFcn : function(v)
40995 return Math.floor(v);
40998 beforeBlur : function()
41000 var v = this.parseValue(this.getRawValue());
41002 if(v || v === 0 || v === ''){
41007 hiddenEl : function()
41009 return this.el.select('input.hidden-number-input',true).first();
41021 * @class Roo.bootstrap.DocumentSlider
41022 * @extends Roo.bootstrap.Component
41023 * Bootstrap DocumentSlider class
41026 * Create a new DocumentViewer
41027 * @param {Object} config The config object
41030 Roo.bootstrap.DocumentSlider = function(config){
41031 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41038 * Fire after initEvent
41039 * @param {Roo.bootstrap.DocumentSlider} this
41044 * Fire after update
41045 * @param {Roo.bootstrap.DocumentSlider} this
41051 * @param {Roo.bootstrap.DocumentSlider} this
41057 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41063 getAutoCreate : function()
41067 cls : 'roo-document-slider',
41071 cls : 'roo-document-slider-header',
41075 cls : 'roo-document-slider-header-title'
41081 cls : 'roo-document-slider-body',
41085 cls : 'roo-document-slider-prev',
41089 cls : 'fa fa-chevron-left'
41095 cls : 'roo-document-slider-thumb',
41099 cls : 'roo-document-slider-image'
41105 cls : 'roo-document-slider-next',
41109 cls : 'fa fa-chevron-right'
41121 initEvents : function()
41123 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41124 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41126 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41127 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41129 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41130 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41132 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41133 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41135 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41136 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41138 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41139 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41141 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41142 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41144 this.thumbEl.on('click', this.onClick, this);
41146 this.prevIndicator.on('click', this.prev, this);
41148 this.nextIndicator.on('click', this.next, this);
41152 initial : function()
41154 if(this.files.length){
41155 this.indicator = 1;
41159 this.fireEvent('initial', this);
41162 update : function()
41164 this.imageEl.attr('src', this.files[this.indicator - 1]);
41166 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41168 this.prevIndicator.show();
41170 if(this.indicator == 1){
41171 this.prevIndicator.hide();
41174 this.nextIndicator.show();
41176 if(this.indicator == this.files.length){
41177 this.nextIndicator.hide();
41180 this.thumbEl.scrollTo('top');
41182 this.fireEvent('update', this);
41185 onClick : function(e)
41187 e.preventDefault();
41189 this.fireEvent('click', this);
41194 e.preventDefault();
41196 this.indicator = Math.max(1, this.indicator - 1);
41203 e.preventDefault();
41205 this.indicator = Math.min(this.files.length, this.indicator + 1);
41219 * @class Roo.bootstrap.form.RadioSet
41220 * @extends Roo.bootstrap.form.Input
41221 * @children Roo.bootstrap.form.Radio
41222 * Bootstrap RadioSet class
41223 * @cfg {String} indicatorpos (left|right) default left
41224 * @cfg {Boolean} inline (true|false) inline the element (default true)
41225 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41227 * Create a new RadioSet
41228 * @param {Object} config The config object
41231 Roo.bootstrap.form.RadioSet = function(config){
41233 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41237 Roo.bootstrap.form.RadioSet.register(this);
41242 * Fires when the element is checked or unchecked.
41243 * @param {Roo.bootstrap.form.RadioSet} this This radio
41244 * @param {Roo.bootstrap.form.Radio} item The checked item
41249 * Fires when the element is click.
41250 * @param {Roo.bootstrap.form.RadioSet} this This radio set
41251 * @param {Roo.bootstrap.form.Radio} item The checked item
41252 * @param {Roo.EventObject} e The event object
41259 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
41267 indicatorpos : 'left',
41269 getAutoCreate : function()
41273 cls : 'roo-radio-set-label',
41277 html : this.fieldLabel
41281 if (Roo.bootstrap.version == 3) {
41284 if(this.indicatorpos == 'left'){
41287 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41288 tooltip : 'This field is required'
41293 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41294 tooltip : 'This field is required'
41300 cls : 'roo-radio-set-items'
41303 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41305 if (align === 'left' && this.fieldLabel.length) {
41308 cls : "roo-radio-set-right",
41314 if(this.labelWidth > 12){
41315 label.style = "width: " + this.labelWidth + 'px';
41318 if(this.labelWidth < 13 && this.labelmd == 0){
41319 this.labelmd = this.labelWidth;
41322 if(this.labellg > 0){
41323 label.cls += ' col-lg-' + this.labellg;
41324 items.cls += ' col-lg-' + (12 - this.labellg);
41327 if(this.labelmd > 0){
41328 label.cls += ' col-md-' + this.labelmd;
41329 items.cls += ' col-md-' + (12 - this.labelmd);
41332 if(this.labelsm > 0){
41333 label.cls += ' col-sm-' + this.labelsm;
41334 items.cls += ' col-sm-' + (12 - this.labelsm);
41337 if(this.labelxs > 0){
41338 label.cls += ' col-xs-' + this.labelxs;
41339 items.cls += ' col-xs-' + (12 - this.labelxs);
41345 cls : 'roo-radio-set',
41349 cls : 'roo-radio-set-input',
41352 value : this.value ? this.value : ''
41359 if(this.weight.length){
41360 cfg.cls += ' roo-radio-' + this.weight;
41364 cfg.cls += ' roo-radio-set-inline';
41368 ['xs','sm','md','lg'].map(function(size){
41369 if (settings[size]) {
41370 cfg.cls += ' col-' + size + '-' + settings[size];
41378 initEvents : function()
41380 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41381 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41383 if(!this.fieldLabel.length){
41384 this.labelEl.hide();
41387 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41388 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41390 this.indicator = this.indicatorEl();
41392 if(this.indicator){
41393 this.indicator.addClass('invisible');
41396 this.originalValue = this.getValue();
41400 inputEl: function ()
41402 return this.el.select('.roo-radio-set-input', true).first();
41405 getChildContainer : function()
41407 return this.itemsEl;
41410 register : function(item)
41412 this.radioes.push(item);
41416 validate : function()
41418 if(this.getVisibilityEl().hasClass('hidden')){
41424 Roo.each(this.radioes, function(i){
41433 if(this.allowBlank) {
41437 if(this.disabled || valid){
41442 this.markInvalid();
41447 markValid : function()
41449 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41450 this.indicatorEl().removeClass('visible');
41451 this.indicatorEl().addClass('invisible');
41455 if (Roo.bootstrap.version == 3) {
41456 this.el.removeClass([this.invalidClass, this.validClass]);
41457 this.el.addClass(this.validClass);
41459 this.el.removeClass(['is-invalid','is-valid']);
41460 this.el.addClass(['is-valid']);
41462 this.fireEvent('valid', this);
41465 markInvalid : function(msg)
41467 if(this.allowBlank || this.disabled){
41471 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41472 this.indicatorEl().removeClass('invisible');
41473 this.indicatorEl().addClass('visible');
41475 if (Roo.bootstrap.version == 3) {
41476 this.el.removeClass([this.invalidClass, this.validClass]);
41477 this.el.addClass(this.invalidClass);
41479 this.el.removeClass(['is-invalid','is-valid']);
41480 this.el.addClass(['is-invalid']);
41483 this.fireEvent('invalid', this, msg);
41487 setValue : function(v, suppressEvent)
41489 if(this.value === v){
41496 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41499 Roo.each(this.radioes, function(i){
41501 i.el.removeClass('checked');
41504 Roo.each(this.radioes, function(i){
41506 if(i.value === v || i.value.toString() === v.toString()){
41508 i.el.addClass('checked');
41510 if(suppressEvent !== true){
41511 this.fireEvent('check', this, i);
41522 clearInvalid : function(){
41524 if(!this.el || this.preventMark){
41528 this.el.removeClass([this.invalidClass]);
41530 this.fireEvent('valid', this);
41535 Roo.apply(Roo.bootstrap.form.RadioSet, {
41539 register : function(set)
41541 this.groups[set.name] = set;
41544 get: function(name)
41546 if (typeof(this.groups[name]) == 'undefined') {
41550 return this.groups[name] ;
41556 * Ext JS Library 1.1.1
41557 * Copyright(c) 2006-2007, Ext JS, LLC.
41559 * Originally Released Under LGPL - original licence link has changed is not relivant.
41562 * <script type="text/javascript">
41567 * @class Roo.bootstrap.SplitBar
41568 * @extends Roo.util.Observable
41569 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41573 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41574 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41575 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41576 split.minSize = 100;
41577 split.maxSize = 600;
41578 split.animate = true;
41579 split.on('moved', splitterMoved);
41582 * Create a new SplitBar
41583 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
41584 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
41585 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41586 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
41587 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41588 position of the SplitBar).
41590 Roo.bootstrap.SplitBar = function(cfg){
41595 // dragElement : elm
41596 // resizingElement: el,
41598 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41599 // placement : Roo.bootstrap.SplitBar.LEFT ,
41600 // existingProxy ???
41603 this.el = Roo.get(cfg.dragElement, true);
41604 this.el.dom.unselectable = "on";
41606 this.resizingEl = Roo.get(cfg.resizingElement, true);
41610 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41611 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41614 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41617 * The minimum size of the resizing element. (Defaults to 0)
41623 * The maximum size of the resizing element. (Defaults to 2000)
41626 this.maxSize = 2000;
41629 * Whether to animate the transition to the new size
41632 this.animate = false;
41635 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41638 this.useShim = false;
41643 if(!cfg.existingProxy){
41645 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41647 this.proxy = Roo.get(cfg.existingProxy).dom;
41650 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41653 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41656 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41659 this.dragSpecs = {};
41662 * @private The adapter to use to positon and resize elements
41664 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41665 this.adapter.init(this);
41667 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41669 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41670 this.el.addClass("roo-splitbar-h");
41673 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41674 this.el.addClass("roo-splitbar-v");
41680 * Fires when the splitter is moved (alias for {@link #event-moved})
41681 * @param {Roo.bootstrap.SplitBar} this
41682 * @param {Number} newSize the new width or height
41687 * Fires when the splitter is moved
41688 * @param {Roo.bootstrap.SplitBar} this
41689 * @param {Number} newSize the new width or height
41693 * @event beforeresize
41694 * Fires before the splitter is dragged
41695 * @param {Roo.bootstrap.SplitBar} this
41697 "beforeresize" : true,
41699 "beforeapply" : true
41702 Roo.util.Observable.call(this);
41705 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41706 onStartProxyDrag : function(x, y){
41707 this.fireEvent("beforeresize", this);
41709 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
41711 o.enableDisplayMode("block");
41712 // all splitbars share the same overlay
41713 Roo.bootstrap.SplitBar.prototype.overlay = o;
41715 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41716 this.overlay.show();
41717 Roo.get(this.proxy).setDisplayed("block");
41718 var size = this.adapter.getElementSize(this);
41719 this.activeMinSize = this.getMinimumSize();;
41720 this.activeMaxSize = this.getMaximumSize();;
41721 var c1 = size - this.activeMinSize;
41722 var c2 = Math.max(this.activeMaxSize - size, 0);
41723 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41724 this.dd.resetConstraints();
41725 this.dd.setXConstraint(
41726 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
41727 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41729 this.dd.setYConstraint(0, 0);
41731 this.dd.resetConstraints();
41732 this.dd.setXConstraint(0, 0);
41733 this.dd.setYConstraint(
41734 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
41735 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41738 this.dragSpecs.startSize = size;
41739 this.dragSpecs.startPoint = [x, y];
41740 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41744 * @private Called after the drag operation by the DDProxy
41746 onEndProxyDrag : function(e){
41747 Roo.get(this.proxy).setDisplayed(false);
41748 var endPoint = Roo.lib.Event.getXY(e);
41750 this.overlay.hide();
41753 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41754 newSize = this.dragSpecs.startSize +
41755 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41756 endPoint[0] - this.dragSpecs.startPoint[0] :
41757 this.dragSpecs.startPoint[0] - endPoint[0]
41760 newSize = this.dragSpecs.startSize +
41761 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41762 endPoint[1] - this.dragSpecs.startPoint[1] :
41763 this.dragSpecs.startPoint[1] - endPoint[1]
41766 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41767 if(newSize != this.dragSpecs.startSize){
41768 if(this.fireEvent('beforeapply', this, newSize) !== false){
41769 this.adapter.setElementSize(this, newSize);
41770 this.fireEvent("moved", this, newSize);
41771 this.fireEvent("resize", this, newSize);
41777 * Get the adapter this SplitBar uses
41778 * @return The adapter object
41780 getAdapter : function(){
41781 return this.adapter;
41785 * Set the adapter this SplitBar uses
41786 * @param {Object} adapter A SplitBar adapter object
41788 setAdapter : function(adapter){
41789 this.adapter = adapter;
41790 this.adapter.init(this);
41794 * Gets the minimum size for the resizing element
41795 * @return {Number} The minimum size
41797 getMinimumSize : function(){
41798 return this.minSize;
41802 * Sets the minimum size for the resizing element
41803 * @param {Number} minSize The minimum size
41805 setMinimumSize : function(minSize){
41806 this.minSize = minSize;
41810 * Gets the maximum size for the resizing element
41811 * @return {Number} The maximum size
41813 getMaximumSize : function(){
41814 return this.maxSize;
41818 * Sets the maximum size for the resizing element
41819 * @param {Number} maxSize The maximum size
41821 setMaximumSize : function(maxSize){
41822 this.maxSize = maxSize;
41826 * Sets the initialize size for the resizing element
41827 * @param {Number} size The initial size
41829 setCurrentSize : function(size){
41830 var oldAnimate = this.animate;
41831 this.animate = false;
41832 this.adapter.setElementSize(this, size);
41833 this.animate = oldAnimate;
41837 * Destroy this splitbar.
41838 * @param {Boolean} removeEl True to remove the element
41840 destroy : function(removeEl){
41842 this.shim.remove();
41845 this.proxy.parentNode.removeChild(this.proxy);
41853 * @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.
41855 Roo.bootstrap.SplitBar.createProxy = function(dir){
41856 var proxy = new Roo.Element(document.createElement("div"));
41857 proxy.unselectable();
41858 var cls = 'roo-splitbar-proxy';
41859 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41860 document.body.appendChild(proxy.dom);
41865 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41866 * Default Adapter. It assumes the splitter and resizing element are not positioned
41867 * elements and only gets/sets the width of the element. Generally used for table based layouts.
41869 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41872 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41873 // do nothing for now
41874 init : function(s){
41878 * Called before drag operations to get the current size of the resizing element.
41879 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41881 getElementSize : function(s){
41882 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41883 return s.resizingEl.getWidth();
41885 return s.resizingEl.getHeight();
41890 * Called after drag operations to set the size of the resizing element.
41891 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41892 * @param {Number} newSize The new size to set
41893 * @param {Function} onComplete A function to be invoked when resizing is complete
41895 setElementSize : function(s, newSize, onComplete){
41896 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41898 s.resizingEl.setWidth(newSize);
41900 onComplete(s, newSize);
41903 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41908 s.resizingEl.setHeight(newSize);
41910 onComplete(s, newSize);
41913 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41920 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41921 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41922 * Adapter that moves the splitter element to align with the resized sizing element.
41923 * Used with an absolute positioned SplitBar.
41924 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41925 * document.body, make sure you assign an id to the body element.
41927 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41928 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41929 this.container = Roo.get(container);
41932 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41933 init : function(s){
41934 this.basic.init(s);
41937 getElementSize : function(s){
41938 return this.basic.getElementSize(s);
41941 setElementSize : function(s, newSize, onComplete){
41942 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41945 moveSplitter : function(s){
41946 var yes = Roo.bootstrap.SplitBar;
41947 switch(s.placement){
41949 s.el.setX(s.resizingEl.getRight());
41952 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41955 s.el.setY(s.resizingEl.getBottom());
41958 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
41965 * Orientation constant - Create a vertical SplitBar
41969 Roo.bootstrap.SplitBar.VERTICAL = 1;
41972 * Orientation constant - Create a horizontal SplitBar
41976 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
41979 * Placement constant - The resizing element is to the left of the splitter element
41983 Roo.bootstrap.SplitBar.LEFT = 1;
41986 * Placement constant - The resizing element is to the right of the splitter element
41990 Roo.bootstrap.SplitBar.RIGHT = 2;
41993 * Placement constant - The resizing element is positioned above the splitter element
41997 Roo.bootstrap.SplitBar.TOP = 3;
42000 * Placement constant - The resizing element is positioned under splitter element
42004 Roo.bootstrap.SplitBar.BOTTOM = 4;
42007 * Ext JS Library 1.1.1
42008 * Copyright(c) 2006-2007, Ext JS, LLC.
42010 * Originally Released Under LGPL - original licence link has changed is not relivant.
42013 * <script type="text/javascript">
42017 * @class Roo.bootstrap.layout.Manager
42018 * @extends Roo.bootstrap.Component
42020 * Base class for layout managers.
42022 Roo.bootstrap.layout.Manager = function(config)
42024 this.monitorWindowResize = true; // do this before we apply configuration.
42026 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42032 /** false to disable window resize monitoring @type Boolean */
42038 * Fires when a layout is performed.
42039 * @param {Roo.LayoutManager} this
42043 * @event regionresized
42044 * Fires when the user resizes a region.
42045 * @param {Roo.LayoutRegion} region The resized region
42046 * @param {Number} newSize The new size (width for east/west, height for north/south)
42048 "regionresized" : true,
42050 * @event regioncollapsed
42051 * Fires when a region is collapsed.
42052 * @param {Roo.LayoutRegion} region The collapsed region
42054 "regioncollapsed" : true,
42056 * @event regionexpanded
42057 * Fires when a region is expanded.
42058 * @param {Roo.LayoutRegion} region The expanded region
42060 "regionexpanded" : true
42062 this.updating = false;
42065 this.el = Roo.get(config.el);
42071 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42076 monitorWindowResize : true,
42082 onRender : function(ct, position)
42085 this.el = Roo.get(ct);
42088 //this.fireEvent('render',this);
42092 initEvents: function()
42096 // ie scrollbar fix
42097 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42098 document.body.scroll = "no";
42099 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42100 this.el.position('relative');
42102 this.id = this.el.id;
42103 this.el.addClass("roo-layout-container");
42104 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42105 if(this.el.dom != document.body ) {
42106 this.el.on('resize', this.layout,this);
42107 this.el.on('show', this.layout,this);
42113 * Returns true if this layout is currently being updated
42114 * @return {Boolean}
42116 isUpdating : function(){
42117 return this.updating;
42121 * Suspend the LayoutManager from doing auto-layouts while
42122 * making multiple add or remove calls
42124 beginUpdate : function(){
42125 this.updating = true;
42129 * Restore auto-layouts and optionally disable the manager from performing a layout
42130 * @param {Boolean} noLayout true to disable a layout update
42132 endUpdate : function(noLayout){
42133 this.updating = false;
42139 layout: function(){
42143 onRegionResized : function(region, newSize){
42144 this.fireEvent("regionresized", region, newSize);
42148 onRegionCollapsed : function(region){
42149 this.fireEvent("regioncollapsed", region);
42152 onRegionExpanded : function(region){
42153 this.fireEvent("regionexpanded", region);
42157 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42158 * performs box-model adjustments.
42159 * @return {Object} The size as an object {width: (the width), height: (the height)}
42161 getViewSize : function()
42164 if(this.el.dom != document.body){
42165 size = this.el.getSize();
42167 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42169 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42170 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42175 * Returns the Element this layout is bound to.
42176 * @return {Roo.Element}
42178 getEl : function(){
42183 * Returns the specified region.
42184 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42185 * @return {Roo.LayoutRegion}
42187 getRegion : function(target){
42188 return this.regions[target.toLowerCase()];
42191 onWindowResize : function(){
42192 if(this.monitorWindowResize){
42199 * Ext JS Library 1.1.1
42200 * Copyright(c) 2006-2007, Ext JS, LLC.
42202 * Originally Released Under LGPL - original licence link has changed is not relivant.
42205 * <script type="text/javascript">
42208 * @class Roo.bootstrap.layout.Border
42209 * @extends Roo.bootstrap.layout.Manager
42210 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42211 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42212 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42213 * please see: examples/bootstrap/nested.html<br><br>
42215 <b>The container the layout is rendered into can be either the body element or any other element.
42216 If it is not the body element, the container needs to either be an absolute positioned element,
42217 or you will need to add "position:relative" to the css of the container. You will also need to specify
42218 the container size if it is not the body element.</b>
42221 * Create a new Border
42222 * @param {Object} config Configuration options
42224 Roo.bootstrap.layout.Border = function(config){
42225 config = config || {};
42226 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42230 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42231 if(config[region]){
42232 config[region].region = region;
42233 this.addRegion(config[region]);
42239 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
42241 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42244 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42247 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42250 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42253 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42256 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42262 parent : false, // this might point to a 'nest' or a ???
42265 * Creates and adds a new region if it doesn't already exist.
42266 * @param {String} target The target region key (north, south, east, west or center).
42267 * @param {Object} config The regions config object
42268 * @return {BorderLayoutRegion} The new region
42270 addRegion : function(config)
42272 if(!this.regions[config.region]){
42273 var r = this.factory(config);
42274 this.bindRegion(r);
42276 return this.regions[config.region];
42280 bindRegion : function(r){
42281 this.regions[r.config.region] = r;
42283 r.on("visibilitychange", this.layout, this);
42284 r.on("paneladded", this.layout, this);
42285 r.on("panelremoved", this.layout, this);
42286 r.on("invalidated", this.layout, this);
42287 r.on("resized", this.onRegionResized, this);
42288 r.on("collapsed", this.onRegionCollapsed, this);
42289 r.on("expanded", this.onRegionExpanded, this);
42293 * Performs a layout update.
42295 layout : function()
42297 if(this.updating) {
42301 // render all the rebions if they have not been done alreayd?
42302 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42303 if(this.regions[region] && !this.regions[region].bodyEl){
42304 this.regions[region].onRender(this.el)
42308 var size = this.getViewSize();
42309 var w = size.width;
42310 var h = size.height;
42315 //var x = 0, y = 0;
42317 var rs = this.regions;
42318 var north = rs["north"];
42319 var south = rs["south"];
42320 var west = rs["west"];
42321 var east = rs["east"];
42322 var center = rs["center"];
42323 //if(this.hideOnLayout){ // not supported anymore
42324 //c.el.setStyle("display", "none");
42326 if(north && north.isVisible()){
42327 var b = north.getBox();
42328 var m = north.getMargins();
42329 b.width = w - (m.left+m.right);
42332 centerY = b.height + b.y + m.bottom;
42333 centerH -= centerY;
42334 north.updateBox(this.safeBox(b));
42336 if(south && south.isVisible()){
42337 var b = south.getBox();
42338 var m = south.getMargins();
42339 b.width = w - (m.left+m.right);
42341 var totalHeight = (b.height + m.top + m.bottom);
42342 b.y = h - totalHeight + m.top;
42343 centerH -= totalHeight;
42344 south.updateBox(this.safeBox(b));
42346 if(west && west.isVisible()){
42347 var b = west.getBox();
42348 var m = west.getMargins();
42349 b.height = centerH - (m.top+m.bottom);
42351 b.y = centerY + m.top;
42352 var totalWidth = (b.width + m.left + m.right);
42353 centerX += totalWidth;
42354 centerW -= totalWidth;
42355 west.updateBox(this.safeBox(b));
42357 if(east && east.isVisible()){
42358 var b = east.getBox();
42359 var m = east.getMargins();
42360 b.height = centerH - (m.top+m.bottom);
42361 var totalWidth = (b.width + m.left + m.right);
42362 b.x = w - totalWidth + m.left;
42363 b.y = centerY + m.top;
42364 centerW -= totalWidth;
42365 east.updateBox(this.safeBox(b));
42368 var m = center.getMargins();
42370 x: centerX + m.left,
42371 y: centerY + m.top,
42372 width: centerW - (m.left+m.right),
42373 height: centerH - (m.top+m.bottom)
42375 //if(this.hideOnLayout){
42376 //center.el.setStyle("display", "block");
42378 center.updateBox(this.safeBox(centerBox));
42381 this.fireEvent("layout", this);
42385 safeBox : function(box){
42386 box.width = Math.max(0, box.width);
42387 box.height = Math.max(0, box.height);
42392 * Adds a ContentPanel (or subclass) to this layout.
42393 * @param {String} target The target region key (north, south, east, west or center).
42394 * @param {Roo.ContentPanel} panel The panel to add
42395 * @return {Roo.ContentPanel} The added panel
42397 add : function(target, panel){
42399 target = target.toLowerCase();
42400 return this.regions[target].add(panel);
42404 * Remove a ContentPanel (or subclass) to this layout.
42405 * @param {String} target The target region key (north, south, east, west or center).
42406 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42407 * @return {Roo.ContentPanel} The removed panel
42409 remove : function(target, panel){
42410 target = target.toLowerCase();
42411 return this.regions[target].remove(panel);
42415 * Searches all regions for a panel with the specified id
42416 * @param {String} panelId
42417 * @return {Roo.ContentPanel} The panel or null if it wasn't found
42419 findPanel : function(panelId){
42420 var rs = this.regions;
42421 for(var target in rs){
42422 if(typeof rs[target] != "function"){
42423 var p = rs[target].getPanel(panelId);
42433 * Searches all regions for a panel with the specified id and activates (shows) it.
42434 * @param {String/ContentPanel} panelId The panels id or the panel itself
42435 * @return {Roo.ContentPanel} The shown panel or null
42437 showPanel : function(panelId) {
42438 var rs = this.regions;
42439 for(var target in rs){
42440 var r = rs[target];
42441 if(typeof r != "function"){
42442 if(r.hasPanel(panelId)){
42443 return r.showPanel(panelId);
42451 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42452 * @param {Roo.state.Provider} provider (optional) An alternate state provider
42455 restoreState : function(provider){
42457 provider = Roo.state.Manager;
42459 var sm = new Roo.LayoutStateManager();
42460 sm.init(this, provider);
42466 * Adds a xtype elements to the layout.
42470 xtype : 'ContentPanel',
42477 xtype : 'NestedLayoutPanel',
42483 items : [ ... list of content panels or nested layout panels.. ]
42487 * @param {Object} cfg Xtype definition of item to add.
42489 addxtype : function(cfg)
42491 // basically accepts a pannel...
42492 // can accept a layout region..!?!?
42493 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42496 // theory? children can only be panels??
42498 //if (!cfg.xtype.match(/Panel$/)) {
42503 if (typeof(cfg.region) == 'undefined') {
42504 Roo.log("Failed to add Panel, region was not set");
42508 var region = cfg.region;
42514 xitems = cfg.items;
42519 if ( region == 'center') {
42520 Roo.log("Center: " + cfg.title);
42526 case 'Content': // ContentPanel (el, cfg)
42527 case 'Scroll': // ContentPanel (el, cfg)
42529 cfg.autoCreate = cfg.autoCreate || true;
42530 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42532 // var el = this.el.createChild();
42533 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42536 this.add(region, ret);
42540 case 'TreePanel': // our new panel!
42541 cfg.el = this.el.createChild();
42542 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42543 this.add(region, ret);
42548 // create a new Layout (which is a Border Layout...
42550 var clayout = cfg.layout;
42551 clayout.el = this.el.createChild();
42552 clayout.items = clayout.items || [];
42556 // replace this exitems with the clayout ones..
42557 xitems = clayout.items;
42559 // force background off if it's in center...
42560 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42561 cfg.background = false;
42563 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
42566 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42567 //console.log('adding nested layout panel ' + cfg.toSource());
42568 this.add(region, ret);
42569 nb = {}; /// find first...
42574 // needs grid and region
42576 //var el = this.getRegion(region).el.createChild();
42578 *var el = this.el.createChild();
42579 // create the grid first...
42580 cfg.grid.container = el;
42581 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42584 if (region == 'center' && this.active ) {
42585 cfg.background = false;
42588 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42590 this.add(region, ret);
42592 if (cfg.background) {
42593 // render grid on panel activation (if panel background)
42594 ret.on('activate', function(gp) {
42595 if (!gp.grid.rendered) {
42596 // gp.grid.render(el);
42600 // cfg.grid.render(el);
42606 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42607 // it was the old xcomponent building that caused this before.
42608 // espeically if border is the top element in the tree.
42618 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42620 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42621 this.add(region, ret);
42625 throw "Can not add '" + cfg.xtype + "' to Border";
42631 this.beginUpdate();
42635 Roo.each(xitems, function(i) {
42636 region = nb && i.region ? i.region : false;
42638 var add = ret.addxtype(i);
42641 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42642 if (!i.background) {
42643 abn[region] = nb[region] ;
42650 // make the last non-background panel active..
42651 //if (nb) { Roo.log(abn); }
42654 for(var r in abn) {
42655 region = this.getRegion(r);
42657 // tried using nb[r], but it does not work..
42659 region.showPanel(abn[r]);
42670 factory : function(cfg)
42673 var validRegions = Roo.bootstrap.layout.Border.regions;
42675 var target = cfg.region;
42678 var r = Roo.bootstrap.layout;
42682 return new r.North(cfg);
42684 return new r.South(cfg);
42686 return new r.East(cfg);
42688 return new r.West(cfg);
42690 return new r.Center(cfg);
42692 throw 'Layout region "'+target+'" not supported.';
42699 * Ext JS Library 1.1.1
42700 * Copyright(c) 2006-2007, Ext JS, LLC.
42702 * Originally Released Under LGPL - original licence link has changed is not relivant.
42705 * <script type="text/javascript">
42709 * @class Roo.bootstrap.layout.Basic
42710 * @extends Roo.util.Observable
42711 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42712 * and does not have a titlebar, tabs or any other features. All it does is size and position
42713 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42714 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
42715 * @cfg {string} region the region that it inhabits..
42716 * @cfg {bool} skipConfig skip config?
42720 Roo.bootstrap.layout.Basic = function(config){
42722 this.mgr = config.mgr;
42724 this.position = config.region;
42726 var skipConfig = config.skipConfig;
42730 * @scope Roo.BasicLayoutRegion
42734 * @event beforeremove
42735 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42736 * @param {Roo.LayoutRegion} this
42737 * @param {Roo.ContentPanel} panel The panel
42738 * @param {Object} e The cancel event object
42740 "beforeremove" : true,
42742 * @event invalidated
42743 * Fires when the layout for this region is changed.
42744 * @param {Roo.LayoutRegion} this
42746 "invalidated" : true,
42748 * @event visibilitychange
42749 * Fires when this region is shown or hidden
42750 * @param {Roo.LayoutRegion} this
42751 * @param {Boolean} visibility true or false
42753 "visibilitychange" : true,
42755 * @event paneladded
42756 * Fires when a panel is added.
42757 * @param {Roo.LayoutRegion} this
42758 * @param {Roo.ContentPanel} panel The panel
42760 "paneladded" : true,
42762 * @event panelremoved
42763 * Fires when a panel is removed.
42764 * @param {Roo.LayoutRegion} this
42765 * @param {Roo.ContentPanel} panel The panel
42767 "panelremoved" : true,
42769 * @event beforecollapse
42770 * Fires when this region before collapse.
42771 * @param {Roo.LayoutRegion} this
42773 "beforecollapse" : true,
42776 * Fires when this region is collapsed.
42777 * @param {Roo.LayoutRegion} this
42779 "collapsed" : true,
42782 * Fires when this region is expanded.
42783 * @param {Roo.LayoutRegion} this
42788 * Fires when this region is slid into view.
42789 * @param {Roo.LayoutRegion} this
42791 "slideshow" : true,
42794 * Fires when this region slides out of view.
42795 * @param {Roo.LayoutRegion} this
42797 "slidehide" : true,
42799 * @event panelactivated
42800 * Fires when a panel is activated.
42801 * @param {Roo.LayoutRegion} this
42802 * @param {Roo.ContentPanel} panel The activated panel
42804 "panelactivated" : true,
42807 * Fires when the user resizes this region.
42808 * @param {Roo.LayoutRegion} this
42809 * @param {Number} newSize The new size (width for east/west, height for north/south)
42813 /** A collection of panels in this region. @type Roo.util.MixedCollection */
42814 this.panels = new Roo.util.MixedCollection();
42815 this.panels.getKey = this.getPanelId.createDelegate(this);
42817 this.activePanel = null;
42818 // ensure listeners are added...
42820 if (config.listeners || config.events) {
42821 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42822 listeners : config.listeners || {},
42823 events : config.events || {}
42827 if(skipConfig !== true){
42828 this.applyConfig(config);
42832 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42834 getPanelId : function(p){
42838 applyConfig : function(config){
42839 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42840 this.config = config;
42845 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
42846 * the width, for horizontal (north, south) the height.
42847 * @param {Number} newSize The new width or height
42849 resizeTo : function(newSize){
42850 var el = this.el ? this.el :
42851 (this.activePanel ? this.activePanel.getEl() : null);
42853 switch(this.position){
42856 el.setWidth(newSize);
42857 this.fireEvent("resized", this, newSize);
42861 el.setHeight(newSize);
42862 this.fireEvent("resized", this, newSize);
42868 getBox : function(){
42869 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42872 getMargins : function(){
42873 return this.margins;
42876 updateBox : function(box){
42878 var el = this.activePanel.getEl();
42879 el.dom.style.left = box.x + "px";
42880 el.dom.style.top = box.y + "px";
42881 this.activePanel.setSize(box.width, box.height);
42885 * Returns the container element for this region.
42886 * @return {Roo.Element}
42888 getEl : function(){
42889 return this.activePanel;
42893 * Returns true if this region is currently visible.
42894 * @return {Boolean}
42896 isVisible : function(){
42897 return this.activePanel ? true : false;
42900 setActivePanel : function(panel){
42901 panel = this.getPanel(panel);
42902 if(this.activePanel && this.activePanel != panel){
42903 this.activePanel.setActiveState(false);
42904 this.activePanel.getEl().setLeftTop(-10000,-10000);
42906 this.activePanel = panel;
42907 panel.setActiveState(true);
42909 panel.setSize(this.box.width, this.box.height);
42911 this.fireEvent("panelactivated", this, panel);
42912 this.fireEvent("invalidated");
42916 * Show the specified panel.
42917 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42918 * @return {Roo.ContentPanel} The shown panel or null
42920 showPanel : function(panel){
42921 panel = this.getPanel(panel);
42923 this.setActivePanel(panel);
42929 * Get the active panel for this region.
42930 * @return {Roo.ContentPanel} The active panel or null
42932 getActivePanel : function(){
42933 return this.activePanel;
42937 * Add the passed ContentPanel(s)
42938 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42939 * @return {Roo.ContentPanel} The panel added (if only one was added)
42941 add : function(panel){
42942 if(arguments.length > 1){
42943 for(var i = 0, len = arguments.length; i < len; i++) {
42944 this.add(arguments[i]);
42948 if(this.hasPanel(panel)){
42949 this.showPanel(panel);
42952 var el = panel.getEl();
42953 if(el.dom.parentNode != this.mgr.el.dom){
42954 this.mgr.el.dom.appendChild(el.dom);
42956 if(panel.setRegion){
42957 panel.setRegion(this);
42959 this.panels.add(panel);
42960 el.setStyle("position", "absolute");
42961 if(!panel.background){
42962 this.setActivePanel(panel);
42963 if(this.config.initialSize && this.panels.getCount()==1){
42964 this.resizeTo(this.config.initialSize);
42967 this.fireEvent("paneladded", this, panel);
42972 * Returns true if the panel is in this region.
42973 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42974 * @return {Boolean}
42976 hasPanel : function(panel){
42977 if(typeof panel == "object"){ // must be panel obj
42978 panel = panel.getId();
42980 return this.getPanel(panel) ? true : false;
42984 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42985 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
42986 * @param {Boolean} preservePanel Overrides the config preservePanel option
42987 * @return {Roo.ContentPanel} The panel that was removed
42989 remove : function(panel, preservePanel){
42990 panel = this.getPanel(panel);
42995 this.fireEvent("beforeremove", this, panel, e);
42996 if(e.cancel === true){
42999 var panelId = panel.getId();
43000 this.panels.removeKey(panelId);
43005 * Returns the panel specified or null if it's not in this region.
43006 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43007 * @return {Roo.ContentPanel}
43009 getPanel : function(id){
43010 if(typeof id == "object"){ // must be panel obj
43013 return this.panels.get(id);
43017 * Returns this regions position (north/south/east/west/center).
43020 getPosition: function(){
43021 return this.position;
43025 * Ext JS Library 1.1.1
43026 * Copyright(c) 2006-2007, Ext JS, LLC.
43028 * Originally Released Under LGPL - original licence link has changed is not relivant.
43031 * <script type="text/javascript">
43035 * @class Roo.bootstrap.layout.Region
43036 * @extends Roo.bootstrap.layout.Basic
43037 * This class represents a region in a layout manager.
43039 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43040 * @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})
43041 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43042 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43043 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43044 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43045 * @cfg {String} title The title for the region (overrides panel titles)
43046 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43047 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43048 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43049 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43050 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43051 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43052 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43053 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43054 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43055 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43057 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43058 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43059 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43060 * @cfg {Number} width For East/West panels
43061 * @cfg {Number} height For North/South panels
43062 * @cfg {Boolean} split To show the splitter
43063 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43065 * @cfg {string} cls Extra CSS classes to add to region
43067 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43068 * @cfg {string} region the region that it inhabits..
43071 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43072 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43074 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43075 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43076 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43078 Roo.bootstrap.layout.Region = function(config)
43080 this.applyConfig(config);
43082 var mgr = config.mgr;
43083 var pos = config.region;
43084 config.skipConfig = true;
43085 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43088 this.onRender(mgr.el);
43091 this.visible = true;
43092 this.collapsed = false;
43093 this.unrendered_panels = [];
43096 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43098 position: '', // set by wrapper (eg. north/south etc..)
43099 unrendered_panels : null, // unrendered panels.
43101 tabPosition : false,
43103 mgr: false, // points to 'Border'
43106 createBody : function(){
43107 /** This region's body element
43108 * @type Roo.Element */
43109 this.bodyEl = this.el.createChild({
43111 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43115 onRender: function(ctr, pos)
43117 var dh = Roo.DomHelper;
43118 /** This region's container element
43119 * @type Roo.Element */
43120 this.el = dh.append(ctr.dom, {
43122 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43124 /** This region's title element
43125 * @type Roo.Element */
43127 this.titleEl = dh.append(this.el.dom, {
43129 unselectable: "on",
43130 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43132 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43133 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43137 this.titleEl.enableDisplayMode();
43138 /** This region's title text element
43139 * @type HTMLElement */
43140 this.titleTextEl = this.titleEl.dom.firstChild;
43141 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43143 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43144 this.closeBtn.enableDisplayMode();
43145 this.closeBtn.on("click", this.closeClicked, this);
43146 this.closeBtn.hide();
43148 this.createBody(this.config);
43149 if(this.config.hideWhenEmpty){
43151 this.on("paneladded", this.validateVisibility, this);
43152 this.on("panelremoved", this.validateVisibility, this);
43154 if(this.autoScroll){
43155 this.bodyEl.setStyle("overflow", "auto");
43157 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43159 //if(c.titlebar !== false){
43160 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43161 this.titleEl.hide();
43163 this.titleEl.show();
43164 if(this.config.title){
43165 this.titleTextEl.innerHTML = this.config.title;
43169 if(this.config.collapsed){
43170 this.collapse(true);
43172 if(this.config.hidden){
43176 if (this.unrendered_panels && this.unrendered_panels.length) {
43177 for (var i =0;i< this.unrendered_panels.length; i++) {
43178 this.add(this.unrendered_panels[i]);
43180 this.unrendered_panels = null;
43186 applyConfig : function(c)
43189 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43190 var dh = Roo.DomHelper;
43191 if(c.titlebar !== false){
43192 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43193 this.collapseBtn.on("click", this.collapse, this);
43194 this.collapseBtn.enableDisplayMode();
43196 if(c.showPin === true || this.showPin){
43197 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43198 this.stickBtn.enableDisplayMode();
43199 this.stickBtn.on("click", this.expand, this);
43200 this.stickBtn.hide();
43205 /** This region's collapsed element
43206 * @type Roo.Element */
43209 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43210 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43213 if(c.floatable !== false){
43214 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43215 this.collapsedEl.on("click", this.collapseClick, this);
43218 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43219 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43220 id: "message", unselectable: "on", style:{"float":"left"}});
43221 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43223 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43224 this.expandBtn.on("click", this.expand, this);
43228 if(this.collapseBtn){
43229 this.collapseBtn.setVisible(c.collapsible == true);
43232 this.cmargins = c.cmargins || this.cmargins ||
43233 (this.position == "west" || this.position == "east" ?
43234 {top: 0, left: 2, right:2, bottom: 0} :
43235 {top: 2, left: 0, right:0, bottom: 2});
43237 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43240 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43242 this.autoScroll = c.autoScroll || false;
43247 this.duration = c.duration || .30;
43248 this.slideDuration = c.slideDuration || .45;
43253 * Returns true if this region is currently visible.
43254 * @return {Boolean}
43256 isVisible : function(){
43257 return this.visible;
43261 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43262 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
43264 //setCollapsedTitle : function(title){
43265 // title = title || " ";
43266 // if(this.collapsedTitleTextEl){
43267 // this.collapsedTitleTextEl.innerHTML = title;
43271 getBox : function(){
43273 // if(!this.collapsed){
43274 b = this.el.getBox(false, true);
43276 // b = this.collapsedEl.getBox(false, true);
43281 getMargins : function(){
43282 return this.margins;
43283 //return this.collapsed ? this.cmargins : this.margins;
43286 highlight : function(){
43287 this.el.addClass("x-layout-panel-dragover");
43290 unhighlight : function(){
43291 this.el.removeClass("x-layout-panel-dragover");
43294 updateBox : function(box)
43296 if (!this.bodyEl) {
43297 return; // not rendered yet..
43301 if(!this.collapsed){
43302 this.el.dom.style.left = box.x + "px";
43303 this.el.dom.style.top = box.y + "px";
43304 this.updateBody(box.width, box.height);
43306 this.collapsedEl.dom.style.left = box.x + "px";
43307 this.collapsedEl.dom.style.top = box.y + "px";
43308 this.collapsedEl.setSize(box.width, box.height);
43311 this.tabs.autoSizeTabs();
43315 updateBody : function(w, h)
43318 this.el.setWidth(w);
43319 w -= this.el.getBorderWidth("rl");
43320 if(this.config.adjustments){
43321 w += this.config.adjustments[0];
43324 if(h !== null && h > 0){
43325 this.el.setHeight(h);
43326 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43327 h -= this.el.getBorderWidth("tb");
43328 if(this.config.adjustments){
43329 h += this.config.adjustments[1];
43331 this.bodyEl.setHeight(h);
43333 h = this.tabs.syncHeight(h);
43336 if(this.panelSize){
43337 w = w !== null ? w : this.panelSize.width;
43338 h = h !== null ? h : this.panelSize.height;
43340 if(this.activePanel){
43341 var el = this.activePanel.getEl();
43342 w = w !== null ? w : el.getWidth();
43343 h = h !== null ? h : el.getHeight();
43344 this.panelSize = {width: w, height: h};
43345 this.activePanel.setSize(w, h);
43347 if(Roo.isIE && this.tabs){
43348 this.tabs.el.repaint();
43353 * Returns the container element for this region.
43354 * @return {Roo.Element}
43356 getEl : function(){
43361 * Hides this region.
43364 //if(!this.collapsed){
43365 this.el.dom.style.left = "-2000px";
43368 // this.collapsedEl.dom.style.left = "-2000px";
43369 // this.collapsedEl.hide();
43371 this.visible = false;
43372 this.fireEvent("visibilitychange", this, false);
43376 * Shows this region if it was previously hidden.
43379 //if(!this.collapsed){
43382 // this.collapsedEl.show();
43384 this.visible = true;
43385 this.fireEvent("visibilitychange", this, true);
43388 closeClicked : function(){
43389 if(this.activePanel){
43390 this.remove(this.activePanel);
43394 collapseClick : function(e){
43396 e.stopPropagation();
43399 e.stopPropagation();
43405 * Collapses this region.
43406 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43409 collapse : function(skipAnim, skipCheck = false){
43410 if(this.collapsed) {
43414 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43416 this.collapsed = true;
43418 this.split.el.hide();
43420 if(this.config.animate && skipAnim !== true){
43421 this.fireEvent("invalidated", this);
43422 this.animateCollapse();
43424 this.el.setLocation(-20000,-20000);
43426 this.collapsedEl.show();
43427 this.fireEvent("collapsed", this);
43428 this.fireEvent("invalidated", this);
43434 animateCollapse : function(){
43439 * Expands this region if it was previously collapsed.
43440 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43441 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43444 expand : function(e, skipAnim){
43446 e.stopPropagation();
43448 if(!this.collapsed || this.el.hasActiveFx()) {
43452 this.afterSlideIn();
43455 this.collapsed = false;
43456 if(this.config.animate && skipAnim !== true){
43457 this.animateExpand();
43461 this.split.el.show();
43463 this.collapsedEl.setLocation(-2000,-2000);
43464 this.collapsedEl.hide();
43465 this.fireEvent("invalidated", this);
43466 this.fireEvent("expanded", this);
43470 animateExpand : function(){
43474 initTabs : function()
43476 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43478 var ts = new Roo.bootstrap.panel.Tabs({
43479 el: this.bodyEl.dom,
43481 tabPosition: this.tabPosition ? this.tabPosition : 'top',
43482 disableTooltips: this.config.disableTabTips,
43483 toolbar : this.config.toolbar
43486 if(this.config.hideTabs){
43487 ts.stripWrap.setDisplayed(false);
43490 ts.resizeTabs = this.config.resizeTabs === true;
43491 ts.minTabWidth = this.config.minTabWidth || 40;
43492 ts.maxTabWidth = this.config.maxTabWidth || 250;
43493 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43494 ts.monitorResize = false;
43495 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43496 ts.bodyEl.addClass('roo-layout-tabs-body');
43497 this.panels.each(this.initPanelAsTab, this);
43500 initPanelAsTab : function(panel){
43501 var ti = this.tabs.addTab(
43505 this.config.closeOnTab && panel.isClosable(),
43508 if(panel.tabTip !== undefined){
43509 ti.setTooltip(panel.tabTip);
43511 ti.on("activate", function(){
43512 this.setActivePanel(panel);
43515 if(this.config.closeOnTab){
43516 ti.on("beforeclose", function(t, e){
43518 this.remove(panel);
43522 panel.tabItem = ti;
43527 updatePanelTitle : function(panel, title)
43529 if(this.activePanel == panel){
43530 this.updateTitle(title);
43533 var ti = this.tabs.getTab(panel.getEl().id);
43535 if(panel.tabTip !== undefined){
43536 ti.setTooltip(panel.tabTip);
43541 updateTitle : function(title){
43542 if(this.titleTextEl && !this.config.title){
43543 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
43547 setActivePanel : function(panel)
43549 panel = this.getPanel(panel);
43550 if(this.activePanel && this.activePanel != panel){
43551 if(this.activePanel.setActiveState(false) === false){
43555 this.activePanel = panel;
43556 panel.setActiveState(true);
43557 if(this.panelSize){
43558 panel.setSize(this.panelSize.width, this.panelSize.height);
43561 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43563 this.updateTitle(panel.getTitle());
43565 this.fireEvent("invalidated", this);
43567 this.fireEvent("panelactivated", this, panel);
43571 * Shows the specified panel.
43572 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43573 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43575 showPanel : function(panel)
43577 panel = this.getPanel(panel);
43580 var tab = this.tabs.getTab(panel.getEl().id);
43581 if(tab.isHidden()){
43582 this.tabs.unhideTab(tab.id);
43586 this.setActivePanel(panel);
43593 * Get the active panel for this region.
43594 * @return {Roo.ContentPanel} The active panel or null
43596 getActivePanel : function(){
43597 return this.activePanel;
43600 validateVisibility : function(){
43601 if(this.panels.getCount() < 1){
43602 this.updateTitle(" ");
43603 this.closeBtn.hide();
43606 if(!this.isVisible()){
43613 * Adds the passed ContentPanel(s) to this region.
43614 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43615 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43617 add : function(panel)
43619 if(arguments.length > 1){
43620 for(var i = 0, len = arguments.length; i < len; i++) {
43621 this.add(arguments[i]);
43626 // if we have not been rendered yet, then we can not really do much of this..
43627 if (!this.bodyEl) {
43628 this.unrendered_panels.push(panel);
43635 if(this.hasPanel(panel)){
43636 this.showPanel(panel);
43639 panel.setRegion(this);
43640 this.panels.add(panel);
43641 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43642 // sinle panel - no tab...?? would it not be better to render it with the tabs,
43643 // and hide them... ???
43644 this.bodyEl.dom.appendChild(panel.getEl().dom);
43645 if(panel.background !== true){
43646 this.setActivePanel(panel);
43648 this.fireEvent("paneladded", this, panel);
43655 this.initPanelAsTab(panel);
43659 if(panel.background !== true){
43660 this.tabs.activate(panel.getEl().id);
43662 this.fireEvent("paneladded", this, panel);
43667 * Hides the tab for the specified panel.
43668 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43670 hidePanel : function(panel){
43671 if(this.tabs && (panel = this.getPanel(panel))){
43672 this.tabs.hideTab(panel.getEl().id);
43677 * Unhides the tab for a previously hidden panel.
43678 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43680 unhidePanel : function(panel){
43681 if(this.tabs && (panel = this.getPanel(panel))){
43682 this.tabs.unhideTab(panel.getEl().id);
43686 clearPanels : function(){
43687 while(this.panels.getCount() > 0){
43688 this.remove(this.panels.first());
43693 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43694 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43695 * @param {Boolean} preservePanel Overrides the config preservePanel option
43696 * @return {Roo.ContentPanel} The panel that was removed
43698 remove : function(panel, preservePanel)
43700 panel = this.getPanel(panel);
43705 this.fireEvent("beforeremove", this, panel, e);
43706 if(e.cancel === true){
43709 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43710 var panelId = panel.getId();
43711 this.panels.removeKey(panelId);
43713 document.body.appendChild(panel.getEl().dom);
43716 this.tabs.removeTab(panel.getEl().id);
43717 }else if (!preservePanel){
43718 this.bodyEl.dom.removeChild(panel.getEl().dom);
43720 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43721 var p = this.panels.first();
43722 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43723 tempEl.appendChild(p.getEl().dom);
43724 this.bodyEl.update("");
43725 this.bodyEl.dom.appendChild(p.getEl().dom);
43727 this.updateTitle(p.getTitle());
43729 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43730 this.setActivePanel(p);
43732 panel.setRegion(null);
43733 if(this.activePanel == panel){
43734 this.activePanel = null;
43736 if(this.config.autoDestroy !== false && preservePanel !== true){
43737 try{panel.destroy();}catch(e){}
43739 this.fireEvent("panelremoved", this, panel);
43744 * Returns the TabPanel component used by this region
43745 * @return {Roo.TabPanel}
43747 getTabs : function(){
43751 createTool : function(parentEl, className){
43752 var btn = Roo.DomHelper.append(parentEl, {
43754 cls: "x-layout-tools-button",
43757 cls: "roo-layout-tools-button-inner " + className,
43761 btn.addClassOnOver("roo-layout-tools-button-over");
43766 * Ext JS Library 1.1.1
43767 * Copyright(c) 2006-2007, Ext JS, LLC.
43769 * Originally Released Under LGPL - original licence link has changed is not relivant.
43772 * <script type="text/javascript">
43778 * @class Roo.SplitLayoutRegion
43779 * @extends Roo.LayoutRegion
43780 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43782 Roo.bootstrap.layout.Split = function(config){
43783 this.cursor = config.cursor;
43784 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43787 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43789 splitTip : "Drag to resize.",
43790 collapsibleSplitTip : "Drag to resize. Double click to hide.",
43791 useSplitTips : false,
43793 applyConfig : function(config){
43794 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43797 onRender : function(ctr,pos) {
43799 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43800 if(!this.config.split){
43805 var splitEl = Roo.DomHelper.append(ctr.dom, {
43807 id: this.el.id + "-split",
43808 cls: "roo-layout-split roo-layout-split-"+this.position,
43811 /** The SplitBar for this region
43812 * @type Roo.SplitBar */
43813 // does not exist yet...
43814 Roo.log([this.position, this.orientation]);
43816 this.split = new Roo.bootstrap.SplitBar({
43817 dragElement : splitEl,
43818 resizingElement: this.el,
43819 orientation : this.orientation
43822 this.split.on("moved", this.onSplitMove, this);
43823 this.split.useShim = this.config.useShim === true;
43824 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43825 if(this.useSplitTips){
43826 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43828 //if(config.collapsible){
43829 // this.split.el.on("dblclick", this.collapse, this);
43832 if(typeof this.config.minSize != "undefined"){
43833 this.split.minSize = this.config.minSize;
43835 if(typeof this.config.maxSize != "undefined"){
43836 this.split.maxSize = this.config.maxSize;
43838 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43839 this.hideSplitter();
43844 getHMaxSize : function(){
43845 var cmax = this.config.maxSize || 10000;
43846 var center = this.mgr.getRegion("center");
43847 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43850 getVMaxSize : function(){
43851 var cmax = this.config.maxSize || 10000;
43852 var center = this.mgr.getRegion("center");
43853 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43856 onSplitMove : function(split, newSize){
43857 this.fireEvent("resized", this, newSize);
43861 * Returns the {@link Roo.SplitBar} for this region.
43862 * @return {Roo.SplitBar}
43864 getSplitBar : function(){
43869 this.hideSplitter();
43870 Roo.bootstrap.layout.Split.superclass.hide.call(this);
43873 hideSplitter : function(){
43875 this.split.el.setLocation(-2000,-2000);
43876 this.split.el.hide();
43882 this.split.el.show();
43884 Roo.bootstrap.layout.Split.superclass.show.call(this);
43887 beforeSlide: function(){
43888 if(Roo.isGecko){// firefox overflow auto bug workaround
43889 this.bodyEl.clip();
43891 this.tabs.bodyEl.clip();
43893 if(this.activePanel){
43894 this.activePanel.getEl().clip();
43896 if(this.activePanel.beforeSlide){
43897 this.activePanel.beforeSlide();
43903 afterSlide : function(){
43904 if(Roo.isGecko){// firefox overflow auto bug workaround
43905 this.bodyEl.unclip();
43907 this.tabs.bodyEl.unclip();
43909 if(this.activePanel){
43910 this.activePanel.getEl().unclip();
43911 if(this.activePanel.afterSlide){
43912 this.activePanel.afterSlide();
43918 initAutoHide : function(){
43919 if(this.autoHide !== false){
43920 if(!this.autoHideHd){
43921 var st = new Roo.util.DelayedTask(this.slideIn, this);
43922 this.autoHideHd = {
43923 "mouseout": function(e){
43924 if(!e.within(this.el, true)){
43928 "mouseover" : function(e){
43934 this.el.on(this.autoHideHd);
43938 clearAutoHide : function(){
43939 if(this.autoHide !== false){
43940 this.el.un("mouseout", this.autoHideHd.mouseout);
43941 this.el.un("mouseover", this.autoHideHd.mouseover);
43945 clearMonitor : function(){
43946 Roo.get(document).un("click", this.slideInIf, this);
43949 // these names are backwards but not changed for compat
43950 slideOut : function(){
43951 if(this.isSlid || this.el.hasActiveFx()){
43954 this.isSlid = true;
43955 if(this.collapseBtn){
43956 this.collapseBtn.hide();
43958 this.closeBtnState = this.closeBtn.getStyle('display');
43959 this.closeBtn.hide();
43961 this.stickBtn.show();
43964 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
43965 this.beforeSlide();
43966 this.el.setStyle("z-index", 10001);
43967 this.el.slideIn(this.getSlideAnchor(), {
43968 callback: function(){
43970 this.initAutoHide();
43971 Roo.get(document).on("click", this.slideInIf, this);
43972 this.fireEvent("slideshow", this);
43979 afterSlideIn : function(){
43980 this.clearAutoHide();
43981 this.isSlid = false;
43982 this.clearMonitor();
43983 this.el.setStyle("z-index", "");
43984 if(this.collapseBtn){
43985 this.collapseBtn.show();
43987 this.closeBtn.setStyle('display', this.closeBtnState);
43989 this.stickBtn.hide();
43991 this.fireEvent("slidehide", this);
43994 slideIn : function(cb){
43995 if(!this.isSlid || this.el.hasActiveFx()){
43999 this.isSlid = false;
44000 this.beforeSlide();
44001 this.el.slideOut(this.getSlideAnchor(), {
44002 callback: function(){
44003 this.el.setLeftTop(-10000, -10000);
44005 this.afterSlideIn();
44013 slideInIf : function(e){
44014 if(!e.within(this.el)){
44019 animateCollapse : function(){
44020 this.beforeSlide();
44021 this.el.setStyle("z-index", 20000);
44022 var anchor = this.getSlideAnchor();
44023 this.el.slideOut(anchor, {
44024 callback : function(){
44025 this.el.setStyle("z-index", "");
44026 this.collapsedEl.slideIn(anchor, {duration:.3});
44028 this.el.setLocation(-10000,-10000);
44030 this.fireEvent("collapsed", this);
44037 animateExpand : function(){
44038 this.beforeSlide();
44039 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44040 this.el.setStyle("z-index", 20000);
44041 this.collapsedEl.hide({
44044 this.el.slideIn(this.getSlideAnchor(), {
44045 callback : function(){
44046 this.el.setStyle("z-index", "");
44049 this.split.el.show();
44051 this.fireEvent("invalidated", this);
44052 this.fireEvent("expanded", this);
44080 getAnchor : function(){
44081 return this.anchors[this.position];
44084 getCollapseAnchor : function(){
44085 return this.canchors[this.position];
44088 getSlideAnchor : function(){
44089 return this.sanchors[this.position];
44092 getAlignAdj : function(){
44093 var cm = this.cmargins;
44094 switch(this.position){
44110 getExpandAdj : function(){
44111 var c = this.collapsedEl, cm = this.cmargins;
44112 switch(this.position){
44114 return [-(cm.right+c.getWidth()+cm.left), 0];
44117 return [cm.right+c.getWidth()+cm.left, 0];
44120 return [0, -(cm.top+cm.bottom+c.getHeight())];
44123 return [0, cm.top+cm.bottom+c.getHeight()];
44129 * Ext JS Library 1.1.1
44130 * Copyright(c) 2006-2007, Ext JS, LLC.
44132 * Originally Released Under LGPL - original licence link has changed is not relivant.
44135 * <script type="text/javascript">
44138 * These classes are private internal classes
44140 Roo.bootstrap.layout.Center = function(config){
44141 config.region = "center";
44142 Roo.bootstrap.layout.Region.call(this, config);
44143 this.visible = true;
44144 this.minWidth = config.minWidth || 20;
44145 this.minHeight = config.minHeight || 20;
44148 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44150 // center panel can't be hidden
44154 // center panel can't be hidden
44157 getMinWidth: function(){
44158 return this.minWidth;
44161 getMinHeight: function(){
44162 return this.minHeight;
44176 Roo.bootstrap.layout.North = function(config)
44178 config.region = 'north';
44179 config.cursor = 'n-resize';
44181 Roo.bootstrap.layout.Split.call(this, config);
44185 this.split.placement = Roo.bootstrap.SplitBar.TOP;
44186 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44187 this.split.el.addClass("roo-layout-split-v");
44189 //var size = config.initialSize || config.height;
44190 //if(this.el && typeof size != "undefined"){
44191 // this.el.setHeight(size);
44194 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44196 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44199 onRender : function(ctr, pos)
44201 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44202 var size = this.config.initialSize || this.config.height;
44203 if(this.el && typeof size != "undefined"){
44204 this.el.setHeight(size);
44209 getBox : function(){
44210 if(this.collapsed){
44211 return this.collapsedEl.getBox();
44213 var box = this.el.getBox();
44215 box.height += this.split.el.getHeight();
44220 updateBox : function(box){
44221 if(this.split && !this.collapsed){
44222 box.height -= this.split.el.getHeight();
44223 this.split.el.setLeft(box.x);
44224 this.split.el.setTop(box.y+box.height);
44225 this.split.el.setWidth(box.width);
44227 if(this.collapsed){
44228 this.updateBody(box.width, null);
44230 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44238 Roo.bootstrap.layout.South = function(config){
44239 config.region = 'south';
44240 config.cursor = 's-resize';
44241 Roo.bootstrap.layout.Split.call(this, config);
44243 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44244 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44245 this.split.el.addClass("roo-layout-split-v");
44250 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44251 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44253 onRender : function(ctr, pos)
44255 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44256 var size = this.config.initialSize || this.config.height;
44257 if(this.el && typeof size != "undefined"){
44258 this.el.setHeight(size);
44263 getBox : function(){
44264 if(this.collapsed){
44265 return this.collapsedEl.getBox();
44267 var box = this.el.getBox();
44269 var sh = this.split.el.getHeight();
44276 updateBox : function(box){
44277 if(this.split && !this.collapsed){
44278 var sh = this.split.el.getHeight();
44281 this.split.el.setLeft(box.x);
44282 this.split.el.setTop(box.y-sh);
44283 this.split.el.setWidth(box.width);
44285 if(this.collapsed){
44286 this.updateBody(box.width, null);
44288 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44292 Roo.bootstrap.layout.East = function(config){
44293 config.region = "east";
44294 config.cursor = "e-resize";
44295 Roo.bootstrap.layout.Split.call(this, config);
44297 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44298 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44299 this.split.el.addClass("roo-layout-split-h");
44303 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44304 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44306 onRender : function(ctr, pos)
44308 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44309 var size = this.config.initialSize || this.config.width;
44310 if(this.el && typeof size != "undefined"){
44311 this.el.setWidth(size);
44316 getBox : function(){
44317 if(this.collapsed){
44318 return this.collapsedEl.getBox();
44320 var box = this.el.getBox();
44322 var sw = this.split.el.getWidth();
44329 updateBox : function(box){
44330 if(this.split && !this.collapsed){
44331 var sw = this.split.el.getWidth();
44333 this.split.el.setLeft(box.x);
44334 this.split.el.setTop(box.y);
44335 this.split.el.setHeight(box.height);
44338 if(this.collapsed){
44339 this.updateBody(null, box.height);
44341 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44345 Roo.bootstrap.layout.West = function(config){
44346 config.region = "west";
44347 config.cursor = "w-resize";
44349 Roo.bootstrap.layout.Split.call(this, config);
44351 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44352 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44353 this.split.el.addClass("roo-layout-split-h");
44357 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44358 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44360 onRender: function(ctr, pos)
44362 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44363 var size = this.config.initialSize || this.config.width;
44364 if(typeof size != "undefined"){
44365 this.el.setWidth(size);
44369 getBox : function(){
44370 if(this.collapsed){
44371 return this.collapsedEl.getBox();
44373 var box = this.el.getBox();
44374 if (box.width == 0) {
44375 box.width = this.config.width; // kludge?
44378 box.width += this.split.el.getWidth();
44383 updateBox : function(box){
44384 if(this.split && !this.collapsed){
44385 var sw = this.split.el.getWidth();
44387 this.split.el.setLeft(box.x+box.width);
44388 this.split.el.setTop(box.y);
44389 this.split.el.setHeight(box.height);
44391 if(this.collapsed){
44392 this.updateBody(null, box.height);
44394 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44398 * Ext JS Library 1.1.1
44399 * Copyright(c) 2006-2007, Ext JS, LLC.
44401 * Originally Released Under LGPL - original licence link has changed is not relivant.
44404 * <script type="text/javascript">
44407 * @class Roo.bootstrap.paenl.Content
44408 * @extends Roo.util.Observable
44409 * @children Roo.bootstrap.Component
44410 * @parent builder Roo.bootstrap.layout.Border
44411 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44412 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
44413 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
44414 * @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
44415 * @cfg {Boolean} closable True if the panel can be closed/removed
44416 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44417 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44418 * @cfg {Toolbar} toolbar A toolbar for this panel
44419 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44420 * @cfg {String} title The title for this panel
44421 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44422 * @cfg {String} url Calls {@link #setUrl} with this value
44423 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44424 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44425 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44426 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
44427 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
44428 * @cfg {Boolean} badges render the badges
44429 * @cfg {String} cls extra classes to use
44430 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44433 * Create a new ContentPanel.
44434 * @param {String/Object} config A string to set only the title or a config object
44437 Roo.bootstrap.panel.Content = function( config){
44439 this.tpl = config.tpl || false;
44441 var el = config.el;
44442 var content = config.content;
44444 if(config.autoCreate){ // xtype is available if this is called from factory
44447 this.el = Roo.get(el);
44448 if(!this.el && config && config.autoCreate){
44449 if(typeof config.autoCreate == "object"){
44450 if(!config.autoCreate.id){
44451 config.autoCreate.id = config.id||el;
44453 this.el = Roo.DomHelper.append(document.body,
44454 config.autoCreate, true);
44458 cls: (config.cls || '') +
44459 (config.background ? ' bg-' + config.background : '') +
44460 " roo-layout-inactive-content",
44463 if (config.iframe) {
44467 style : 'border: 0px',
44468 src : 'about:blank'
44474 elcfg.html = config.html;
44478 this.el = Roo.DomHelper.append(document.body, elcfg , true);
44479 if (config.iframe) {
44480 this.iframeEl = this.el.select('iframe',true).first();
44485 this.closable = false;
44486 this.loaded = false;
44487 this.active = false;
44490 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44492 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44494 this.wrapEl = this.el; //this.el.wrap();
44496 if (config.toolbar.items) {
44497 ti = config.toolbar.items ;
44498 delete config.toolbar.items ;
44502 this.toolbar.render(this.wrapEl, 'before');
44503 for(var i =0;i < ti.length;i++) {
44504 // Roo.log(['add child', items[i]]);
44505 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44507 this.toolbar.items = nitems;
44508 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44509 delete config.toolbar;
44513 // xtype created footer. - not sure if will work as we normally have to render first..
44514 if (this.footer && !this.footer.el && this.footer.xtype) {
44515 if (!this.wrapEl) {
44516 this.wrapEl = this.el.wrap();
44519 this.footer.container = this.wrapEl.createChild();
44521 this.footer = Roo.factory(this.footer, Roo);
44526 if(typeof config == "string"){
44527 this.title = config;
44529 Roo.apply(this, config);
44533 this.resizeEl = Roo.get(this.resizeEl, true);
44535 this.resizeEl = this.el;
44537 // handle view.xtype
44545 * Fires when this panel is activated.
44546 * @param {Roo.ContentPanel} this
44550 * @event deactivate
44551 * Fires when this panel is activated.
44552 * @param {Roo.ContentPanel} this
44554 "deactivate" : true,
44558 * Fires when this panel is resized if fitToFrame is true.
44559 * @param {Roo.ContentPanel} this
44560 * @param {Number} width The width after any component adjustments
44561 * @param {Number} height The height after any component adjustments
44567 * Fires when this tab is created
44568 * @param {Roo.ContentPanel} this
44574 * Fires when this content is scrolled
44575 * @param {Roo.ContentPanel} this
44576 * @param {Event} scrollEvent
44587 if(this.autoScroll && !this.iframe){
44588 this.resizeEl.setStyle("overflow", "auto");
44589 this.resizeEl.on('scroll', this.onScroll, this);
44591 // fix randome scrolling
44592 //this.el.on('scroll', function() {
44593 // Roo.log('fix random scolling');
44594 // this.scrollTo('top',0);
44597 content = content || this.content;
44599 this.setContent(content);
44601 if(config && config.url){
44602 this.setUrl(this.url, this.params, this.loadOnce);
44607 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44609 if (this.view && typeof(this.view.xtype) != 'undefined') {
44610 this.view.el = this.el.appendChild(document.createElement("div"));
44611 this.view = Roo.factory(this.view);
44612 this.view.render && this.view.render(false, '');
44616 this.fireEvent('render', this);
44619 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44629 /* Resize Element - use this to work out scroll etc. */
44632 setRegion : function(region){
44633 this.region = region;
44634 this.setActiveClass(region && !this.background);
44638 setActiveClass: function(state)
44641 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44642 this.el.setStyle('position','relative');
44644 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44645 this.el.setStyle('position', 'absolute');
44650 * Returns the toolbar for this Panel if one was configured.
44651 * @return {Roo.Toolbar}
44653 getToolbar : function(){
44654 return this.toolbar;
44657 setActiveState : function(active)
44659 this.active = active;
44660 this.setActiveClass(active);
44662 if(this.fireEvent("deactivate", this) === false){
44667 this.fireEvent("activate", this);
44671 * Updates this panel's element (not for iframe)
44672 * @param {String} content The new content
44673 * @param {Boolean} loadScripts (optional) true to look for and process scripts
44675 setContent : function(content, loadScripts){
44680 this.el.update(content, loadScripts);
44683 ignoreResize : function(w, h)
44685 //return false; // always resize?
44686 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44689 this.lastSize = {width: w, height: h};
44694 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44695 * @return {Roo.UpdateManager} The UpdateManager
44697 getUpdateManager : function(){
44701 return this.el.getUpdateManager();
44704 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44705 * Does not work with IFRAME contents
44706 * @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:
44709 url: "your-url.php",
44710 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44711 callback: yourFunction,
44712 scope: yourObject, //(optional scope)
44715 text: "Loading...",
44721 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44722 * 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.
44723 * @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}
44724 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44725 * @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.
44726 * @return {Roo.ContentPanel} this
44734 var um = this.el.getUpdateManager();
44735 um.update.apply(um, arguments);
44741 * 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.
44742 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44743 * @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)
44744 * @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)
44745 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44747 setUrl : function(url, params, loadOnce){
44749 this.iframeEl.dom.src = url;
44753 if(this.refreshDelegate){
44754 this.removeListener("activate", this.refreshDelegate);
44756 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44757 this.on("activate", this.refreshDelegate);
44758 return this.el.getUpdateManager();
44761 _handleRefresh : function(url, params, loadOnce){
44762 if(!loadOnce || !this.loaded){
44763 var updater = this.el.getUpdateManager();
44764 updater.update(url, params, this._setLoaded.createDelegate(this));
44768 _setLoaded : function(){
44769 this.loaded = true;
44773 * Returns this panel's id
44776 getId : function(){
44781 * Returns this panel's element - used by regiosn to add.
44782 * @return {Roo.Element}
44784 getEl : function(){
44785 return this.wrapEl || this.el;
44790 adjustForComponents : function(width, height)
44792 //Roo.log('adjustForComponents ');
44793 if(this.resizeEl != this.el){
44794 width -= this.el.getFrameWidth('lr');
44795 height -= this.el.getFrameWidth('tb');
44798 var te = this.toolbar.getEl();
44799 te.setWidth(width);
44800 height -= te.getHeight();
44803 var te = this.footer.getEl();
44804 te.setWidth(width);
44805 height -= te.getHeight();
44809 if(this.adjustments){
44810 width += this.adjustments[0];
44811 height += this.adjustments[1];
44813 return {"width": width, "height": height};
44816 setSize : function(width, height){
44817 if(this.fitToFrame && !this.ignoreResize(width, height)){
44818 if(this.fitContainer && this.resizeEl != this.el){
44819 this.el.setSize(width, height);
44821 var size = this.adjustForComponents(width, height);
44823 this.iframeEl.setSize(width,height);
44826 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44827 this.fireEvent('resize', this, size.width, size.height);
44834 * Returns this panel's title
44837 getTitle : function(){
44839 if (typeof(this.title) != 'object') {
44844 for (var k in this.title) {
44845 if (!this.title.hasOwnProperty(k)) {
44849 if (k.indexOf('-') >= 0) {
44850 var s = k.split('-');
44851 for (var i = 0; i<s.length; i++) {
44852 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44855 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44862 * Set this panel's title
44863 * @param {String} title
44865 setTitle : function(title){
44866 this.title = title;
44868 this.region.updatePanelTitle(this, title);
44873 * Returns true is this panel was configured to be closable
44874 * @return {Boolean}
44876 isClosable : function(){
44877 return this.closable;
44880 beforeSlide : function(){
44882 this.resizeEl.clip();
44885 afterSlide : function(){
44887 this.resizeEl.unclip();
44891 * Force a content refresh from the URL specified in the {@link #setUrl} method.
44892 * Will fail silently if the {@link #setUrl} method has not been called.
44893 * This does not activate the panel, just updates its content.
44895 refresh : function(){
44896 if(this.refreshDelegate){
44897 this.loaded = false;
44898 this.refreshDelegate();
44903 * Destroys this panel
44905 destroy : function(){
44906 this.el.removeAllListeners();
44907 var tempEl = document.createElement("span");
44908 tempEl.appendChild(this.el.dom);
44909 tempEl.innerHTML = "";
44915 * form - if the content panel contains a form - this is a reference to it.
44916 * @type {Roo.form.Form}
44920 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44921 * This contains a reference to it.
44927 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44937 * @param {Object} cfg Xtype definition of item to add.
44941 getChildContainer: function () {
44942 return this.getEl();
44946 onScroll : function(e)
44948 this.fireEvent('scroll', this, e);
44953 var ret = new Roo.factory(cfg);
44958 if (cfg.xtype.match(/^Form$/)) {
44961 //if (this.footer) {
44962 // el = this.footer.container.insertSibling(false, 'before');
44964 el = this.el.createChild();
44967 this.form = new Roo.form.Form(cfg);
44970 if ( this.form.allItems.length) {
44971 this.form.render(el.dom);
44975 // should only have one of theses..
44976 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
44977 // views.. should not be just added - used named prop 'view''
44979 cfg.el = this.el.appendChild(document.createElement("div"));
44982 var ret = new Roo.factory(cfg);
44984 ret.render && ret.render(false, ''); // render blank..
44994 * @class Roo.bootstrap.panel.Grid
44995 * @extends Roo.bootstrap.panel.Content
44997 * Create a new GridPanel.
44998 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
44999 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45000 * @param {Object} config A the config object
45006 Roo.bootstrap.panel.Grid = function(config)
45010 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45011 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45013 config.el = this.wrapper;
45014 //this.el = this.wrapper;
45016 if (config.container) {
45017 // ctor'ed from a Border/panel.grid
45020 this.wrapper.setStyle("overflow", "hidden");
45021 this.wrapper.addClass('roo-grid-container');
45026 if(config.toolbar){
45027 var tool_el = this.wrapper.createChild();
45028 this.toolbar = Roo.factory(config.toolbar);
45030 if (config.toolbar.items) {
45031 ti = config.toolbar.items ;
45032 delete config.toolbar.items ;
45036 this.toolbar.render(tool_el);
45037 for(var i =0;i < ti.length;i++) {
45038 // Roo.log(['add child', items[i]]);
45039 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45041 this.toolbar.items = nitems;
45043 delete config.toolbar;
45046 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45047 config.grid.scrollBody = true;;
45048 config.grid.monitorWindowResize = false; // turn off autosizing
45049 config.grid.autoHeight = false;
45050 config.grid.autoWidth = false;
45052 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45054 if (config.background) {
45055 // render grid on panel activation (if panel background)
45056 this.on('activate', function(gp) {
45057 if (!gp.grid.rendered) {
45058 gp.grid.render(this.wrapper);
45059 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45064 this.grid.render(this.wrapper);
45065 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45068 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45069 // ??? needed ??? config.el = this.wrapper;
45074 // xtype created footer. - not sure if will work as we normally have to render first..
45075 if (this.footer && !this.footer.el && this.footer.xtype) {
45077 var ctr = this.grid.getView().getFooterPanel(true);
45078 this.footer.dataSource = this.grid.dataSource;
45079 this.footer = Roo.factory(this.footer, Roo);
45080 this.footer.render(ctr);
45090 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45093 getId : function(){
45094 return this.grid.id;
45098 * Returns the grid for this panel
45099 * @return {Roo.bootstrap.Table}
45101 getGrid : function(){
45105 setSize : function(width, height)
45108 //if(!this.ignoreResize(width, height)){
45109 var grid = this.grid;
45110 var size = this.adjustForComponents(width, height);
45111 // tfoot is not a footer?
45114 var gridel = grid.getGridEl();
45115 gridel.setSize(size.width, size.height);
45117 var tbd = grid.getGridEl().select('tbody', true).first();
45118 var thd = grid.getGridEl().select('thead',true).first();
45119 var tbf= grid.getGridEl().select('tfoot', true).first();
45122 size.height -= tbf.getHeight();
45125 size.height -= thd.getHeight();
45128 tbd.setSize(size.width, size.height );
45129 // this is for the account management tab -seems to work there.
45130 var thd = grid.getGridEl().select('thead',true).first();
45132 // tbd.setSize(size.width, size.height - thd.getHeight());
45142 beforeSlide : function(){
45143 this.grid.getView().scroller.clip();
45146 afterSlide : function(){
45147 this.grid.getView().scroller.unclip();
45150 destroy : function(){
45151 this.grid.destroy();
45153 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45158 * @class Roo.bootstrap.panel.Nest
45159 * @extends Roo.bootstrap.panel.Content
45161 * Create a new Panel, that can contain a layout.Border.
45164 * @param {String/Object} config A string to set only the title or a config object
45166 Roo.bootstrap.panel.Nest = function(config)
45168 // construct with only one argument..
45169 /* FIXME - implement nicer consturctors
45170 if (layout.layout) {
45172 layout = config.layout;
45173 delete config.layout;
45175 if (layout.xtype && !layout.getEl) {
45176 // then layout needs constructing..
45177 layout = Roo.factory(layout, Roo);
45181 config.el = config.layout.getEl();
45183 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45185 config.layout.monitorWindowResize = false; // turn off autosizing
45186 this.layout = config.layout;
45187 this.layout.getEl().addClass("roo-layout-nested-layout");
45188 this.layout.parent = this;
45195 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45197 * @cfg {Roo.BorderLayout} layout The layout for this panel
45201 setSize : function(width, height){
45202 if(!this.ignoreResize(width, height)){
45203 var size = this.adjustForComponents(width, height);
45204 var el = this.layout.getEl();
45205 if (size.height < 1) {
45206 el.setWidth(size.width);
45208 el.setSize(size.width, size.height);
45210 var touch = el.dom.offsetWidth;
45211 this.layout.layout();
45212 // ie requires a double layout on the first pass
45213 if(Roo.isIE && !this.initialized){
45214 this.initialized = true;
45215 this.layout.layout();
45220 // activate all subpanels if not currently active..
45222 setActiveState : function(active){
45223 this.active = active;
45224 this.setActiveClass(active);
45227 this.fireEvent("deactivate", this);
45231 this.fireEvent("activate", this);
45232 // not sure if this should happen before or after..
45233 if (!this.layout) {
45234 return; // should not happen..
45237 for (var r in this.layout.regions) {
45238 reg = this.layout.getRegion(r);
45239 if (reg.getActivePanel()) {
45240 //reg.showPanel(reg.getActivePanel()); // force it to activate..
45241 reg.setActivePanel(reg.getActivePanel());
45244 if (!reg.panels.length) {
45247 reg.showPanel(reg.getPanel(0));
45256 * Returns the nested BorderLayout for this panel
45257 * @return {Roo.BorderLayout}
45259 getLayout : function(){
45260 return this.layout;
45264 * Adds a xtype elements to the layout of the nested panel
45268 xtype : 'ContentPanel',
45275 xtype : 'NestedLayoutPanel',
45281 items : [ ... list of content panels or nested layout panels.. ]
45285 * @param {Object} cfg Xtype definition of item to add.
45287 addxtype : function(cfg) {
45288 return this.layout.addxtype(cfg);
45293 * Ext JS Library 1.1.1
45294 * Copyright(c) 2006-2007, Ext JS, LLC.
45296 * Originally Released Under LGPL - original licence link has changed is not relivant.
45299 * <script type="text/javascript">
45302 * @class Roo.TabPanel
45303 * @extends Roo.util.Observable
45304 * A lightweight tab container.
45308 // basic tabs 1, built from existing content
45309 var tabs = new Roo.TabPanel("tabs1");
45310 tabs.addTab("script", "View Script");
45311 tabs.addTab("markup", "View Markup");
45312 tabs.activate("script");
45314 // more advanced tabs, built from javascript
45315 var jtabs = new Roo.TabPanel("jtabs");
45316 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45318 // set up the UpdateManager
45319 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45320 var updater = tab2.getUpdateManager();
45321 updater.setDefaultUrl("ajax1.htm");
45322 tab2.on('activate', updater.refresh, updater, true);
45324 // Use setUrl for Ajax loading
45325 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45326 tab3.setUrl("ajax2.htm", null, true);
45329 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45332 jtabs.activate("jtabs-1");
45335 * Create a new TabPanel.
45336 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45337 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45339 Roo.bootstrap.panel.Tabs = function(config){
45341 * The container element for this TabPanel.
45342 * @type Roo.Element
45344 this.el = Roo.get(config.el);
45347 if(typeof config == "boolean"){
45348 this.tabPosition = config ? "bottom" : "top";
45350 Roo.apply(this, config);
45354 if(this.tabPosition == "bottom"){
45355 // if tabs are at the bottom = create the body first.
45356 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45357 this.el.addClass("roo-tabs-bottom");
45359 // next create the tabs holders
45361 if (this.tabPosition == "west"){
45363 var reg = this.region; // fake it..
45365 if (!reg.mgr.parent) {
45368 reg = reg.mgr.parent.region;
45370 Roo.log("got nest?");
45372 if (reg.mgr.getRegion('west')) {
45373 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45374 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45375 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45376 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45377 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45385 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45386 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45387 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45388 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45393 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45396 // finally - if tabs are at the top, then create the body last..
45397 if(this.tabPosition != "bottom"){
45398 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45399 * @type Roo.Element
45401 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45402 this.el.addClass("roo-tabs-top");
45406 this.bodyEl.setStyle("position", "relative");
45408 this.active = null;
45409 this.activateDelegate = this.activate.createDelegate(this);
45414 * Fires when the active tab changes
45415 * @param {Roo.TabPanel} this
45416 * @param {Roo.TabPanelItem} activePanel The new active tab
45420 * @event beforetabchange
45421 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45422 * @param {Roo.TabPanel} this
45423 * @param {Object} e Set cancel to true on this object to cancel the tab change
45424 * @param {Roo.TabPanelItem} tab The tab being changed to
45426 "beforetabchange" : true
45429 Roo.EventManager.onWindowResize(this.onResize, this);
45430 this.cpad = this.el.getPadding("lr");
45431 this.hiddenCount = 0;
45434 // toolbar on the tabbar support...
45435 if (this.toolbar) {
45436 alert("no toolbar support yet");
45437 this.toolbar = false;
45439 var tcfg = this.toolbar;
45440 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
45441 this.toolbar = new Roo.Toolbar(tcfg);
45442 if (Roo.isSafari) {
45443 var tbl = tcfg.container.child('table', true);
45444 tbl.setAttribute('width', '100%');
45452 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45455 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45457 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45459 tabPosition : "top",
45461 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45463 currentTabWidth : 0,
45465 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45469 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45473 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45475 preferredTabWidth : 175,
45477 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45479 resizeTabs : false,
45481 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45483 monitorResize : true,
45485 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
45487 toolbar : false, // set by caller..
45489 region : false, /// set by caller
45491 disableTooltips : true, // not used yet...
45494 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45495 * @param {String} id The id of the div to use <b>or create</b>
45496 * @param {String} text The text for the tab
45497 * @param {String} content (optional) Content to put in the TabPanelItem body
45498 * @param {Boolean} closable (optional) True to create a close icon on the tab
45499 * @return {Roo.TabPanelItem} The created TabPanelItem
45501 addTab : function(id, text, content, closable, tpl)
45503 var item = new Roo.bootstrap.panel.TabItem({
45507 closable : closable,
45510 this.addTabItem(item);
45512 item.setContent(content);
45518 * Returns the {@link Roo.TabPanelItem} with the specified id/index
45519 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45520 * @return {Roo.TabPanelItem}
45522 getTab : function(id){
45523 return this.items[id];
45527 * Hides the {@link Roo.TabPanelItem} with the specified id/index
45528 * @param {String/Number} id The id or index of the TabPanelItem to hide.
45530 hideTab : function(id){
45531 var t = this.items[id];
45534 this.hiddenCount++;
45535 this.autoSizeTabs();
45540 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45541 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45543 unhideTab : function(id){
45544 var t = this.items[id];
45546 t.setHidden(false);
45547 this.hiddenCount--;
45548 this.autoSizeTabs();
45553 * Adds an existing {@link Roo.TabPanelItem}.
45554 * @param {Roo.TabPanelItem} item The TabPanelItem to add
45556 addTabItem : function(item)
45558 this.items[item.id] = item;
45559 this.items.push(item);
45560 this.autoSizeTabs();
45561 // if(this.resizeTabs){
45562 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45563 // this.autoSizeTabs();
45565 // item.autoSize();
45570 * Removes a {@link Roo.TabPanelItem}.
45571 * @param {String/Number} id The id or index of the TabPanelItem to remove.
45573 removeTab : function(id){
45574 var items = this.items;
45575 var tab = items[id];
45576 if(!tab) { return; }
45577 var index = items.indexOf(tab);
45578 if(this.active == tab && items.length > 1){
45579 var newTab = this.getNextAvailable(index);
45584 this.stripEl.dom.removeChild(tab.pnode.dom);
45585 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45586 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45588 items.splice(index, 1);
45589 delete this.items[tab.id];
45590 tab.fireEvent("close", tab);
45591 tab.purgeListeners();
45592 this.autoSizeTabs();
45595 getNextAvailable : function(start){
45596 var items = this.items;
45598 // look for a next tab that will slide over to
45599 // replace the one being removed
45600 while(index < items.length){
45601 var item = items[++index];
45602 if(item && !item.isHidden()){
45606 // if one isn't found select the previous tab (on the left)
45609 var item = items[--index];
45610 if(item && !item.isHidden()){
45618 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45619 * @param {String/Number} id The id or index of the TabPanelItem to disable.
45621 disableTab : function(id){
45622 var tab = this.items[id];
45623 if(tab && this.active != tab){
45629 * Enables a {@link Roo.TabPanelItem} that is disabled.
45630 * @param {String/Number} id The id or index of the TabPanelItem to enable.
45632 enableTab : function(id){
45633 var tab = this.items[id];
45638 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45639 * @param {String/Number} id The id or index of the TabPanelItem to activate.
45640 * @return {Roo.TabPanelItem} The TabPanelItem.
45642 activate : function(id)
45644 //Roo.log('activite:' + id);
45646 var tab = this.items[id];
45650 if(tab == this.active || tab.disabled){
45654 this.fireEvent("beforetabchange", this, e, tab);
45655 if(e.cancel !== true && !tab.disabled){
45657 this.active.hide();
45659 this.active = this.items[id];
45660 this.active.show();
45661 this.fireEvent("tabchange", this, this.active);
45667 * Gets the active {@link Roo.TabPanelItem}.
45668 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45670 getActiveTab : function(){
45671 return this.active;
45675 * Updates the tab body element to fit the height of the container element
45676 * for overflow scrolling
45677 * @param {Number} targetHeight (optional) Override the starting height from the elements height
45679 syncHeight : function(targetHeight){
45680 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45681 var bm = this.bodyEl.getMargins();
45682 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45683 this.bodyEl.setHeight(newHeight);
45687 onResize : function(){
45688 if(this.monitorResize){
45689 this.autoSizeTabs();
45694 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45696 beginUpdate : function(){
45697 this.updating = true;
45701 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45703 endUpdate : function(){
45704 this.updating = false;
45705 this.autoSizeTabs();
45709 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45711 autoSizeTabs : function()
45713 var count = this.items.length;
45714 var vcount = count - this.hiddenCount;
45717 this.stripEl.hide();
45719 this.stripEl.show();
45722 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45727 var w = Math.max(this.el.getWidth() - this.cpad, 10);
45728 var availWidth = Math.floor(w / vcount);
45729 var b = this.stripBody;
45730 if(b.getWidth() > w){
45731 var tabs = this.items;
45732 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45733 if(availWidth < this.minTabWidth){
45734 /*if(!this.sleft){ // incomplete scrolling code
45735 this.createScrollButtons();
45738 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45741 if(this.currentTabWidth < this.preferredTabWidth){
45742 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45748 * Returns the number of tabs in this TabPanel.
45751 getCount : function(){
45752 return this.items.length;
45756 * Resizes all the tabs to the passed width
45757 * @param {Number} The new width
45759 setTabWidth : function(width){
45760 this.currentTabWidth = width;
45761 for(var i = 0, len = this.items.length; i < len; i++) {
45762 if(!this.items[i].isHidden()) {
45763 this.items[i].setWidth(width);
45769 * Destroys this TabPanel
45770 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45772 destroy : function(removeEl){
45773 Roo.EventManager.removeResizeListener(this.onResize, this);
45774 for(var i = 0, len = this.items.length; i < len; i++){
45775 this.items[i].purgeListeners();
45777 if(removeEl === true){
45778 this.el.update("");
45783 createStrip : function(container)
45785 var strip = document.createElement("nav");
45786 strip.className = Roo.bootstrap.version == 4 ?
45787 "navbar-light bg-light" :
45788 "navbar navbar-default"; //"x-tabs-wrap";
45789 container.appendChild(strip);
45793 createStripList : function(strip)
45795 // div wrapper for retard IE
45796 // returns the "tr" element.
45797 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45798 //'<div class="x-tabs-strip-wrap">'+
45799 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45800 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45801 return strip.firstChild; //.firstChild.firstChild.firstChild;
45803 createBody : function(container)
45805 var body = document.createElement("div");
45806 Roo.id(body, "tab-body");
45807 //Roo.fly(body).addClass("x-tabs-body");
45808 Roo.fly(body).addClass("tab-content");
45809 container.appendChild(body);
45812 createItemBody :function(bodyEl, id){
45813 var body = Roo.getDom(id);
45815 body = document.createElement("div");
45818 //Roo.fly(body).addClass("x-tabs-item-body");
45819 Roo.fly(body).addClass("tab-pane");
45820 bodyEl.insertBefore(body, bodyEl.firstChild);
45824 createStripElements : function(stripEl, text, closable, tpl)
45826 var td = document.createElement("li"); // was td..
45827 td.className = 'nav-item';
45829 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45832 stripEl.appendChild(td);
45834 td.className = "x-tabs-closable";
45835 if(!this.closeTpl){
45836 this.closeTpl = new Roo.Template(
45837 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45838 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45839 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
45842 var el = this.closeTpl.overwrite(td, {"text": text});
45843 var close = el.getElementsByTagName("div")[0];
45844 var inner = el.getElementsByTagName("em")[0];
45845 return {"el": el, "close": close, "inner": inner};
45848 // not sure what this is..
45849 // if(!this.tabTpl){
45850 //this.tabTpl = new Roo.Template(
45851 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45852 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45854 // this.tabTpl = new Roo.Template(
45855 // '<a href="#">' +
45856 // '<span unselectable="on"' +
45857 // (this.disableTooltips ? '' : ' title="{text}"') +
45858 // ' >{text}</span></a>'
45864 var template = tpl || this.tabTpl || false;
45867 template = new Roo.Template(
45868 Roo.bootstrap.version == 4 ?
45870 '<a class="nav-link" href="#" unselectable="on"' +
45871 (this.disableTooltips ? '' : ' title="{text}"') +
45874 '<a class="nav-link" href="#">' +
45875 '<span unselectable="on"' +
45876 (this.disableTooltips ? '' : ' title="{text}"') +
45877 ' >{text}</span></a>'
45882 switch (typeof(template)) {
45886 template = new Roo.Template(template);
45892 var el = template.overwrite(td, {"text": text});
45894 var inner = el.getElementsByTagName("span")[0];
45896 return {"el": el, "inner": inner};
45904 * @class Roo.TabPanelItem
45905 * @extends Roo.util.Observable
45906 * Represents an individual item (tab plus body) in a TabPanel.
45907 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45908 * @param {String} id The id of this TabPanelItem
45909 * @param {String} text The text for the tab of this TabPanelItem
45910 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45912 Roo.bootstrap.panel.TabItem = function(config){
45914 * The {@link Roo.TabPanel} this TabPanelItem belongs to
45915 * @type Roo.TabPanel
45917 this.tabPanel = config.panel;
45919 * The id for this TabPanelItem
45922 this.id = config.id;
45924 this.disabled = false;
45926 this.text = config.text;
45928 this.loaded = false;
45929 this.closable = config.closable;
45932 * The body element for this TabPanelItem.
45933 * @type Roo.Element
45935 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45936 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45937 this.bodyEl.setStyle("display", "block");
45938 this.bodyEl.setStyle("zoom", "1");
45939 //this.hideAction();
45941 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45943 this.el = Roo.get(els.el);
45944 this.inner = Roo.get(els.inner, true);
45945 this.textEl = Roo.bootstrap.version == 4 ?
45946 this.el : Roo.get(this.el.dom.firstChild, true);
45948 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45949 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45952 // this.el.on("mousedown", this.onTabMouseDown, this);
45953 this.el.on("click", this.onTabClick, this);
45955 if(config.closable){
45956 var c = Roo.get(els.close, true);
45957 c.dom.title = this.closeText;
45958 c.addClassOnOver("close-over");
45959 c.on("click", this.closeClick, this);
45965 * Fires when this tab becomes the active tab.
45966 * @param {Roo.TabPanel} tabPanel The parent TabPanel
45967 * @param {Roo.TabPanelItem} this
45971 * @event beforeclose
45972 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
45973 * @param {Roo.TabPanelItem} this
45974 * @param {Object} e Set cancel to true on this object to cancel the close.
45976 "beforeclose": true,
45979 * Fires when this tab is closed.
45980 * @param {Roo.TabPanelItem} this
45984 * @event deactivate
45985 * Fires when this tab is no longer the active tab.
45986 * @param {Roo.TabPanel} tabPanel The parent TabPanel
45987 * @param {Roo.TabPanelItem} this
45989 "deactivate" : true
45991 this.hidden = false;
45993 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
45996 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
45998 purgeListeners : function(){
45999 Roo.util.Observable.prototype.purgeListeners.call(this);
46000 this.el.removeAllListeners();
46003 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46006 this.status_node.addClass("active");
46009 this.tabPanel.stripWrap.repaint();
46011 this.fireEvent("activate", this.tabPanel, this);
46015 * Returns true if this tab is the active tab.
46016 * @return {Boolean}
46018 isActive : function(){
46019 return this.tabPanel.getActiveTab() == this;
46023 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46026 this.status_node.removeClass("active");
46028 this.fireEvent("deactivate", this.tabPanel, this);
46031 hideAction : function(){
46032 this.bodyEl.hide();
46033 this.bodyEl.setStyle("position", "absolute");
46034 this.bodyEl.setLeft("-20000px");
46035 this.bodyEl.setTop("-20000px");
46038 showAction : function(){
46039 this.bodyEl.setStyle("position", "relative");
46040 this.bodyEl.setTop("");
46041 this.bodyEl.setLeft("");
46042 this.bodyEl.show();
46046 * Set the tooltip for the tab.
46047 * @param {String} tooltip The tab's tooltip
46049 setTooltip : function(text){
46050 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46051 this.textEl.dom.qtip = text;
46052 this.textEl.dom.removeAttribute('title');
46054 this.textEl.dom.title = text;
46058 onTabClick : function(e){
46059 e.preventDefault();
46060 this.tabPanel.activate(this.id);
46063 onTabMouseDown : function(e){
46064 e.preventDefault();
46065 this.tabPanel.activate(this.id);
46068 getWidth : function(){
46069 return this.inner.getWidth();
46072 setWidth : function(width){
46073 var iwidth = width - this.linode.getPadding("lr");
46074 this.inner.setWidth(iwidth);
46075 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46076 this.linode.setWidth(width);
46080 * Show or hide the tab
46081 * @param {Boolean} hidden True to hide or false to show.
46083 setHidden : function(hidden){
46084 this.hidden = hidden;
46085 this.linode.setStyle("display", hidden ? "none" : "");
46089 * Returns true if this tab is "hidden"
46090 * @return {Boolean}
46092 isHidden : function(){
46093 return this.hidden;
46097 * Returns the text for this tab
46100 getText : function(){
46104 autoSize : function(){
46105 //this.el.beginMeasure();
46106 this.textEl.setWidth(1);
46108 * #2804 [new] Tabs in Roojs
46109 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46111 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46112 //this.el.endMeasure();
46116 * Sets the text for the tab (Note: this also sets the tooltip text)
46117 * @param {String} text The tab's text and tooltip
46119 setText : function(text){
46121 this.textEl.update(text);
46122 this.setTooltip(text);
46123 //if(!this.tabPanel.resizeTabs){
46124 // this.autoSize();
46128 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46130 activate : function(){
46131 this.tabPanel.activate(this.id);
46135 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46137 disable : function(){
46138 if(this.tabPanel.active != this){
46139 this.disabled = true;
46140 this.status_node.addClass("disabled");
46145 * Enables this TabPanelItem if it was previously disabled.
46147 enable : function(){
46148 this.disabled = false;
46149 this.status_node.removeClass("disabled");
46153 * Sets the content for this TabPanelItem.
46154 * @param {String} content The content
46155 * @param {Boolean} loadScripts true to look for and load scripts
46157 setContent : function(content, loadScripts){
46158 this.bodyEl.update(content, loadScripts);
46162 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46163 * @return {Roo.UpdateManager} The UpdateManager
46165 getUpdateManager : function(){
46166 return this.bodyEl.getUpdateManager();
46170 * Set a URL to be used to load the content for this TabPanelItem.
46171 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46172 * @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)
46173 * @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)
46174 * @return {Roo.UpdateManager} The UpdateManager
46176 setUrl : function(url, params, loadOnce){
46177 if(this.refreshDelegate){
46178 this.un('activate', this.refreshDelegate);
46180 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46181 this.on("activate", this.refreshDelegate);
46182 return this.bodyEl.getUpdateManager();
46186 _handleRefresh : function(url, params, loadOnce){
46187 if(!loadOnce || !this.loaded){
46188 var updater = this.bodyEl.getUpdateManager();
46189 updater.update(url, params, this._setLoaded.createDelegate(this));
46194 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
46195 * Will fail silently if the setUrl method has not been called.
46196 * This does not activate the panel, just updates its content.
46198 refresh : function(){
46199 if(this.refreshDelegate){
46200 this.loaded = false;
46201 this.refreshDelegate();
46206 _setLoaded : function(){
46207 this.loaded = true;
46211 closeClick : function(e){
46214 this.fireEvent("beforeclose", this, o);
46215 if(o.cancel !== true){
46216 this.tabPanel.removeTab(this.id);
46220 * The text displayed in the tooltip for the close icon.
46223 closeText : "Close this tab"
46226 * This script refer to:
46227 * Title: International Telephone Input
46228 * Author: Jack O'Connor
46229 * Code version: v12.1.12
46230 * Availability: https://github.com/jackocnr/intl-tel-input.git
46233 Roo.bootstrap.form.PhoneInputData = function() {
46236 "Afghanistan (افغانستان)",
46241 "Albania (Shqipëri)",
46246 "Algeria (الجزائر)",
46271 "Antigua and Barbuda",
46281 "Armenia (Հայաստան)",
46297 "Austria (Österreich)",
46302 "Azerbaijan (Azərbaycan)",
46312 "Bahrain (البحرين)",
46317 "Bangladesh (বাংলাদেশ)",
46327 "Belarus (Беларусь)",
46332 "Belgium (België)",
46362 "Bosnia and Herzegovina (Босна и Херцеговина)",
46377 "British Indian Ocean Territory",
46382 "British Virgin Islands",
46392 "Bulgaria (България)",
46402 "Burundi (Uburundi)",
46407 "Cambodia (កម្ពុជា)",
46412 "Cameroon (Cameroun)",
46421 ["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"]
46424 "Cape Verde (Kabu Verdi)",
46429 "Caribbean Netherlands",
46440 "Central African Republic (République centrafricaine)",
46460 "Christmas Island",
46466 "Cocos (Keeling) Islands",
46477 "Comoros (جزر القمر)",
46482 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46487 "Congo (Republic) (Congo-Brazzaville)",
46507 "Croatia (Hrvatska)",
46528 "Czech Republic (Česká republika)",
46533 "Denmark (Danmark)",
46548 "Dominican Republic (República Dominicana)",
46552 ["809", "829", "849"]
46570 "Equatorial Guinea (Guinea Ecuatorial)",
46590 "Falkland Islands (Islas Malvinas)",
46595 "Faroe Islands (Føroyar)",
46616 "French Guiana (Guyane française)",
46621 "French Polynesia (Polynésie française)",
46636 "Georgia (საქართველო)",
46641 "Germany (Deutschland)",
46661 "Greenland (Kalaallit Nunaat)",
46698 "Guinea-Bissau (Guiné Bissau)",
46723 "Hungary (Magyarország)",
46728 "Iceland (Ísland)",
46748 "Iraq (العراق)",
46764 "Israel (ישראל)",
46791 "Jordan (الأردن)",
46796 "Kazakhstan (Казахстан)",
46817 "Kuwait (الكويت)",
46822 "Kyrgyzstan (Кыргызстан)",
46832 "Latvia (Latvija)",
46837 "Lebanon (لبنان)",
46852 "Libya (ليبيا)",
46862 "Lithuania (Lietuva)",
46877 "Macedonia (FYROM) (Македонија)",
46882 "Madagascar (Madagasikara)",
46912 "Marshall Islands",
46922 "Mauritania (موريتانيا)",
46927 "Mauritius (Moris)",
46948 "Moldova (Republica Moldova)",
46958 "Mongolia (Монгол)",
46963 "Montenegro (Crna Gora)",
46973 "Morocco (المغرب)",
46979 "Mozambique (Moçambique)",
46984 "Myanmar (Burma) (မြန်မာ)",
46989 "Namibia (Namibië)",
47004 "Netherlands (Nederland)",
47009 "New Caledonia (Nouvelle-Calédonie)",
47044 "North Korea (조선 민주주의 인민 공화국)",
47049 "Northern Mariana Islands",
47065 "Pakistan (پاکستان)",
47075 "Palestine (فلسطين)",
47085 "Papua New Guinea",
47127 "Réunion (La Réunion)",
47133 "Romania (România)",
47149 "Saint Barthélemy",
47160 "Saint Kitts and Nevis",
47170 "Saint Martin (Saint-Martin (partie française))",
47176 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47181 "Saint Vincent and the Grenadines",
47196 "São Tomé and Príncipe (São Tomé e Príncipe)",
47201 "Saudi Arabia (المملكة العربية السعودية)",
47206 "Senegal (Sénégal)",
47236 "Slovakia (Slovensko)",
47241 "Slovenia (Slovenija)",
47251 "Somalia (Soomaaliya)",
47261 "South Korea (대한민국)",
47266 "South Sudan (جنوب السودان)",
47276 "Sri Lanka (ශ්රී ලංකාව)",
47281 "Sudan (السودان)",
47291 "Svalbard and Jan Mayen",
47302 "Sweden (Sverige)",
47307 "Switzerland (Schweiz)",
47312 "Syria (سوريا)",
47357 "Trinidad and Tobago",
47362 "Tunisia (تونس)",
47367 "Turkey (Türkiye)",
47377 "Turks and Caicos Islands",
47387 "U.S. Virgin Islands",
47397 "Ukraine (Україна)",
47402 "United Arab Emirates (الإمارات العربية المتحدة)",
47424 "Uzbekistan (Oʻzbekiston)",
47434 "Vatican City (Città del Vaticano)",
47445 "Vietnam (Việt Nam)",
47450 "Wallis and Futuna (Wallis-et-Futuna)",
47455 "Western Sahara (الصحراء الغربية)",
47461 "Yemen (اليمن)",
47485 * This script refer to:
47486 * Title: International Telephone Input
47487 * Author: Jack O'Connor
47488 * Code version: v12.1.12
47489 * Availability: https://github.com/jackocnr/intl-tel-input.git
47493 * @class Roo.bootstrap.form.PhoneInput
47494 * @extends Roo.bootstrap.form.TriggerField
47495 * An input with International dial-code selection
47497 * @cfg {String} defaultDialCode default '+852'
47498 * @cfg {Array} preferedCountries default []
47501 * Create a new PhoneInput.
47502 * @param {Object} config Configuration options
47505 Roo.bootstrap.form.PhoneInput = function(config) {
47506 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47509 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47511 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47513 listWidth: undefined,
47515 selectedClass: 'active',
47517 invalidClass : "has-warning",
47519 validClass: 'has-success',
47521 allowed: '0123456789',
47526 * @cfg {String} defaultDialCode The default dial code when initializing the input
47528 defaultDialCode: '+852',
47531 * @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
47533 preferedCountries: false,
47535 getAutoCreate : function()
47537 var data = Roo.bootstrap.form.PhoneInputData();
47538 var align = this.labelAlign || this.parentLabelAlign();
47541 this.allCountries = [];
47542 this.dialCodeMapping = [];
47544 for (var i = 0; i < data.length; i++) {
47546 this.allCountries[i] = {
47550 priority: c[3] || 0,
47551 areaCodes: c[4] || null
47553 this.dialCodeMapping[c[2]] = {
47556 priority: c[3] || 0,
47557 areaCodes: c[4] || null
47569 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47570 maxlength: this.max_length,
47571 cls : 'form-control tel-input',
47572 autocomplete: 'new-password'
47575 var hiddenInput = {
47578 cls: 'hidden-tel-input'
47582 hiddenInput.name = this.name;
47585 if (this.disabled) {
47586 input.disabled = true;
47589 var flag_container = {
47606 cls: this.hasFeedback ? 'has-feedback' : '',
47612 cls: 'dial-code-holder',
47619 cls: 'roo-select2-container input-group',
47626 if (this.fieldLabel.length) {
47629 tooltip: 'This field is required'
47635 cls: 'control-label',
47641 html: this.fieldLabel
47644 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47650 if(this.indicatorpos == 'right') {
47651 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47658 if(align == 'left') {
47666 if(this.labelWidth > 12){
47667 label.style = "width: " + this.labelWidth + 'px';
47669 if(this.labelWidth < 13 && this.labelmd == 0){
47670 this.labelmd = this.labelWidth;
47672 if(this.labellg > 0){
47673 label.cls += ' col-lg-' + this.labellg;
47674 input.cls += ' col-lg-' + (12 - this.labellg);
47676 if(this.labelmd > 0){
47677 label.cls += ' col-md-' + this.labelmd;
47678 container.cls += ' col-md-' + (12 - this.labelmd);
47680 if(this.labelsm > 0){
47681 label.cls += ' col-sm-' + this.labelsm;
47682 container.cls += ' col-sm-' + (12 - this.labelsm);
47684 if(this.labelxs > 0){
47685 label.cls += ' col-xs-' + this.labelxs;
47686 container.cls += ' col-xs-' + (12 - this.labelxs);
47696 var settings = this;
47698 ['xs','sm','md','lg'].map(function(size){
47699 if (settings[size]) {
47700 cfg.cls += ' col-' + size + '-' + settings[size];
47704 this.store = new Roo.data.Store({
47705 proxy : new Roo.data.MemoryProxy({}),
47706 reader : new Roo.data.JsonReader({
47717 'name' : 'dialCode',
47721 'name' : 'priority',
47725 'name' : 'areaCodes',
47732 if(!this.preferedCountries) {
47733 this.preferedCountries = [
47740 var p = this.preferedCountries.reverse();
47743 for (var i = 0; i < p.length; i++) {
47744 for (var j = 0; j < this.allCountries.length; j++) {
47745 if(this.allCountries[j].iso2 == p[i]) {
47746 var t = this.allCountries[j];
47747 this.allCountries.splice(j,1);
47748 this.allCountries.unshift(t);
47754 this.store.proxy.data = {
47756 data: this.allCountries
47762 initEvents : function()
47765 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47767 this.indicator = this.indicatorEl();
47768 this.flag = this.flagEl();
47769 this.dialCodeHolder = this.dialCodeHolderEl();
47771 this.trigger = this.el.select('div.flag-box',true).first();
47772 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47777 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47778 _this.list.setWidth(lw);
47781 this.list.on('mouseover', this.onViewOver, this);
47782 this.list.on('mousemove', this.onViewMove, this);
47783 this.inputEl().on("keyup", this.onKeyUp, this);
47784 this.inputEl().on("keypress", this.onKeyPress, this);
47786 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47788 this.view = new Roo.View(this.list, this.tpl, {
47789 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47792 this.view.on('click', this.onViewClick, this);
47793 this.setValue(this.defaultDialCode);
47796 onTriggerClick : function(e)
47798 Roo.log('trigger click');
47803 if(this.isExpanded()){
47805 this.hasFocus = false;
47807 this.store.load({});
47808 this.hasFocus = true;
47813 isExpanded : function()
47815 return this.list.isVisible();
47818 collapse : function()
47820 if(!this.isExpanded()){
47824 Roo.get(document).un('mousedown', this.collapseIf, this);
47825 Roo.get(document).un('mousewheel', this.collapseIf, this);
47826 this.fireEvent('collapse', this);
47830 expand : function()
47834 if(this.isExpanded() || !this.hasFocus){
47838 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47839 this.list.setWidth(lw);
47842 this.restrictHeight();
47844 Roo.get(document).on('mousedown', this.collapseIf, this);
47845 Roo.get(document).on('mousewheel', this.collapseIf, this);
47847 this.fireEvent('expand', this);
47850 restrictHeight : function()
47852 this.list.alignTo(this.inputEl(), this.listAlign);
47853 this.list.alignTo(this.inputEl(), this.listAlign);
47856 onViewOver : function(e, t)
47858 if(this.inKeyMode){
47861 var item = this.view.findItemFromChild(t);
47864 var index = this.view.indexOf(item);
47865 this.select(index, false);
47870 onViewClick : function(view, doFocus, el, e)
47872 var index = this.view.getSelectedIndexes()[0];
47874 var r = this.store.getAt(index);
47877 this.onSelect(r, index);
47879 if(doFocus !== false && !this.blockFocus){
47880 this.inputEl().focus();
47884 onViewMove : function(e, t)
47886 this.inKeyMode = false;
47889 select : function(index, scrollIntoView)
47891 this.selectedIndex = index;
47892 this.view.select(index);
47893 if(scrollIntoView !== false){
47894 var el = this.view.getNode(index);
47896 this.list.scrollChildIntoView(el, false);
47901 createList : function()
47903 this.list = Roo.get(document.body).createChild({
47905 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47906 style: 'display:none'
47909 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47912 collapseIf : function(e)
47914 var in_combo = e.within(this.el);
47915 var in_list = e.within(this.list);
47916 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47918 if (in_combo || in_list || is_list) {
47924 onSelect : function(record, index)
47926 if(this.fireEvent('beforeselect', this, record, index) !== false){
47928 this.setFlagClass(record.data.iso2);
47929 this.setDialCode(record.data.dialCode);
47930 this.hasFocus = false;
47932 this.fireEvent('select', this, record, index);
47936 flagEl : function()
47938 var flag = this.el.select('div.flag',true).first();
47945 dialCodeHolderEl : function()
47947 var d = this.el.select('input.dial-code-holder',true).first();
47954 setDialCode : function(v)
47956 this.dialCodeHolder.dom.value = '+'+v;
47959 setFlagClass : function(n)
47961 this.flag.dom.className = 'flag '+n;
47964 getValue : function()
47966 var v = this.inputEl().getValue();
47967 if(this.dialCodeHolder) {
47968 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
47973 setValue : function(v)
47975 var d = this.getDialCode(v);
47977 //invalid dial code
47978 if(v.length == 0 || !d || d.length == 0) {
47980 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
47981 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47987 this.setFlagClass(this.dialCodeMapping[d].iso2);
47988 this.setDialCode(d);
47989 this.inputEl().dom.value = v.replace('+'+d,'');
47990 this.hiddenEl().dom.value = this.getValue();
47995 getDialCode : function(v)
47999 if (v.length == 0) {
48000 return this.dialCodeHolder.dom.value;
48004 if (v.charAt(0) != "+") {
48007 var numericChars = "";
48008 for (var i = 1; i < v.length; i++) {
48009 var c = v.charAt(i);
48012 if (this.dialCodeMapping[numericChars]) {
48013 dialCode = v.substr(1, i);
48015 if (numericChars.length == 4) {
48025 this.setValue(this.defaultDialCode);
48029 hiddenEl : function()
48031 return this.el.select('input.hidden-tel-input',true).first();
48034 // after setting val
48035 onKeyUp : function(e){
48036 this.setValue(this.getValue());
48039 onKeyPress : function(e){
48040 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48047 * @class Roo.bootstrap.form.MoneyField
48048 * @extends Roo.bootstrap.form.ComboBox
48049 * Bootstrap MoneyField class
48052 * Create a new MoneyField.
48053 * @param {Object} config Configuration options
48056 Roo.bootstrap.form.MoneyField = function(config) {
48058 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48062 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48065 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48067 allowDecimals : true,
48069 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48071 decimalSeparator : ".",
48073 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48075 decimalPrecision : 0,
48077 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48079 allowNegative : true,
48081 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48085 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48087 minValue : Number.NEGATIVE_INFINITY,
48089 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48091 maxValue : Number.MAX_VALUE,
48093 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48095 minText : "The minimum value for this field is {0}",
48097 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48099 maxText : "The maximum value for this field is {0}",
48101 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48102 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48104 nanText : "{0} is not a valid number",
48106 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48110 * @cfg {String} defaults currency of the MoneyField
48111 * value should be in lkey
48113 defaultCurrency : false,
48115 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48117 thousandsDelimiter : false,
48119 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48128 * @cfg {Roo.data.Store} store Store to lookup currency??
48132 getAutoCreate : function()
48134 var align = this.labelAlign || this.parentLabelAlign();
48146 cls : 'form-control roo-money-amount-input',
48147 autocomplete: 'new-password'
48150 var hiddenInput = {
48154 cls: 'hidden-number-input'
48157 if(this.max_length) {
48158 input.maxlength = this.max_length;
48162 hiddenInput.name = this.name;
48165 if (this.disabled) {
48166 input.disabled = true;
48169 var clg = 12 - this.inputlg;
48170 var cmd = 12 - this.inputmd;
48171 var csm = 12 - this.inputsm;
48172 var cxs = 12 - this.inputxs;
48176 cls : 'row roo-money-field',
48180 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48184 cls: 'roo-select2-container input-group',
48188 cls : 'form-control roo-money-currency-input',
48189 autocomplete: 'new-password',
48191 name : this.currencyName
48195 cls : 'input-group-addon',
48209 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48213 cls: this.hasFeedback ? 'has-feedback' : '',
48224 if (this.fieldLabel.length) {
48227 tooltip: 'This field is required'
48233 cls: 'control-label',
48239 html: this.fieldLabel
48242 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48248 if(this.indicatorpos == 'right') {
48249 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48256 if(align == 'left') {
48264 if(this.labelWidth > 12){
48265 label.style = "width: " + this.labelWidth + 'px';
48267 if(this.labelWidth < 13 && this.labelmd == 0){
48268 this.labelmd = this.labelWidth;
48270 if(this.labellg > 0){
48271 label.cls += ' col-lg-' + this.labellg;
48272 input.cls += ' col-lg-' + (12 - this.labellg);
48274 if(this.labelmd > 0){
48275 label.cls += ' col-md-' + this.labelmd;
48276 container.cls += ' col-md-' + (12 - this.labelmd);
48278 if(this.labelsm > 0){
48279 label.cls += ' col-sm-' + this.labelsm;
48280 container.cls += ' col-sm-' + (12 - this.labelsm);
48282 if(this.labelxs > 0){
48283 label.cls += ' col-xs-' + this.labelxs;
48284 container.cls += ' col-xs-' + (12 - this.labelxs);
48295 var settings = this;
48297 ['xs','sm','md','lg'].map(function(size){
48298 if (settings[size]) {
48299 cfg.cls += ' col-' + size + '-' + settings[size];
48306 initEvents : function()
48308 this.indicator = this.indicatorEl();
48310 this.initCurrencyEvent();
48312 this.initNumberEvent();
48315 initCurrencyEvent : function()
48318 throw "can not find store for combo";
48321 this.store = Roo.factory(this.store, Roo.data);
48322 this.store.parent = this;
48326 this.triggerEl = this.el.select('.input-group-addon', true).first();
48328 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48333 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48334 _this.list.setWidth(lw);
48337 this.list.on('mouseover', this.onViewOver, this);
48338 this.list.on('mousemove', this.onViewMove, this);
48339 this.list.on('scroll', this.onViewScroll, this);
48342 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48345 this.view = new Roo.View(this.list, this.tpl, {
48346 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48349 this.view.on('click', this.onViewClick, this);
48351 this.store.on('beforeload', this.onBeforeLoad, this);
48352 this.store.on('load', this.onLoad, this);
48353 this.store.on('loadexception', this.onLoadException, this);
48355 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48356 "up" : function(e){
48357 this.inKeyMode = true;
48361 "down" : function(e){
48362 if(!this.isExpanded()){
48363 this.onTriggerClick();
48365 this.inKeyMode = true;
48370 "enter" : function(e){
48373 if(this.fireEvent("specialkey", this, e)){
48374 this.onViewClick(false);
48380 "esc" : function(e){
48384 "tab" : function(e){
48387 if(this.fireEvent("specialkey", this, e)){
48388 this.onViewClick(false);
48396 doRelay : function(foo, bar, hname){
48397 if(hname == 'down' || this.scope.isExpanded()){
48398 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48406 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48410 initNumberEvent : function(e)
48412 this.inputEl().on("keydown" , this.fireKey, this);
48413 this.inputEl().on("focus", this.onFocus, this);
48414 this.inputEl().on("blur", this.onBlur, this);
48416 this.inputEl().relayEvent('keyup', this);
48418 if(this.indicator){
48419 this.indicator.addClass('invisible');
48422 this.originalValue = this.getValue();
48424 if(this.validationEvent == 'keyup'){
48425 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48426 this.inputEl().on('keyup', this.filterValidation, this);
48428 else if(this.validationEvent !== false){
48429 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48432 if(this.selectOnFocus){
48433 this.on("focus", this.preFocus, this);
48436 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48437 this.inputEl().on("keypress", this.filterKeys, this);
48439 this.inputEl().relayEvent('keypress', this);
48442 var allowed = "0123456789";
48444 if(this.allowDecimals){
48445 allowed += this.decimalSeparator;
48448 if(this.allowNegative){
48452 if(this.thousandsDelimiter) {
48456 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48458 var keyPress = function(e){
48460 var k = e.getKey();
48462 var c = e.getCharCode();
48465 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48466 allowed.indexOf(String.fromCharCode(c)) === -1
48472 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48476 if(allowed.indexOf(String.fromCharCode(c)) === -1){
48481 this.inputEl().on("keypress", keyPress, this);
48485 onTriggerClick : function(e)
48492 this.loadNext = false;
48494 if(this.isExpanded()){
48499 this.hasFocus = true;
48501 if(this.triggerAction == 'all') {
48502 this.doQuery(this.allQuery, true);
48506 this.doQuery(this.getRawValue());
48509 getCurrency : function()
48511 var v = this.currencyEl().getValue();
48516 restrictHeight : function()
48518 this.list.alignTo(this.currencyEl(), this.listAlign);
48519 this.list.alignTo(this.currencyEl(), this.listAlign);
48522 onViewClick : function(view, doFocus, el, e)
48524 var index = this.view.getSelectedIndexes()[0];
48526 var r = this.store.getAt(index);
48529 this.onSelect(r, index);
48533 onSelect : function(record, index){
48535 if(this.fireEvent('beforeselect', this, record, index) !== false){
48537 this.setFromCurrencyData(index > -1 ? record.data : false);
48541 this.fireEvent('select', this, record, index);
48545 setFromCurrencyData : function(o)
48549 this.lastCurrency = o;
48551 if (this.currencyField) {
48552 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48554 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
48557 this.lastSelectionText = currency;
48559 //setting default currency
48560 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48561 this.setCurrency(this.defaultCurrency);
48565 this.setCurrency(currency);
48568 setFromData : function(o)
48572 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48574 this.setFromCurrencyData(c);
48579 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48581 Roo.log('no value set for '+ (this.name ? this.name : this.id));
48584 this.setValue(value);
48588 setCurrency : function(v)
48590 this.currencyValue = v;
48593 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48598 setValue : function(v)
48600 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48606 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48608 this.inputEl().dom.value = (v == '') ? '' :
48609 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48611 if(!this.allowZero && v === '0') {
48612 this.hiddenEl().dom.value = '';
48613 this.inputEl().dom.value = '';
48620 getRawValue : function()
48622 var v = this.inputEl().getValue();
48627 getValue : function()
48629 return this.fixPrecision(this.parseValue(this.getRawValue()));
48632 parseValue : function(value)
48634 if(this.thousandsDelimiter) {
48636 r = new RegExp(",", "g");
48637 value = value.replace(r, "");
48640 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48641 return isNaN(value) ? '' : value;
48645 fixPrecision : function(value)
48647 if(this.thousandsDelimiter) {
48649 r = new RegExp(",", "g");
48650 value = value.replace(r, "");
48653 var nan = isNaN(value);
48655 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48656 return nan ? '' : value;
48658 return parseFloat(value).toFixed(this.decimalPrecision);
48661 decimalPrecisionFcn : function(v)
48663 return Math.floor(v);
48666 validateValue : function(value)
48668 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48672 var num = this.parseValue(value);
48675 this.markInvalid(String.format(this.nanText, value));
48679 if(num < this.minValue){
48680 this.markInvalid(String.format(this.minText, this.minValue));
48684 if(num > this.maxValue){
48685 this.markInvalid(String.format(this.maxText, this.maxValue));
48692 validate : function()
48694 if(this.disabled || this.allowBlank){
48699 var currency = this.getCurrency();
48701 if(this.validateValue(this.getRawValue()) && currency.length){
48706 this.markInvalid();
48710 getName: function()
48715 beforeBlur : function()
48721 var v = this.parseValue(this.getRawValue());
48728 onBlur : function()
48732 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48733 //this.el.removeClass(this.focusClass);
48736 this.hasFocus = false;
48738 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48742 var v = this.getValue();
48744 if(String(v) !== String(this.startValue)){
48745 this.fireEvent('change', this, v, this.startValue);
48748 this.fireEvent("blur", this);
48751 inputEl : function()
48753 return this.el.select('.roo-money-amount-input', true).first();
48756 currencyEl : function()
48758 return this.el.select('.roo-money-currency-input', true).first();
48761 hiddenEl : function()
48763 return this.el.select('input.hidden-number-input',true).first();
48767 * @class Roo.bootstrap.BezierSignature
48768 * @extends Roo.bootstrap.Component
48769 * Bootstrap BezierSignature class
48770 * This script refer to:
48771 * Title: Signature Pad
48773 * Availability: https://github.com/szimek/signature_pad
48776 * Create a new BezierSignature
48777 * @param {Object} config The config object
48780 Roo.bootstrap.BezierSignature = function(config){
48781 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48787 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48794 mouse_btn_down: true,
48797 * @cfg {int} canvas height
48799 canvas_height: '200px',
48802 * @cfg {float|function} Radius of a single dot.
48807 * @cfg {float} Minimum width of a line. Defaults to 0.5.
48812 * @cfg {float} Maximum width of a line. Defaults to 2.5.
48817 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48822 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48827 * @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.
48829 bg_color: 'rgba(0, 0, 0, 0)',
48832 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48834 dot_color: 'black',
48837 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48839 velocity_filter_weight: 0.7,
48842 * @cfg {function} Callback when stroke begin.
48847 * @cfg {function} Callback when stroke end.
48851 getAutoCreate : function()
48853 var cls = 'roo-signature column';
48856 cls += ' ' + this.cls;
48866 for(var i = 0; i < col_sizes.length; i++) {
48867 if(this[col_sizes[i]]) {
48868 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48878 cls: 'roo-signature-body',
48882 cls: 'roo-signature-body-canvas',
48883 height: this.canvas_height,
48884 width: this.canvas_width
48891 style: 'display: none'
48899 initEvents: function()
48901 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48903 var canvas = this.canvasEl();
48905 // mouse && touch event swapping...
48906 canvas.dom.style.touchAction = 'none';
48907 canvas.dom.style.msTouchAction = 'none';
48909 this.mouse_btn_down = false;
48910 canvas.on('mousedown', this._handleMouseDown, this);
48911 canvas.on('mousemove', this._handleMouseMove, this);
48912 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48914 if (window.PointerEvent) {
48915 canvas.on('pointerdown', this._handleMouseDown, this);
48916 canvas.on('pointermove', this._handleMouseMove, this);
48917 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48920 if ('ontouchstart' in window) {
48921 canvas.on('touchstart', this._handleTouchStart, this);
48922 canvas.on('touchmove', this._handleTouchMove, this);
48923 canvas.on('touchend', this._handleTouchEnd, this);
48926 Roo.EventManager.onWindowResize(this.resize, this, true);
48928 // file input event
48929 this.fileEl().on('change', this.uploadImage, this);
48936 resize: function(){
48938 var canvas = this.canvasEl().dom;
48939 var ctx = this.canvasElCtx();
48940 var img_data = false;
48942 if(canvas.width > 0) {
48943 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48945 // setting canvas width will clean img data
48948 var style = window.getComputedStyle ?
48949 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48951 var padding_left = parseInt(style.paddingLeft) || 0;
48952 var padding_right = parseInt(style.paddingRight) || 0;
48954 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48957 ctx.putImageData(img_data, 0, 0);
48961 _handleMouseDown: function(e)
48963 if (e.browserEvent.which === 1) {
48964 this.mouse_btn_down = true;
48965 this.strokeBegin(e);
48969 _handleMouseMove: function (e)
48971 if (this.mouse_btn_down) {
48972 this.strokeMoveUpdate(e);
48976 _handleMouseUp: function (e)
48978 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
48979 this.mouse_btn_down = false;
48984 _handleTouchStart: function (e) {
48986 e.preventDefault();
48987 if (e.browserEvent.targetTouches.length === 1) {
48988 // var touch = e.browserEvent.changedTouches[0];
48989 // this.strokeBegin(touch);
48991 this.strokeBegin(e); // assume e catching the correct xy...
48995 _handleTouchMove: function (e) {
48996 e.preventDefault();
48997 // var touch = event.targetTouches[0];
48998 // _this._strokeMoveUpdate(touch);
48999 this.strokeMoveUpdate(e);
49002 _handleTouchEnd: function (e) {
49003 var wasCanvasTouched = e.target === this.canvasEl().dom;
49004 if (wasCanvasTouched) {
49005 e.preventDefault();
49006 // var touch = event.changedTouches[0];
49007 // _this._strokeEnd(touch);
49012 reset: function () {
49013 this._lastPoints = [];
49014 this._lastVelocity = 0;
49015 this._lastWidth = (this.min_width + this.max_width) / 2;
49016 this.canvasElCtx().fillStyle = this.dot_color;
49019 strokeMoveUpdate: function(e)
49021 this.strokeUpdate(e);
49023 if (this.throttle) {
49024 this.throttleStroke(this.strokeUpdate, this.throttle);
49027 this.strokeUpdate(e);
49031 strokeBegin: function(e)
49033 var newPointGroup = {
49034 color: this.dot_color,
49038 if (typeof this.onBegin === 'function') {
49042 this.curve_data.push(newPointGroup);
49044 this.strokeUpdate(e);
49047 strokeUpdate: function(e)
49049 var rect = this.canvasEl().dom.getBoundingClientRect();
49050 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49051 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49052 var lastPoints = lastPointGroup.points;
49053 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49054 var isLastPointTooClose = lastPoint
49055 ? point.distanceTo(lastPoint) <= this.min_distance
49057 var color = lastPointGroup.color;
49058 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49059 var curve = this.addPoint(point);
49061 this.drawDot({color: color, point: point});
49064 this.drawCurve({color: color, curve: curve});
49074 strokeEnd: function(e)
49076 this.strokeUpdate(e);
49077 if (typeof this.onEnd === 'function') {
49082 addPoint: function (point) {
49083 var _lastPoints = this._lastPoints;
49084 _lastPoints.push(point);
49085 if (_lastPoints.length > 2) {
49086 if (_lastPoints.length === 3) {
49087 _lastPoints.unshift(_lastPoints[0]);
49089 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49090 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49091 _lastPoints.shift();
49097 calculateCurveWidths: function (startPoint, endPoint) {
49098 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49099 (1 - this.velocity_filter_weight) * this._lastVelocity;
49101 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49104 start: this._lastWidth
49107 this._lastVelocity = velocity;
49108 this._lastWidth = newWidth;
49112 drawDot: function (_a) {
49113 var color = _a.color, point = _a.point;
49114 var ctx = this.canvasElCtx();
49115 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49117 this.drawCurveSegment(point.x, point.y, width);
49119 ctx.fillStyle = color;
49123 drawCurve: function (_a) {
49124 var color = _a.color, curve = _a.curve;
49125 var ctx = this.canvasElCtx();
49126 var widthDelta = curve.endWidth - curve.startWidth;
49127 var drawSteps = Math.floor(curve.length()) * 2;
49129 ctx.fillStyle = color;
49130 for (var i = 0; i < drawSteps; i += 1) {
49131 var t = i / drawSteps;
49137 var x = uuu * curve.startPoint.x;
49138 x += 3 * uu * t * curve.control1.x;
49139 x += 3 * u * tt * curve.control2.x;
49140 x += ttt * curve.endPoint.x;
49141 var y = uuu * curve.startPoint.y;
49142 y += 3 * uu * t * curve.control1.y;
49143 y += 3 * u * tt * curve.control2.y;
49144 y += ttt * curve.endPoint.y;
49145 var width = curve.startWidth + ttt * widthDelta;
49146 this.drawCurveSegment(x, y, width);
49152 drawCurveSegment: function (x, y, width) {
49153 var ctx = this.canvasElCtx();
49155 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49156 this.is_empty = false;
49161 var ctx = this.canvasElCtx();
49162 var canvas = this.canvasEl().dom;
49163 ctx.fillStyle = this.bg_color;
49164 ctx.clearRect(0, 0, canvas.width, canvas.height);
49165 ctx.fillRect(0, 0, canvas.width, canvas.height);
49166 this.curve_data = [];
49168 this.is_empty = true;
49173 return this.el.select('input',true).first();
49176 canvasEl: function()
49178 return this.el.select('canvas',true).first();
49181 canvasElCtx: function()
49183 return this.el.select('canvas',true).first().dom.getContext('2d');
49186 getImage: function(type)
49188 if(this.is_empty) {
49193 return this.canvasEl().dom.toDataURL('image/'+type, 1);
49196 drawFromImage: function(img_src)
49198 var img = new Image();
49200 img.onload = function(){
49201 this.canvasElCtx().drawImage(img, 0, 0);
49206 this.is_empty = false;
49209 selectImage: function()
49211 this.fileEl().dom.click();
49214 uploadImage: function(e)
49216 var reader = new FileReader();
49218 reader.onload = function(e){
49219 var img = new Image();
49220 img.onload = function(){
49222 this.canvasElCtx().drawImage(img, 0, 0);
49224 img.src = e.target.result;
49227 reader.readAsDataURL(e.target.files[0]);
49230 // Bezier Point Constructor
49231 Point: (function () {
49232 function Point(x, y, time) {
49235 this.time = time || Date.now();
49237 Point.prototype.distanceTo = function (start) {
49238 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49240 Point.prototype.equals = function (other) {
49241 return this.x === other.x && this.y === other.y && this.time === other.time;
49243 Point.prototype.velocityFrom = function (start) {
49244 return this.time !== start.time
49245 ? this.distanceTo(start) / (this.time - start.time)
49252 // Bezier Constructor
49253 Bezier: (function () {
49254 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49255 this.startPoint = startPoint;
49256 this.control2 = control2;
49257 this.control1 = control1;
49258 this.endPoint = endPoint;
49259 this.startWidth = startWidth;
49260 this.endWidth = endWidth;
49262 Bezier.fromPoints = function (points, widths, scope) {
49263 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49264 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49265 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49267 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49268 var dx1 = s1.x - s2.x;
49269 var dy1 = s1.y - s2.y;
49270 var dx2 = s2.x - s3.x;
49271 var dy2 = s2.y - s3.y;
49272 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49273 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49274 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49275 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49276 var dxm = m1.x - m2.x;
49277 var dym = m1.y - m2.y;
49278 var k = l2 / (l1 + l2);
49279 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49280 var tx = s2.x - cm.x;
49281 var ty = s2.y - cm.y;
49283 c1: new scope.Point(m1.x + tx, m1.y + ty),
49284 c2: new scope.Point(m2.x + tx, m2.y + ty)
49287 Bezier.prototype.length = function () {
49292 for (var i = 0; i <= steps; i += 1) {
49294 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49295 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49297 var xdiff = cx - px;
49298 var ydiff = cy - py;
49299 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49306 Bezier.prototype.point = function (t, start, c1, c2, end) {
49307 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49308 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49309 + (3.0 * c2 * (1.0 - t) * t * t)
49310 + (end * t * t * t);
49315 throttleStroke: function(fn, wait) {
49316 if (wait === void 0) { wait = 250; }
49318 var timeout = null;
49322 var later = function () {
49323 previous = Date.now();
49325 result = fn.apply(storedContext, storedArgs);
49327 storedContext = null;
49331 return function wrapper() {
49333 for (var _i = 0; _i < arguments.length; _i++) {
49334 args[_i] = arguments[_i];
49336 var now = Date.now();
49337 var remaining = wait - (now - previous);
49338 storedContext = this;
49340 if (remaining <= 0 || remaining > wait) {
49342 clearTimeout(timeout);
49346 result = fn.apply(storedContext, storedArgs);
49348 storedContext = null;
49352 else if (!timeout) {
49353 timeout = window.setTimeout(later, remaining);
49363 // old names for form elements
49364 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
49365 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
49366 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
49367 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
49368 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
49369 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
49370 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
49371 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
49372 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
49373 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
49374 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
49375 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
49376 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
49377 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
49378 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
49379 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
49380 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
49381 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
49382 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
49383 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
49384 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
49385 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
49386 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
49387 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
49388 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
49389 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
49391 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
49392 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49394 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
49395 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
49397 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
49398 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49399 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
49400 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator