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();
4549 closeClick : function()
4554 initEvents : function()
4556 if (this.allow_close) {
4557 this.closeEl.on('click', this.closeClick, this);
4559 Roo.EventManager.onWindowResize(this.resize, this, true);
4560 if (this.editableTitle) {
4561 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4562 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4563 this.headerEditEl.on('keyup', function(e) {
4564 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4565 this.toggleHeaderInput(false)
4568 this.headerEditEl.on('blur', function(e) {
4569 this.toggleHeaderInput(false)
4578 this.maskEl.setSize(
4579 Roo.lib.Dom.getViewWidth(true),
4580 Roo.lib.Dom.getViewHeight(true)
4583 if (this.fitwindow) {
4585 this.dialogEl.setStyle( { 'max-width' : '100%' });
4587 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4588 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4593 if(this.max_width !== 0) {
4595 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4598 this.setSize(w, this.height);
4602 if(this.max_height) {
4603 this.setSize(w,Math.min(
4605 Roo.lib.Dom.getViewportHeight(true) - 60
4611 if(!this.fit_content) {
4612 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4616 this.setSize(w, Math.min(
4618 this.headerEl.getHeight() +
4619 this.footerEl.getHeight() +
4620 this.getChildHeight(this.bodyEl.dom.childNodes),
4621 Roo.lib.Dom.getViewportHeight(true) - 60)
4627 setSize : function(w,h)
4634 // any layout/border etc.. resize..
4636 this.items.forEach( function(e) {
4637 e.layout ? e.layout() : false;
4646 if (!this.rendered) {
4649 this.toggleHeaderInput(false);
4650 //this.el.setStyle('display', 'block');
4651 this.el.removeClass('hideing');
4652 this.el.dom.style.display='block';
4654 Roo.get(document.body).addClass('modal-open');
4656 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4659 this.el.addClass('show');
4660 this.el.addClass('in');
4663 this.el.addClass('show');
4664 this.el.addClass('in');
4667 // not sure how we can show data in here..
4669 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4672 Roo.get(document.body).addClass("x-body-masked");
4674 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4675 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4676 this.maskEl.dom.style.display = 'block';
4677 this.maskEl.addClass('show');
4682 this.fireEvent('show', this);
4684 // set zindex here - otherwise it appears to be ignored...
4685 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4688 // this is for children that are... layout.Border
4690 this.items.forEach( function(e) {
4691 e.layout ? e.layout() : false;
4699 if(this.fireEvent("beforehide", this) !== false){
4701 this.maskEl.removeClass('show');
4703 this.maskEl.dom.style.display = '';
4704 Roo.get(document.body).removeClass("x-body-masked");
4705 this.el.removeClass('in');
4706 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4708 if(this.animate){ // why
4709 this.el.addClass('hideing');
4710 this.el.removeClass('show');
4712 if (!this.el.hasClass('hideing')) {
4713 return; // it's been shown again...
4716 this.el.dom.style.display='';
4718 Roo.get(document.body).removeClass('modal-open');
4719 this.el.removeClass('hideing');
4723 this.el.removeClass('show');
4724 this.el.dom.style.display='';
4725 Roo.get(document.body).removeClass('modal-open');
4728 this.fireEvent('hide', this);
4731 isVisible : function()
4734 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4738 addButton : function(str, cb)
4742 var b = Roo.apply({}, { html : str } );
4743 b.xns = b.xns || Roo.bootstrap;
4744 b.xtype = b.xtype || 'Button';
4745 if (typeof(b.listeners) == 'undefined') {
4746 b.listeners = { click : cb.createDelegate(this) };
4749 var btn = Roo.factory(b);
4751 btn.render(this.getButtonContainer());
4757 setDefaultButton : function(btn)
4759 //this.el.select('.modal-footer').()
4762 resizeTo: function(w,h)
4764 this.dialogEl.setWidth(w);
4766 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4768 this.bodyEl.setHeight(h - diff);
4770 this.fireEvent('resize', this);
4773 setContentSize : function(w, h)
4777 onButtonClick: function(btn,e)
4780 this.fireEvent('btnclick', btn.name, e);
4783 * Set the title of the Dialog
4784 * @param {String} str new Title
4786 setTitle: function(str) {
4787 this.titleEl.dom.innerHTML = str;
4791 * Set the body of the Dialog
4792 * @param {String} str new Title
4794 setBody: function(str) {
4795 this.bodyEl.dom.innerHTML = str;
4798 * Set the body of the Dialog using the template
4799 * @param {Obj} data - apply this data to the template and replace the body contents.
4801 applyBody: function(obj)
4804 Roo.log("Error - using apply Body without a template");
4807 this.tmpl.overwrite(this.bodyEl, obj);
4810 getChildHeight : function(child_nodes)
4814 child_nodes.length == 0
4819 var child_height = 0;
4821 for(var i = 0; i < child_nodes.length; i++) {
4824 * for modal with tabs...
4825 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4827 var layout_childs = child_nodes[i].childNodes;
4829 for(var j = 0; j < layout_childs.length; j++) {
4831 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4833 var layout_body_childs = layout_childs[j].childNodes;
4835 for(var k = 0; k < layout_body_childs.length; k++) {
4837 if(layout_body_childs[k].classList.contains('navbar')) {
4838 child_height += layout_body_childs[k].offsetHeight;
4842 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4844 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4846 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4848 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4849 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4864 child_height += child_nodes[i].offsetHeight;
4865 // Roo.log(child_nodes[i].offsetHeight);
4868 return child_height;
4870 toggleHeaderInput : function(is_edit)
4872 if (!this.editableTitle) {
4873 return; // not editable.
4875 if (is_edit && this.is_header_editing) {
4876 return; // already editing..
4880 this.headerEditEl.dom.value = this.title;
4881 this.headerEditEl.removeClass('d-none');
4882 this.headerEditEl.dom.focus();
4883 this.titleEl.addClass('d-none');
4885 this.is_header_editing = true;
4888 // flip back to not editing.
4889 this.title = this.headerEditEl.dom.value;
4890 this.headerEditEl.addClass('d-none');
4891 this.titleEl.removeClass('d-none');
4892 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4893 this.is_header_editing = false;
4894 this.fireEvent('titlechanged', this, this.title);
4903 Roo.apply(Roo.bootstrap.Modal, {
4905 * Button config that displays a single OK button
4914 * Button config that displays Yes and No buttons
4930 * Button config that displays OK and Cancel buttons
4945 * Button config that displays Yes, No and Cancel buttons
4970 * messagebox - can be used as a replace
4974 * @class Roo.MessageBox
4975 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4979 Roo.Msg.alert('Status', 'Changes saved successfully.');
4981 // Prompt for user data:
4982 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4984 // process text value...
4988 // Show a dialog using config options:
4990 title:'Save Changes?',
4991 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4992 buttons: Roo.Msg.YESNOCANCEL,
4999 Roo.bootstrap.MessageBox = function(){
5000 var dlg, opt, mask, waitTimer;
5001 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5002 var buttons, activeTextEl, bwidth;
5006 var handleButton = function(button){
5008 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5012 var handleHide = function(){
5014 dlg.el.removeClass(opt.cls);
5017 // Roo.TaskMgr.stop(waitTimer);
5018 // waitTimer = null;
5023 var updateButtons = function(b){
5026 buttons["ok"].hide();
5027 buttons["cancel"].hide();
5028 buttons["yes"].hide();
5029 buttons["no"].hide();
5030 dlg.footerEl.hide();
5034 dlg.footerEl.show();
5035 for(var k in buttons){
5036 if(typeof buttons[k] != "function"){
5039 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5040 width += buttons[k].el.getWidth()+15;
5050 var handleEsc = function(d, k, e){
5051 if(opt && opt.closable !== false){
5061 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5062 * @return {Roo.BasicDialog} The BasicDialog element
5064 getDialog : function(){
5066 dlg = new Roo.bootstrap.Modal( {
5069 //constraintoviewport:false,
5071 //collapsible : false,
5076 //buttonAlign:"center",
5077 closeClick : function(){
5078 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5081 handleButton("cancel");
5086 dlg.on("hide", handleHide);
5088 //dlg.addKeyListener(27, handleEsc);
5090 this.buttons = buttons;
5091 var bt = this.buttonText;
5092 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5093 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5094 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5095 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5097 bodyEl = dlg.bodyEl.createChild({
5099 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5100 '<textarea class="roo-mb-textarea"></textarea>' +
5101 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5103 msgEl = bodyEl.dom.firstChild;
5104 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5105 textboxEl.enableDisplayMode();
5106 textboxEl.addKeyListener([10,13], function(){
5107 if(dlg.isVisible() && opt && opt.buttons){
5110 }else if(opt.buttons.yes){
5111 handleButton("yes");
5115 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5116 textareaEl.enableDisplayMode();
5117 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5118 progressEl.enableDisplayMode();
5120 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5121 var pf = progressEl.dom.firstChild;
5123 pp = Roo.get(pf.firstChild);
5124 pp.setHeight(pf.offsetHeight);
5132 * Updates the message box body text
5133 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5134 * the XHTML-compliant non-breaking space character '&#160;')
5135 * @return {Roo.MessageBox} This message box
5137 updateText : function(text)
5139 if(!dlg.isVisible() && !opt.width){
5140 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5141 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5143 msgEl.innerHTML = text || ' ';
5145 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5146 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5148 Math.min(opt.width || cw , this.maxWidth),
5149 Math.max(opt.minWidth || this.minWidth, bwidth)
5152 activeTextEl.setWidth(w);
5154 if(dlg.isVisible()){
5155 dlg.fixedcenter = false;
5157 // to big, make it scroll. = But as usual stupid IE does not support
5160 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5161 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5162 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5164 bodyEl.dom.style.height = '';
5165 bodyEl.dom.style.overflowY = '';
5168 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5170 bodyEl.dom.style.overflowX = '';
5173 dlg.setContentSize(w, bodyEl.getHeight());
5174 if(dlg.isVisible()){
5175 dlg.fixedcenter = true;
5181 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5182 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5183 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5184 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5185 * @return {Roo.MessageBox} This message box
5187 updateProgress : function(value, text){
5189 this.updateText(text);
5192 if (pp) { // weird bug on my firefox - for some reason this is not defined
5193 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5194 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5200 * Returns true if the message box is currently displayed
5201 * @return {Boolean} True if the message box is visible, else false
5203 isVisible : function(){
5204 return dlg && dlg.isVisible();
5208 * Hides the message box if it is displayed
5211 if(this.isVisible()){
5217 * Displays a new message box, or reinitializes an existing message box, based on the config options
5218 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5219 * The following config object properties are supported:
5221 Property Type Description
5222 ---------- --------------- ------------------------------------------------------------------------------------
5223 animEl String/Element An id or Element from which the message box should animate as it opens and
5224 closes (defaults to undefined)
5225 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5226 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5227 closable Boolean False to hide the top-right close button (defaults to true). Note that
5228 progress and wait dialogs will ignore this property and always hide the
5229 close button as they can only be closed programmatically.
5230 cls String A custom CSS class to apply to the message box element
5231 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5232 displayed (defaults to 75)
5233 fn Function A callback function to execute after closing the dialog. The arguments to the
5234 function will be btn (the name of the button that was clicked, if applicable,
5235 e.g. "ok"), and text (the value of the active text field, if applicable).
5236 Progress and wait dialogs will ignore this option since they do not respond to
5237 user actions and can only be closed programmatically, so any required function
5238 should be called by the same code after it closes the dialog.
5239 icon String A CSS class that provides a background image to be used as an icon for
5240 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5241 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5242 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5243 modal Boolean False to allow user interaction with the page while the message box is
5244 displayed (defaults to true)
5245 msg String A string that will replace the existing message box body text (defaults
5246 to the XHTML-compliant non-breaking space character ' ')
5247 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5248 progress Boolean True to display a progress bar (defaults to false)
5249 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5250 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5251 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5252 title String The title text
5253 value String The string value to set into the active textbox element if displayed
5254 wait Boolean True to display a progress bar (defaults to false)
5255 width Number The width of the dialog in pixels
5262 msg: 'Please enter your address:',
5264 buttons: Roo.MessageBox.OKCANCEL,
5267 animEl: 'addAddressBtn'
5270 * @param {Object} config Configuration options
5271 * @return {Roo.MessageBox} This message box
5273 show : function(options)
5276 // this causes nightmares if you show one dialog after another
5277 // especially on callbacks..
5279 if(this.isVisible()){
5282 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5283 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5284 Roo.log("New Dialog Message:" + options.msg )
5285 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5286 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5289 var d = this.getDialog();
5291 d.setTitle(opt.title || " ");
5292 d.closeEl.setDisplayed(opt.closable !== false);
5293 activeTextEl = textboxEl;
5294 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5299 textareaEl.setHeight(typeof opt.multiline == "number" ?
5300 opt.multiline : this.defaultTextHeight);
5301 activeTextEl = textareaEl;
5310 progressEl.setDisplayed(opt.progress === true);
5312 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5314 this.updateProgress(0);
5315 activeTextEl.dom.value = opt.value || "";
5317 dlg.setDefaultButton(activeTextEl);
5319 var bs = opt.buttons;
5323 }else if(bs && bs.yes){
5324 db = buttons["yes"];
5326 dlg.setDefaultButton(db);
5328 bwidth = updateButtons(opt.buttons);
5329 this.updateText(opt.msg);
5331 d.el.addClass(opt.cls);
5333 d.proxyDrag = opt.proxyDrag === true;
5334 d.modal = opt.modal !== false;
5335 d.mask = opt.modal !== false ? mask : false;
5337 // force it to the end of the z-index stack so it gets a cursor in FF
5338 document.body.appendChild(dlg.el.dom);
5339 d.animateTarget = null;
5340 d.show(options.animEl);
5346 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5347 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5348 * and closing the message box when the process is complete.
5349 * @param {String} title The title bar text
5350 * @param {String} msg The message box body text
5351 * @return {Roo.MessageBox} This message box
5353 progress : function(title, msg){
5360 minWidth: this.minProgressWidth,
5367 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5368 * If a callback function is passed it will be called after the user clicks the button, and the
5369 * id of the button that was clicked will be passed as the only parameter to the callback
5370 * (could also be the top-right close button).
5371 * @param {String} title The title bar text
5372 * @param {String} msg The message box body text
5373 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5374 * @param {Object} scope (optional) The scope of the callback function
5375 * @return {Roo.MessageBox} This message box
5377 alert : function(title, msg, fn, scope)
5392 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5393 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5394 * You are responsible for closing the message box when the process is complete.
5395 * @param {String} msg The message box body text
5396 * @param {String} title (optional) The title bar text
5397 * @return {Roo.MessageBox} This message box
5399 wait : function(msg, title){
5410 waitTimer = Roo.TaskMgr.start({
5412 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5420 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5421 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5422 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @return {Roo.MessageBox} This message box
5429 confirm : function(title, msg, fn, scope){
5433 buttons: this.YESNO,
5442 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5443 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5444 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5445 * (could also be the top-right close button) and the text that was entered will be passed as the two
5446 * parameters to the callback.
5447 * @param {String} title The title bar text
5448 * @param {String} msg The message box body text
5449 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5450 * @param {Object} scope (optional) The scope of the callback function
5451 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5452 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5453 * @return {Roo.MessageBox} This message box
5455 prompt : function(title, msg, fn, scope, multiline){
5459 buttons: this.OKCANCEL,
5464 multiline: multiline,
5471 * Button config that displays a single OK button
5476 * Button config that displays Yes and No buttons
5479 YESNO : {yes:true, no:true},
5481 * Button config that displays OK and Cancel buttons
5484 OKCANCEL : {ok:true, cancel:true},
5486 * Button config that displays Yes, No and Cancel buttons
5489 YESNOCANCEL : {yes:true, no:true, cancel:true},
5492 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5495 defaultTextHeight : 75,
5497 * The maximum width in pixels of the message box (defaults to 600)
5502 * The minimum width in pixels of the message box (defaults to 100)
5507 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5508 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5511 minProgressWidth : 250,
5513 * An object containing the default button text strings that can be overriden for localized language support.
5514 * Supported properties are: ok, cancel, yes and no.
5515 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5528 * Shorthand for {@link Roo.MessageBox}
5530 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5531 Roo.Msg = Roo.Msg || Roo.MessageBox;
5540 * @class Roo.bootstrap.nav.Bar
5541 * @extends Roo.bootstrap.Component
5543 * Bootstrap Navbar class
5546 * Create a new Navbar
5547 * @param {Object} config The config object
5551 Roo.bootstrap.nav.Bar = function(config){
5552 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5556 * @event beforetoggle
5557 * Fire before toggle the menu
5558 * @param {Roo.EventObject} e
5560 "beforetoggle" : true
5564 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5573 getAutoCreate : function(){
5576 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5580 initEvents :function ()
5582 //Roo.log(this.el.select('.navbar-toggle',true));
5583 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5590 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5592 var size = this.el.getSize();
5593 this.maskEl.setSize(size.width, size.height);
5594 this.maskEl.enableDisplayMode("block");
5603 getChildContainer : function()
5605 if (this.el && this.el.select('.collapse').getCount()) {
5606 return this.el.select('.collapse',true).first();
5621 onToggle : function()
5624 if(this.fireEvent('beforetoggle', this) === false){
5627 var ce = this.el.select('.navbar-collapse',true).first();
5629 if (!ce.hasClass('show')) {
5639 * Expand the navbar pulldown
5641 expand : function ()
5644 var ce = this.el.select('.navbar-collapse',true).first();
5645 if (ce.hasClass('collapsing')) {
5648 ce.dom.style.height = '';
5650 ce.addClass('in'); // old...
5651 ce.removeClass('collapse');
5652 ce.addClass('show');
5653 var h = ce.getHeight();
5655 ce.removeClass('show');
5656 // at this point we should be able to see it..
5657 ce.addClass('collapsing');
5659 ce.setHeight(0); // resize it ...
5660 ce.on('transitionend', function() {
5661 //Roo.log('done transition');
5662 ce.removeClass('collapsing');
5663 ce.addClass('show');
5664 ce.removeClass('collapse');
5666 ce.dom.style.height = '';
5667 }, this, { single: true} );
5669 ce.dom.scrollTop = 0;
5672 * Collapse the navbar pulldown
5674 collapse : function()
5676 var ce = this.el.select('.navbar-collapse',true).first();
5678 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5679 // it's collapsed or collapsing..
5682 ce.removeClass('in'); // old...
5683 ce.setHeight(ce.getHeight());
5684 ce.removeClass('show');
5685 ce.addClass('collapsing');
5687 ce.on('transitionend', function() {
5688 ce.dom.style.height = '';
5689 ce.removeClass('collapsing');
5690 ce.addClass('collapse');
5691 }, this, { single: true} );
5711 * @class Roo.bootstrap.nav.Simplebar
5712 * @extends Roo.bootstrap.nav.Bar
5713 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5714 * Bootstrap Sidebar class
5716 * @cfg {Boolean} inverse is inverted color
5718 * @cfg {String} type (nav | pills | tabs)
5719 * @cfg {Boolean} arrangement stacked | justified
5720 * @cfg {String} align (left | right) alignment
5722 * @cfg {Boolean} main (true|false) main nav bar? default false
5723 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5725 * @cfg {String} tag (header|footer|nav|div) default is nav
5727 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5731 * Create a new Sidebar
5732 * @param {Object} config The config object
5736 Roo.bootstrap.nav.Simplebar = function(config){
5737 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5740 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5756 getAutoCreate : function(){
5760 tag : this.tag || 'div',
5761 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5763 if (['light','white'].indexOf(this.weight) > -1) {
5764 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5766 cfg.cls += ' bg-' + this.weight;
5769 cfg.cls += ' navbar-inverse';
5773 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5775 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5784 cls: 'nav nav-' + this.xtype,
5790 this.type = this.type || 'nav';
5791 if (['tabs','pills'].indexOf(this.type) != -1) {
5792 cfg.cn[0].cls += ' nav-' + this.type
5796 if (this.type!=='nav') {
5797 Roo.log('nav type must be nav/tabs/pills')
5799 cfg.cn[0].cls += ' navbar-nav'
5805 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5806 cfg.cn[0].cls += ' nav-' + this.arrangement;
5810 if (this.align === 'right') {
5811 cfg.cn[0].cls += ' navbar-right';
5836 * navbar-expand-md fixed-top
5840 * @class Roo.bootstrap.nav.Headerbar
5841 * @extends Roo.bootstrap.nav.Simplebar
5842 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5843 * Bootstrap Sidebar class
5845 * @cfg {String} brand what is brand
5846 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5847 * @cfg {String} brand_href href of the brand
5848 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5849 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5850 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5851 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5854 * Create a new Sidebar
5855 * @param {Object} config The config object
5859 Roo.bootstrap.nav.Headerbar = function(config){
5860 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5864 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5871 desktopCenter : false,
5874 getAutoCreate : function(){
5877 tag: this.nav || 'nav',
5878 cls: 'navbar navbar-expand-md',
5884 if (this.desktopCenter) {
5885 cn.push({cls : 'container', cn : []});
5893 cls: 'navbar-toggle navbar-toggler',
5894 'data-toggle': 'collapse',
5899 html: 'Toggle navigation'
5903 cls: 'icon-bar navbar-toggler-icon'
5916 cn.push( Roo.bootstrap.version == 4 ? btn : {
5918 cls: 'navbar-header',
5927 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5931 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5933 if (['light','white'].indexOf(this.weight) > -1) {
5934 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5936 cfg.cls += ' bg-' + this.weight;
5939 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5940 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5942 // tag can override this..
5944 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5947 if (this.brand !== '') {
5948 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5949 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5951 href: this.brand_href ? this.brand_href : '#',
5952 cls: 'navbar-brand',
5960 cfg.cls += ' main-nav';
5968 getHeaderChildContainer : function()
5970 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5971 return this.el.select('.navbar-header',true).first();
5974 return this.getChildContainer();
5977 getChildContainer : function()
5980 return this.el.select('.roo-navbar-collapse',true).first();
5985 initEvents : function()
5987 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5989 if (this.autohide) {
5994 Roo.get(document).on('scroll',function(e) {
5995 var ns = Roo.get(document).getScroll().top;
5996 var os = prevScroll;
6000 ft.removeClass('slideDown');
6001 ft.addClass('slideUp');
6004 ft.removeClass('slideUp');
6005 ft.addClass('slideDown');
6026 * @class Roo.bootstrap.nav.Sidebar
6027 * @extends Roo.bootstrap.nav.Bar
6028 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6029 * Bootstrap Sidebar class
6032 * Create a new Sidebar
6033 * @param {Object} config The config object
6037 Roo.bootstrap.nav.Sidebar = function(config){
6038 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6041 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6043 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6045 getAutoCreate : function(){
6050 cls: 'sidebar sidebar-nav'
6072 * @class Roo.bootstrap.nav.Group
6073 * @extends Roo.bootstrap.Component
6074 * @children Roo.bootstrap.nav.Item
6075 * Bootstrap NavGroup class
6076 * @cfg {String} align (left|right)
6077 * @cfg {Boolean} inverse
6078 * @cfg {String} type (nav|pills|tab) default nav
6079 * @cfg {String} navId - reference Id for navbar.
6080 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6083 * Create a new nav group
6084 * @param {Object} config The config object
6087 Roo.bootstrap.nav.Group = function(config){
6088 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6091 Roo.bootstrap.nav.Group.register(this);
6095 * Fires when the active item changes
6096 * @param {Roo.bootstrap.nav.Group} this
6097 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6098 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6105 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6117 getAutoCreate : function()
6119 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6125 if (Roo.bootstrap.version == 4) {
6126 if (['tabs','pills'].indexOf(this.type) != -1) {
6127 cfg.cls += ' nav-' + this.type;
6129 // trying to remove so header bar can right align top?
6130 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6131 // do not use on header bar...
6132 cfg.cls += ' navbar-nav';
6137 if (['tabs','pills'].indexOf(this.type) != -1) {
6138 cfg.cls += ' nav-' + this.type
6140 if (this.type !== 'nav') {
6141 Roo.log('nav type must be nav/tabs/pills')
6143 cfg.cls += ' navbar-nav'
6147 if (this.parent() && this.parent().sidebar) {
6150 cls: 'dashboard-menu sidebar-menu'
6156 if (this.form === true) {
6159 cls: 'navbar-form form-inline'
6161 //nav navbar-right ml-md-auto
6162 if (this.align === 'right') {
6163 cfg.cls += ' navbar-right ml-md-auto';
6165 cfg.cls += ' navbar-left';
6169 if (this.align === 'right') {
6170 cfg.cls += ' navbar-right ml-md-auto';
6172 cfg.cls += ' mr-auto';
6176 cfg.cls += ' navbar-inverse';
6184 * sets the active Navigation item
6185 * @param {Roo.bootstrap.nav.Item} the new current navitem
6187 setActiveItem : function(item)
6190 Roo.each(this.navItems, function(v){
6195 v.setActive(false, true);
6202 item.setActive(true, true);
6203 this.fireEvent('changed', this, item, prev);
6208 * gets the active Navigation item
6209 * @return {Roo.bootstrap.nav.Item} the current navitem
6211 getActive : function()
6215 Roo.each(this.navItems, function(v){
6226 indexOfNav : function()
6230 Roo.each(this.navItems, function(v,i){
6241 * adds a Navigation item
6242 * @param {Roo.bootstrap.nav.Item} the navitem to add
6244 addItem : function(cfg)
6246 if (this.form && Roo.bootstrap.version == 4) {
6249 var cn = new Roo.bootstrap.nav.Item(cfg);
6251 cn.parentId = this.id;
6252 cn.onRender(this.el, null);
6256 * register a Navigation item
6257 * @param {Roo.bootstrap.nav.Item} the navitem to add
6259 register : function(item)
6261 this.navItems.push( item);
6262 item.navId = this.navId;
6267 * clear all the Navigation item
6270 clearAll : function()
6273 this.el.dom.innerHTML = '';
6276 getNavItem: function(tabId)
6279 Roo.each(this.navItems, function(e) {
6280 if (e.tabId == tabId) {
6290 setActiveNext : function()
6292 var i = this.indexOfNav(this.getActive());
6293 if (i > this.navItems.length) {
6296 this.setActiveItem(this.navItems[i+1]);
6298 setActivePrev : function()
6300 var i = this.indexOfNav(this.getActive());
6304 this.setActiveItem(this.navItems[i-1]);
6306 clearWasActive : function(except) {
6307 Roo.each(this.navItems, function(e) {
6308 if (e.tabId != except.tabId && e.was_active) {
6309 e.was_active = false;
6316 getWasActive : function ()
6319 Roo.each(this.navItems, function(e) {
6334 Roo.apply(Roo.bootstrap.nav.Group, {
6338 * register a Navigation Group
6339 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6341 register : function(navgrp)
6343 this.groups[navgrp.navId] = navgrp;
6347 * fetch a Navigation Group based on the navigation ID
6348 * @param {string} the navgroup to add
6349 * @returns {Roo.bootstrap.nav.Group} the navgroup
6351 get: function(navId) {
6352 if (typeof(this.groups[navId]) == 'undefined') {
6354 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6356 return this.groups[navId] ;
6364 * @class Roo.bootstrap.nav.Item
6365 * @extends Roo.bootstrap.Component
6366 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6367 * @parent Roo.bootstrap.nav.Group
6369 * Bootstrap Navbar.NavItem class
6371 * @cfg {String} href link to
6372 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6373 * @cfg {Boolean} button_outline show and outlined button
6374 * @cfg {String} html content of button
6375 * @cfg {String} badge text inside badge
6376 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6377 * @cfg {String} glyphicon DEPRICATED - use fa
6378 * @cfg {String} icon DEPRICATED - use fa
6379 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6380 * @cfg {Boolean} active Is item active
6381 * @cfg {Boolean} disabled Is item disabled
6382 * @cfg {String} linkcls Link Class
6383 * @cfg {Boolean} preventDefault (true | false) default false
6384 * @cfg {String} tabId the tab that this item activates.
6385 * @cfg {String} tagtype (a|span) render as a href or span?
6386 * @cfg {Boolean} animateRef (true|false) link to element default false
6387 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6390 * Create a new Navbar Item
6391 * @param {Object} config The config object
6393 Roo.bootstrap.nav.Item = function(config){
6394 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6399 * The raw click event for the entire grid.
6400 * @param {Roo.EventObject} e
6405 * Fires when the active item active state changes
6406 * @param {Roo.bootstrap.nav.Item} this
6407 * @param {boolean} state the new state
6413 * Fires when scroll to element
6414 * @param {Roo.bootstrap.nav.Item} this
6415 * @param {Object} options
6416 * @param {Roo.EventObject} e
6424 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6433 preventDefault : false,
6441 button_outline : false,
6445 getAutoCreate : function(){
6452 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6455 cfg.cls += ' active' ;
6457 if (this.disabled) {
6458 cfg.cls += ' disabled';
6462 if (this.button_weight.length) {
6463 cfg.tag = this.href ? 'a' : 'button';
6464 cfg.html = this.html || '';
6465 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6467 cfg.href = this.href;
6470 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6472 cfg.cls += " nav-html";
6475 // menu .. should add dropdown-menu class - so no need for carat..
6477 if (this.badge !== '') {
6479 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6484 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6488 href : this.href || "#",
6489 html: this.html || '',
6493 if (this.tagtype == 'a') {
6494 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6498 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6499 } else if (this.fa) {
6500 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6501 } else if(this.glyphicon) {
6502 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6504 cfg.cn[0].cls += " nav-html";
6508 cfg.cn[0].html += " <span class='caret'></span>";
6512 if (this.badge !== '') {
6513 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6521 onRender : function(ct, position)
6523 // Roo.log("Call onRender: " + this.xtype);
6524 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6528 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6529 this.navLink = this.el.select('.nav-link',true).first();
6530 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6535 initEvents: function()
6537 if (typeof (this.menu) != 'undefined') {
6538 this.menu.parentType = this.xtype;
6539 this.menu.triggerEl = this.el;
6540 this.menu = this.addxtype(Roo.apply({}, this.menu));
6543 this.el.on('click', this.onClick, this);
6545 //if(this.tagtype == 'span'){
6546 // this.el.select('span',true).on('click', this.onClick, this);
6549 // at this point parent should be available..
6550 this.parent().register(this);
6553 onClick : function(e)
6555 if (e.getTarget('.dropdown-menu-item')) {
6556 // did you click on a menu itemm.... - then don't trigger onclick..
6561 this.preventDefault ||
6562 this.href === false ||
6565 //Roo.log("NavItem - prevent Default?");
6569 if (this.disabled) {
6573 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6574 if (tg && tg.transition) {
6575 Roo.log("waiting for the transitionend");
6581 //Roo.log("fire event clicked");
6582 if(this.fireEvent('click', this, e) === false){
6586 if(this.tagtype == 'span'){
6590 //Roo.log(this.href);
6591 var ael = this.el.select('a',true).first();
6594 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6595 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6596 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6597 return; // ignore... - it's a 'hash' to another page.
6599 Roo.log("NavItem - prevent Default?");
6601 this.scrollToElement(e);
6605 var p = this.parent();
6607 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6608 if (typeof(p.setActiveItem) !== 'undefined') {
6609 p.setActiveItem(this);
6613 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6614 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6615 // remove the collapsed menu expand...
6616 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6620 isActive: function () {
6623 setActive : function(state, fire, is_was_active)
6625 if (this.active && !state && this.navId) {
6626 this.was_active = true;
6627 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6629 nv.clearWasActive(this);
6633 this.active = state;
6636 this.el.removeClass('active');
6637 this.navLink ? this.navLink.removeClass('active') : false;
6638 } else if (!this.el.hasClass('active')) {
6640 this.el.addClass('active');
6641 if (Roo.bootstrap.version == 4 && this.navLink ) {
6642 this.navLink.addClass('active');
6647 this.fireEvent('changed', this, state);
6650 // show a panel if it's registered and related..
6652 if (!this.navId || !this.tabId || !state || is_was_active) {
6656 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6660 var pan = tg.getPanelByName(this.tabId);
6664 // if we can not flip to new panel - go back to old nav highlight..
6665 if (false == tg.showPanel(pan)) {
6666 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6668 var onav = nv.getWasActive();
6670 onav.setActive(true, false, true);
6679 // this should not be here...
6680 setDisabled : function(state)
6682 this.disabled = state;
6684 this.el.removeClass('disabled');
6685 } else if (!this.el.hasClass('disabled')) {
6686 this.el.addClass('disabled');
6692 * Fetch the element to display the tooltip on.
6693 * @return {Roo.Element} defaults to this.el
6695 tooltipEl : function()
6697 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6700 scrollToElement : function(e)
6702 var c = document.body;
6705 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6707 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6708 c = document.documentElement;
6711 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6717 var o = target.calcOffsetsTo(c);
6724 this.fireEvent('scrollto', this, options, e);
6726 Roo.get(c).scrollTo('top', options.value, true);
6731 * Set the HTML (text content) of the item
6732 * @param {string} html content for the nav item
6734 setHtml : function(html)
6737 this.htmlEl.dom.innerHTML = html;
6749 * <span> icon </span>
6750 * <span> text </span>
6751 * <span>badge </span>
6755 * @class Roo.bootstrap.nav.SidebarItem
6756 * @extends Roo.bootstrap.nav.Item
6757 * Bootstrap Navbar.NavSidebarItem class
6759 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6760 * {Boolean} open is the menu open
6761 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6762 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6763 * {String} buttonSize (sm|md|lg)the extra classes for the button
6764 * {Boolean} showArrow show arrow next to the text (default true)
6766 * Create a new Navbar Button
6767 * @param {Object} config The config object
6769 Roo.bootstrap.nav.SidebarItem = function(config){
6770 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6775 * The raw click event for the entire grid.
6776 * @param {Roo.EventObject} e
6781 * Fires when the active item active state changes
6782 * @param {Roo.bootstrap.nav.SidebarItem} this
6783 * @param {boolean} state the new state
6791 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6793 badgeWeight : 'default',
6799 buttonWeight : 'default',
6805 getAutoCreate : function(){
6810 href : this.href || '#',
6816 if(this.buttonView){
6819 href : this.href || '#',
6820 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6833 cfg.cls += ' active';
6836 if (this.disabled) {
6837 cfg.cls += ' disabled';
6840 cfg.cls += ' open x-open';
6843 if (this.glyphicon || this.icon) {
6844 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6845 a.cn.push({ tag : 'i', cls : c }) ;
6848 if(!this.buttonView){
6851 html : this.html || ''
6858 if (this.badge !== '') {
6859 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6865 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6868 a.cls += ' dropdown-toggle treeview' ;
6874 initEvents : function()
6876 if (typeof (this.menu) != 'undefined') {
6877 this.menu.parentType = this.xtype;
6878 this.menu.triggerEl = this.el;
6879 this.menu = this.addxtype(Roo.apply({}, this.menu));
6882 this.el.on('click', this.onClick, this);
6884 if(this.badge !== ''){
6885 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6890 onClick : function(e)
6897 if(this.preventDefault){
6901 this.fireEvent('click', this, e);
6904 disable : function()
6906 this.setDisabled(true);
6911 this.setDisabled(false);
6914 setDisabled : function(state)
6916 if(this.disabled == state){
6920 this.disabled = state;
6923 this.el.addClass('disabled');
6927 this.el.removeClass('disabled');
6932 setActive : function(state)
6934 if(this.active == state){
6938 this.active = state;
6941 this.el.addClass('active');
6945 this.el.removeClass('active');
6950 isActive: function ()
6955 setBadge : function(str)
6961 this.badgeEl.dom.innerHTML = str;
6978 * @class Roo.bootstrap.nav.ProgressBar
6979 * @extends Roo.bootstrap.Component
6980 * @children Roo.bootstrap.nav.ProgressBarItem
6981 * Bootstrap NavProgressBar class
6984 * Create a new nav progress bar - a bar indicating step along a process
6985 * @param {Object} config The config object
6988 Roo.bootstrap.nav.ProgressBar = function(config){
6989 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6991 this.bullets = this.bullets || [];
6993 // Roo.bootstrap.nav.ProgressBar.register(this);
6997 * Fires when the active item changes
6998 * @param {Roo.bootstrap.nav.ProgressBar} this
6999 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7000 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7007 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7009 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7010 * Bullets for the Nav Progress bar for the toolbar
7015 getAutoCreate : function()
7017 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7021 cls : 'roo-navigation-bar-group',
7025 cls : 'roo-navigation-top-bar'
7029 cls : 'roo-navigation-bullets-bar',
7033 cls : 'roo-navigation-bar'
7040 cls : 'roo-navigation-bottom-bar'
7050 initEvents: function()
7055 onRender : function(ct, position)
7057 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7059 if(this.bullets.length){
7060 Roo.each(this.bullets, function(b){
7069 addItem : function(cfg)
7071 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7073 item.parentId = this.id;
7074 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7077 var top = new Roo.bootstrap.Element({
7079 cls : 'roo-navigation-bar-text'
7082 var bottom = new Roo.bootstrap.Element({
7084 cls : 'roo-navigation-bar-text'
7087 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7088 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7090 var topText = new Roo.bootstrap.Element({
7092 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7095 var bottomText = new Roo.bootstrap.Element({
7097 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7100 topText.onRender(top.el, null);
7101 bottomText.onRender(bottom.el, null);
7104 item.bottomEl = bottom;
7107 this.barItems.push(item);
7112 getActive : function()
7116 Roo.each(this.barItems, function(v){
7118 if (!v.isActive()) {
7130 setActiveItem : function(item)
7134 Roo.each(this.barItems, function(v){
7135 if (v.rid == item.rid) {
7145 item.setActive(true);
7147 this.fireEvent('changed', this, item, prev);
7150 getBarItem: function(rid)
7154 Roo.each(this.barItems, function(e) {
7166 indexOfItem : function(item)
7170 Roo.each(this.barItems, function(v, i){
7172 if (v.rid != item.rid) {
7183 setActiveNext : function()
7185 var i = this.indexOfItem(this.getActive());
7187 if (i > this.barItems.length) {
7191 this.setActiveItem(this.barItems[i+1]);
7194 setActivePrev : function()
7196 var i = this.indexOfItem(this.getActive());
7202 this.setActiveItem(this.barItems[i-1]);
7207 if(!this.barItems.length){
7211 var width = 100 / this.barItems.length;
7213 Roo.each(this.barItems, function(i){
7214 i.el.setStyle('width', width + '%');
7215 i.topEl.el.setStyle('width', width + '%');
7216 i.bottomEl.el.setStyle('width', width + '%');
7230 * @class Roo.bootstrap.nav.ProgressBarItem
7231 * @extends Roo.bootstrap.Component
7232 * Bootstrap NavProgressBarItem class
7233 * @cfg {String} rid the reference id
7234 * @cfg {Boolean} active (true|false) Is item active default false
7235 * @cfg {Boolean} disabled (true|false) Is item active default false
7236 * @cfg {String} html
7237 * @cfg {String} position (top|bottom) text position default bottom
7238 * @cfg {String} icon show icon instead of number
7241 * Create a new NavProgressBarItem
7242 * @param {Object} config The config object
7244 Roo.bootstrap.nav.ProgressBarItem = function(config){
7245 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7250 * The raw click event for the entire grid.
7251 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7252 * @param {Roo.EventObject} e
7259 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7265 position : 'bottom',
7268 getAutoCreate : function()
7270 var iconCls = 'roo-navigation-bar-item-icon';
7272 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7276 cls: 'roo-navigation-bar-item',
7286 cfg.cls += ' active';
7289 cfg.cls += ' disabled';
7295 disable : function()
7297 this.setDisabled(true);
7302 this.setDisabled(false);
7305 initEvents: function()
7307 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7309 this.iconEl.on('click', this.onClick, this);
7312 onClick : function(e)
7320 if(this.fireEvent('click', this, e) === false){
7324 this.parent().setActiveItem(this);
7327 isActive: function ()
7332 setActive : function(state)
7334 if(this.active == state){
7338 this.active = state;
7341 this.el.addClass('active');
7345 this.el.removeClass('active');
7350 setDisabled : function(state)
7352 if(this.disabled == state){
7356 this.disabled = state;
7359 this.el.addClass('disabled');
7363 this.el.removeClass('disabled');
7366 tooltipEl : function()
7368 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7379 Roo.namespace('Roo.bootstrap.breadcrumb');
7383 * @class Roo.bootstrap.breadcrumb.Nav
7384 * @extends Roo.bootstrap.Component
7385 * Bootstrap Breadcrumb Nav Class
7387 * @children Roo.bootstrap.breadcrumb.Item
7390 * Create a new breadcrumb.Nav
7391 * @param {Object} config The config object
7395 Roo.bootstrap.breadcrumb.Nav = function(config){
7396 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7401 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7403 getAutoCreate : function()
7420 initEvents: function()
7422 this.olEl = this.el.select('ol',true).first();
7424 getChildContainer : function()
7440 * @class Roo.bootstrap.breadcrumb.Nav
7441 * @extends Roo.bootstrap.Component
7442 * @children Roo.bootstrap.Component
7443 * @parent Roo.bootstrap.breadcrumb.Nav
7444 * Bootstrap Breadcrumb Nav Class
7447 * @cfg {String} html the content of the link.
7448 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7449 * @cfg {Boolean} active is it active
7453 * Create a new breadcrumb.Nav
7454 * @param {Object} config The config object
7457 Roo.bootstrap.breadcrumb.Item = function(config){
7458 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7463 * The img click event for the img.
7464 * @param {Roo.EventObject} e
7471 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7476 getAutoCreate : function()
7481 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7483 if (this.href !== false) {
7490 cfg.html = this.html;
7496 initEvents: function()
7499 this.el.select('a', true).first().on('click',this.onClick, this)
7503 onClick : function(e)
7506 this.fireEvent('click',this, e);
7519 * @class Roo.bootstrap.Row
7520 * @extends Roo.bootstrap.Component
7521 * @children Roo.bootstrap.Component
7522 * Bootstrap Row class (contains columns...)
7526 * @param {Object} config The config object
7529 Roo.bootstrap.Row = function(config){
7530 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7535 getAutoCreate : function(){
7554 * @class Roo.bootstrap.Pagination
7555 * @extends Roo.bootstrap.Component
7556 * @children Roo.bootstrap.Pagination
7557 * Bootstrap Pagination class
7559 * @cfg {String} size (xs|sm|md|lg|xl)
7560 * @cfg {Boolean} inverse
7563 * Create a new Pagination
7564 * @param {Object} config The config object
7567 Roo.bootstrap.Pagination = function(config){
7568 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7571 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7577 getAutoCreate : function(){
7583 cfg.cls += ' inverse';
7589 cfg.cls += " " + this.cls;
7607 * @class Roo.bootstrap.PaginationItem
7608 * @extends Roo.bootstrap.Component
7609 * Bootstrap PaginationItem class
7610 * @cfg {String} html text
7611 * @cfg {String} href the link
7612 * @cfg {Boolean} preventDefault (true | false) default true
7613 * @cfg {Boolean} active (true | false) default false
7614 * @cfg {Boolean} disabled default false
7618 * Create a new PaginationItem
7619 * @param {Object} config The config object
7623 Roo.bootstrap.PaginationItem = function(config){
7624 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7629 * The raw click event for the entire grid.
7630 * @param {Roo.EventObject} e
7636 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7640 preventDefault: true,
7645 getAutoCreate : function(){
7651 href : this.href ? this.href : '#',
7652 html : this.html ? this.html : ''
7662 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7666 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7672 initEvents: function() {
7674 this.el.on('click', this.onClick, this);
7677 onClick : function(e)
7679 Roo.log('PaginationItem on click ');
7680 if(this.preventDefault){
7688 this.fireEvent('click', this, e);
7704 * @class Roo.bootstrap.Slider
7705 * @extends Roo.bootstrap.Component
7706 * Bootstrap Slider class
7709 * Create a new Slider
7710 * @param {Object} config The config object
7713 Roo.bootstrap.Slider = function(config){
7714 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7717 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7719 getAutoCreate : function(){
7723 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7727 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7739 * Ext JS Library 1.1.1
7740 * Copyright(c) 2006-2007, Ext JS, LLC.
7742 * Originally Released Under LGPL - original licence link has changed is not relivant.
7745 * <script type="text/javascript">
7748 * @extends Roo.dd.DDProxy
7749 * @class Roo.grid.SplitDragZone
7750 * Support for Column Header resizing
7752 * @param {Object} config
7755 // This is a support class used internally by the Grid components
7756 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7758 this.view = grid.getView();
7759 this.proxy = this.view.resizeProxy;
7760 Roo.grid.SplitDragZone.superclass.constructor.call(
7763 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7765 dragElId : Roo.id(this.proxy.dom),
7770 this.setHandleElId(Roo.id(hd));
7771 if (hd2 !== false) {
7772 this.setOuterHandleElId(Roo.id(hd2));
7775 this.scroll = false;
7777 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7778 fly: Roo.Element.fly,
7780 b4StartDrag : function(x, y){
7781 this.view.headersDisabled = true;
7782 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7783 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7785 this.proxy.setHeight(h);
7787 // for old system colWidth really stored the actual width?
7788 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7789 // which in reality did not work.. - it worked only for fixed sizes
7790 // for resizable we need to use actual sizes.
7791 var w = this.cm.getColumnWidth(this.cellIndex);
7792 if (!this.view.mainWrap) {
7794 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7799 // this was w-this.grid.minColumnWidth;
7800 // doesnt really make sense? - w = thie curren width or the rendered one?
7801 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7802 this.resetConstraints();
7803 this.setXConstraint(minw, 1000);
7804 this.setYConstraint(0, 0);
7805 this.minX = x - minw;
7806 this.maxX = x + 1000;
7808 if (!this.view.mainWrap) { // this is Bootstrap code..
7809 this.getDragEl().style.display='block';
7812 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7816 handleMouseDown : function(e){
7817 ev = Roo.EventObject.setEvent(e);
7818 var t = this.fly(ev.getTarget());
7819 if(t.hasClass("x-grid-split")){
7820 this.cellIndex = this.view.getCellIndex(t.dom);
7822 this.cm = this.grid.colModel;
7823 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7824 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7829 endDrag : function(e){
7830 this.view.headersDisabled = false;
7831 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7832 var diff = endX - this.startPos;
7834 var w = this.cm.getColumnWidth(this.cellIndex);
7835 if (!this.view.mainWrap) {
7838 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7841 autoOffset : function(){
7846 * Ext JS Library 1.1.1
7847 * Copyright(c) 2006-2007, Ext JS, LLC.
7849 * Originally Released Under LGPL - original licence link has changed is not relivant.
7852 * <script type="text/javascript">
7856 * @class Roo.grid.AbstractSelectionModel
7857 * @extends Roo.util.Observable
7859 * Abstract base class for grid SelectionModels. It provides the interface that should be
7860 * implemented by descendant classes. This class should not be directly instantiated.
7863 Roo.grid.AbstractSelectionModel = function(){
7864 this.locked = false;
7865 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7868 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7869 /** @ignore Called by the grid automatically. Do not call directly. */
7870 init : function(grid){
7876 * Locks the selections.
7883 * Unlocks the selections.
7885 unlock : function(){
7886 this.locked = false;
7890 * Returns true if the selections are locked.
7893 isLocked : function(){
7898 * Ext JS Library 1.1.1
7899 * Copyright(c) 2006-2007, Ext JS, LLC.
7901 * Originally Released Under LGPL - original licence link has changed is not relivant.
7904 * <script type="text/javascript">
7907 * @extends Roo.grid.AbstractSelectionModel
7908 * @class Roo.grid.RowSelectionModel
7909 * The default SelectionModel used by {@link Roo.grid.Grid}.
7910 * It supports multiple selections and keyboard selection/navigation.
7912 * @param {Object} config
7914 Roo.grid.RowSelectionModel = function(config){
7915 Roo.apply(this, config);
7916 this.selections = new Roo.util.MixedCollection(false, function(o){
7921 this.lastActive = false;
7925 * @event selectionchange
7926 * Fires when the selection changes
7927 * @param {SelectionModel} this
7929 "selectionchange" : true,
7931 * @event afterselectionchange
7932 * Fires after the selection changes (eg. by key press or clicking)
7933 * @param {SelectionModel} this
7935 "afterselectionchange" : true,
7937 * @event beforerowselect
7938 * Fires when a row is selected being selected, return false to cancel.
7939 * @param {SelectionModel} this
7940 * @param {Number} rowIndex The selected index
7941 * @param {Boolean} keepExisting False if other selections will be cleared
7943 "beforerowselect" : true,
7946 * Fires when a row is selected.
7947 * @param {SelectionModel} this
7948 * @param {Number} rowIndex The selected index
7949 * @param {Roo.data.Record} r The record
7953 * @event rowdeselect
7954 * Fires when a row is deselected.
7955 * @param {SelectionModel} this
7956 * @param {Number} rowIndex The selected index
7958 "rowdeselect" : true
7960 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7961 this.locked = false;
7964 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7966 * @cfg {Boolean} singleSelect
7967 * True to allow selection of only one row at a time (defaults to false)
7969 singleSelect : false,
7972 initEvents : function(){
7974 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7975 this.grid.on("mousedown", this.handleMouseDown, this);
7976 }else{ // allow click to work like normal
7977 this.grid.on("rowclick", this.handleDragableRowClick, this);
7979 // bootstrap does not have a view..
7980 var view = this.grid.view ? this.grid.view : this.grid;
7981 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7984 this.selectPrevious(e.shiftKey);
7985 }else if(this.last !== false && this.lastActive !== false){
7986 var last = this.last;
7987 this.selectRange(this.last, this.lastActive-1);
7988 view.focusRow(this.lastActive);
7993 this.selectFirstRow();
7995 this.fireEvent("afterselectionchange", this);
7997 "down" : function(e){
7999 this.selectNext(e.shiftKey);
8000 }else if(this.last !== false && this.lastActive !== false){
8001 var last = this.last;
8002 this.selectRange(this.last, this.lastActive+1);
8003 view.focusRow(this.lastActive);
8008 this.selectFirstRow();
8010 this.fireEvent("afterselectionchange", this);
8016 view.on("refresh", this.onRefresh, this);
8017 view.on("rowupdated", this.onRowUpdated, this);
8018 view.on("rowremoved", this.onRemove, this);
8022 onRefresh : function(){
8023 var ds = this.grid.ds, i, v = this.grid.view;
8024 var s = this.selections;
8026 if((i = ds.indexOfId(r.id)) != -1){
8028 s.add(ds.getAt(i)); // updating the selection relate data
8036 onRemove : function(v, index, r){
8037 this.selections.remove(r);
8041 onRowUpdated : function(v, index, r){
8042 if(this.isSelected(r)){
8043 v.onRowSelect(index);
8049 * @param {Array} records The records to select
8050 * @param {Boolean} keepExisting (optional) True to keep existing selections
8052 selectRecords : function(records, keepExisting){
8054 this.clearSelections();
8056 var ds = this.grid.ds;
8057 for(var i = 0, len = records.length; i < len; i++){
8058 this.selectRow(ds.indexOf(records[i]), true);
8063 * Gets the number of selected rows.
8066 getCount : function(){
8067 return this.selections.length;
8071 * Selects the first row in the grid.
8073 selectFirstRow : function(){
8078 * Select the last row.
8079 * @param {Boolean} keepExisting (optional) True to keep existing selections
8081 selectLastRow : function(keepExisting){
8082 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8086 * Selects the row immediately following the last selected row.
8087 * @param {Boolean} keepExisting (optional) True to keep existing selections
8089 selectNext : function(keepExisting){
8090 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8091 this.selectRow(this.last+1, keepExisting);
8092 var view = this.grid.view ? this.grid.view : this.grid;
8093 view.focusRow(this.last);
8098 * Selects the row that precedes the last selected row.
8099 * @param {Boolean} keepExisting (optional) True to keep existing selections
8101 selectPrevious : function(keepExisting){
8103 this.selectRow(this.last-1, keepExisting);
8104 var view = this.grid.view ? this.grid.view : this.grid;
8105 view.focusRow(this.last);
8110 * Returns the selected records
8111 * @return {Array} Array of selected records
8113 getSelections : function(){
8114 return [].concat(this.selections.items);
8118 * Returns the first selected record.
8121 getSelected : function(){
8122 return this.selections.itemAt(0);
8127 * Clears all selections.
8129 clearSelections : function(fast){
8134 var ds = this.grid.ds;
8135 var s = this.selections;
8137 this.deselectRow(ds.indexOfId(r.id));
8141 this.selections.clear();
8150 selectAll : function(){
8154 this.selections.clear();
8155 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8156 this.selectRow(i, true);
8161 * Returns True if there is a selection.
8164 hasSelection : function(){
8165 return this.selections.length > 0;
8169 * Returns True if the specified row is selected.
8170 * @param {Number/Record} record The record or index of the record to check
8173 isSelected : function(index){
8174 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8175 return (r && this.selections.key(r.id) ? true : false);
8179 * Returns True if the specified record id is selected.
8180 * @param {String} id The id of record to check
8183 isIdSelected : function(id){
8184 return (this.selections.key(id) ? true : false);
8188 handleMouseDown : function(e, t)
8190 var view = this.grid.view ? this.grid.view : this.grid;
8192 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8195 if(e.shiftKey && this.last !== false){
8196 var last = this.last;
8197 this.selectRange(last, rowIndex, e.ctrlKey);
8198 this.last = last; // reset the last
8199 view.focusRow(rowIndex);
8201 var isSelected = this.isSelected(rowIndex);
8202 if(e.button !== 0 && isSelected){
8203 view.focusRow(rowIndex);
8204 }else if(e.ctrlKey && isSelected){
8205 this.deselectRow(rowIndex);
8206 }else if(!isSelected){
8207 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8208 view.focusRow(rowIndex);
8211 this.fireEvent("afterselectionchange", this);
8214 handleDragableRowClick : function(grid, rowIndex, e)
8216 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8217 this.selectRow(rowIndex, false);
8218 var view = this.grid.view ? this.grid.view : this.grid;
8219 view.focusRow(rowIndex);
8220 this.fireEvent("afterselectionchange", this);
8225 * Selects multiple rows.
8226 * @param {Array} rows Array of the indexes of the row to select
8227 * @param {Boolean} keepExisting (optional) True to keep existing selections
8229 selectRows : function(rows, keepExisting){
8231 this.clearSelections();
8233 for(var i = 0, len = rows.length; i < len; i++){
8234 this.selectRow(rows[i], true);
8239 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8240 * @param {Number} startRow The index of the first row in the range
8241 * @param {Number} endRow The index of the last row in the range
8242 * @param {Boolean} keepExisting (optional) True to retain existing selections
8244 selectRange : function(startRow, endRow, keepExisting){
8249 this.clearSelections();
8251 if(startRow <= endRow){
8252 for(var i = startRow; i <= endRow; i++){
8253 this.selectRow(i, true);
8256 for(var i = startRow; i >= endRow; i--){
8257 this.selectRow(i, true);
8263 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8264 * @param {Number} startRow The index of the first row in the range
8265 * @param {Number} endRow The index of the last row in the range
8267 deselectRange : function(startRow, endRow, preventViewNotify){
8271 for(var i = startRow; i <= endRow; i++){
8272 this.deselectRow(i, preventViewNotify);
8278 * @param {Number} row The index of the row to select
8279 * @param {Boolean} keepExisting (optional) True to keep existing selections
8281 selectRow : function(index, keepExisting, preventViewNotify){
8282 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8285 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8286 if(!keepExisting || this.singleSelect){
8287 this.clearSelections();
8289 var r = this.grid.ds.getAt(index);
8290 this.selections.add(r);
8291 this.last = this.lastActive = index;
8292 if(!preventViewNotify){
8293 var view = this.grid.view ? this.grid.view : this.grid;
8294 view.onRowSelect(index);
8296 this.fireEvent("rowselect", this, index, r);
8297 this.fireEvent("selectionchange", this);
8303 * @param {Number} row The index of the row to deselect
8305 deselectRow : function(index, preventViewNotify){
8309 if(this.last == index){
8312 if(this.lastActive == index){
8313 this.lastActive = false;
8315 var r = this.grid.ds.getAt(index);
8316 this.selections.remove(r);
8317 if(!preventViewNotify){
8318 var view = this.grid.view ? this.grid.view : this.grid;
8319 view.onRowDeselect(index);
8321 this.fireEvent("rowdeselect", this, index);
8322 this.fireEvent("selectionchange", this);
8326 restoreLast : function(){
8328 this.last = this._last;
8333 acceptsNav : function(row, col, cm){
8334 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8338 onEditorKey : function(field, e){
8339 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8344 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8346 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8348 }else if(k == e.ENTER && !e.ctrlKey){
8352 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8354 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8356 }else if(k == e.ESC){
8360 g.startEditing(newCell[0], newCell[1]);
8365 * Ext JS Library 1.1.1
8366 * Copyright(c) 2006-2007, Ext JS, LLC.
8368 * Originally Released Under LGPL - original licence link has changed is not relivant.
8371 * <script type="text/javascript">
8376 * @class Roo.grid.ColumnModel
8377 * @extends Roo.util.Observable
8378 * This is the default implementation of a ColumnModel used by the Grid. It defines
8379 * the columns in the grid.
8382 var colModel = new Roo.grid.ColumnModel([
8383 {header: "Ticker", width: 60, sortable: true, locked: true},
8384 {header: "Company Name", width: 150, sortable: true},
8385 {header: "Market Cap.", width: 100, sortable: true},
8386 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8387 {header: "Employees", width: 100, sortable: true, resizable: false}
8392 * The config options listed for this class are options which may appear in each
8393 * individual column definition.
8394 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8396 * @param {Object} config An Array of column config objects. See this class's
8397 * config objects for details.
8399 Roo.grid.ColumnModel = function(config){
8401 * The config passed into the constructor
8403 this.config = []; //config;
8406 // if no id, create one
8407 // if the column does not have a dataIndex mapping,
8408 // map it to the order it is in the config
8409 for(var i = 0, len = config.length; i < len; i++){
8410 this.addColumn(config[i]);
8415 * The width of columns which have no width specified (defaults to 100)
8418 this.defaultWidth = 100;
8421 * Default sortable of columns which have no sortable specified (defaults to false)
8424 this.defaultSortable = false;
8428 * @event widthchange
8429 * Fires when the width of a column changes.
8430 * @param {ColumnModel} this
8431 * @param {Number} columnIndex The column index
8432 * @param {Number} newWidth The new width
8434 "widthchange": true,
8436 * @event headerchange
8437 * Fires when the text of a header changes.
8438 * @param {ColumnModel} this
8439 * @param {Number} columnIndex The column index
8440 * @param {Number} newText The new header text
8442 "headerchange": true,
8444 * @event hiddenchange
8445 * Fires when a column is hidden or "unhidden".
8446 * @param {ColumnModel} this
8447 * @param {Number} columnIndex The column index
8448 * @param {Boolean} hidden true if hidden, false otherwise
8450 "hiddenchange": true,
8452 * @event columnmoved
8453 * Fires when a column is moved.
8454 * @param {ColumnModel} this
8455 * @param {Number} oldIndex
8456 * @param {Number} newIndex
8458 "columnmoved" : true,
8460 * @event columlockchange
8461 * Fires when a column's locked state is changed
8462 * @param {ColumnModel} this
8463 * @param {Number} colIndex
8464 * @param {Boolean} locked true if locked
8466 "columnlockchange" : true
8468 Roo.grid.ColumnModel.superclass.constructor.call(this);
8470 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8472 * @cfg {String} header [required] The header text to display in the Grid view.
8475 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8478 * @cfg {String} smHeader Header at Bootsrap Small width
8481 * @cfg {String} mdHeader Header at Bootsrap Medium width
8484 * @cfg {String} lgHeader Header at Bootsrap Large width
8487 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8490 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8491 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8492 * specified, the column's index is used as an index into the Record's data Array.
8495 * @cfg {Number} width The initial width in pixels of the column. Using this
8496 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8499 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8500 * Defaults to the value of the {@link #defaultSortable} property.
8501 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8504 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8507 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8510 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8513 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8516 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8517 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8518 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8519 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8522 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8525 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8528 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8531 * @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)
8534 * @cfg {String} tooltip mouse over tooltip text
8537 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8540 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8546 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8549 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8552 * Returns the id of the column at the specified index.
8553 * @param {Number} index The column index
8554 * @return {String} the id
8556 getColumnId : function(index){
8557 return this.config[index].id;
8561 * Returns the column for a specified id.
8562 * @param {String} id The column id
8563 * @return {Object} the column
8565 getColumnById : function(id){
8566 return this.lookup[id];
8571 * Returns the column Object for a specified dataIndex.
8572 * @param {String} dataIndex The column dataIndex
8573 * @return {Object|Boolean} the column or false if not found
8575 getColumnByDataIndex: function(dataIndex){
8576 var index = this.findColumnIndex(dataIndex);
8577 return index > -1 ? this.config[index] : false;
8581 * Returns the index for a specified column id.
8582 * @param {String} id The column id
8583 * @return {Number} the index, or -1 if not found
8585 getIndexById : function(id){
8586 for(var i = 0, len = this.config.length; i < len; i++){
8587 if(this.config[i].id == id){
8595 * Returns the index for a specified column dataIndex.
8596 * @param {String} dataIndex The column dataIndex
8597 * @return {Number} the index, or -1 if not found
8600 findColumnIndex : function(dataIndex){
8601 for(var i = 0, len = this.config.length; i < len; i++){
8602 if(this.config[i].dataIndex == dataIndex){
8610 moveColumn : function(oldIndex, newIndex){
8611 var c = this.config[oldIndex];
8612 this.config.splice(oldIndex, 1);
8613 this.config.splice(newIndex, 0, c);
8614 this.dataMap = null;
8615 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8618 isLocked : function(colIndex){
8619 return this.config[colIndex].locked === true;
8622 setLocked : function(colIndex, value, suppressEvent){
8623 if(this.isLocked(colIndex) == value){
8626 this.config[colIndex].locked = value;
8628 this.fireEvent("columnlockchange", this, colIndex, value);
8632 getTotalLockedWidth : function(){
8634 for(var i = 0; i < this.config.length; i++){
8635 if(this.isLocked(i) && !this.isHidden(i)){
8636 this.totalWidth += this.getColumnWidth(i);
8642 getLockedCount : function(){
8643 for(var i = 0, len = this.config.length; i < len; i++){
8644 if(!this.isLocked(i)){
8649 return this.config.length;
8653 * Returns the number of columns.
8656 getColumnCount : function(visibleOnly){
8657 if(visibleOnly === true){
8659 for(var i = 0, len = this.config.length; i < len; i++){
8660 if(!this.isHidden(i)){
8666 return this.config.length;
8670 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8671 * @param {Function} fn
8672 * @param {Object} scope (optional)
8673 * @return {Array} result
8675 getColumnsBy : function(fn, scope){
8677 for(var i = 0, len = this.config.length; i < len; i++){
8678 var c = this.config[i];
8679 if(fn.call(scope||this, c, i) === true){
8687 * Returns true if the specified column is sortable.
8688 * @param {Number} col The column index
8691 isSortable : function(col){
8692 if(typeof this.config[col].sortable == "undefined"){
8693 return this.defaultSortable;
8695 return this.config[col].sortable;
8699 * Returns the rendering (formatting) function defined for the column.
8700 * @param {Number} col The column index.
8701 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8703 getRenderer : function(col){
8704 if(!this.config[col].renderer){
8705 return Roo.grid.ColumnModel.defaultRenderer;
8707 return this.config[col].renderer;
8711 * Sets the rendering (formatting) function for a column.
8712 * @param {Number} col The column index
8713 * @param {Function} fn The function to use to process the cell's raw data
8714 * to return HTML markup for the grid view. The render function is called with
8715 * the following parameters:<ul>
8716 * <li>Data value.</li>
8717 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8718 * <li>css A CSS style string to apply to the table cell.</li>
8719 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8720 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8721 * <li>Row index</li>
8722 * <li>Column index</li>
8723 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8725 setRenderer : function(col, fn){
8726 this.config[col].renderer = fn;
8730 * Returns the width for the specified column.
8731 * @param {Number} col The column index
8732 * @param (optional) {String} gridSize bootstrap width size.
8735 getColumnWidth : function(col, gridSize)
8737 var cfg = this.config[col];
8739 if (typeof(gridSize) == 'undefined') {
8740 return cfg.width * 1 || this.defaultWidth;
8742 if (gridSize === false) { // if we set it..
8743 return cfg.width || false;
8745 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8747 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8748 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8751 return cfg[ sizes[i] ];
8758 * Sets the width for a column.
8759 * @param {Number} col The column index
8760 * @param {Number} width The new width
8762 setColumnWidth : function(col, width, suppressEvent){
8763 this.config[col].width = width;
8764 this.totalWidth = null;
8766 this.fireEvent("widthchange", this, col, width);
8771 * Returns the total width of all columns.
8772 * @param {Boolean} includeHidden True to include hidden column widths
8775 getTotalWidth : function(includeHidden){
8776 if(!this.totalWidth){
8777 this.totalWidth = 0;
8778 for(var i = 0, len = this.config.length; i < len; i++){
8779 if(includeHidden || !this.isHidden(i)){
8780 this.totalWidth += this.getColumnWidth(i);
8784 return this.totalWidth;
8788 * Returns the header for the specified column.
8789 * @param {Number} col The column index
8792 getColumnHeader : function(col){
8793 return this.config[col].header;
8797 * Sets the header for a column.
8798 * @param {Number} col The column index
8799 * @param {String} header The new header
8801 setColumnHeader : function(col, header){
8802 this.config[col].header = header;
8803 this.fireEvent("headerchange", this, col, header);
8807 * Returns the tooltip for the specified column.
8808 * @param {Number} col The column index
8811 getColumnTooltip : function(col){
8812 return this.config[col].tooltip;
8815 * Sets the tooltip for a column.
8816 * @param {Number} col The column index
8817 * @param {String} tooltip The new tooltip
8819 setColumnTooltip : function(col, tooltip){
8820 this.config[col].tooltip = tooltip;
8824 * Returns the dataIndex for the specified column.
8825 * @param {Number} col The column index
8828 getDataIndex : function(col){
8829 return this.config[col].dataIndex;
8833 * Sets the dataIndex for a column.
8834 * @param {Number} col The column index
8835 * @param {Number} dataIndex The new dataIndex
8837 setDataIndex : function(col, dataIndex){
8838 this.config[col].dataIndex = dataIndex;
8844 * Returns true if the cell is editable.
8845 * @param {Number} colIndex The column index
8846 * @param {Number} rowIndex The row index - this is nto actually used..?
8849 isCellEditable : function(colIndex, rowIndex){
8850 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8854 * Returns the editor defined for the cell/column.
8855 * return false or null to disable editing.
8856 * @param {Number} colIndex The column index
8857 * @param {Number} rowIndex The row index
8860 getCellEditor : function(colIndex, rowIndex){
8861 return this.config[colIndex].editor;
8865 * Sets if a column is editable.
8866 * @param {Number} col The column index
8867 * @param {Boolean} editable True if the column is editable
8869 setEditable : function(col, editable){
8870 this.config[col].editable = editable;
8875 * Returns true if the column is hidden.
8876 * @param {Number} colIndex The column index
8879 isHidden : function(colIndex){
8880 return this.config[colIndex].hidden;
8885 * Returns true if the column width cannot be changed
8887 isFixed : function(colIndex){
8888 return this.config[colIndex].fixed;
8892 * Returns true if the column can be resized
8895 isResizable : function(colIndex){
8896 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8899 * Sets if a column is hidden.
8900 * @param {Number} colIndex The column index
8901 * @param {Boolean} hidden True if the column is hidden
8903 setHidden : function(colIndex, hidden){
8904 this.config[colIndex].hidden = hidden;
8905 this.totalWidth = null;
8906 this.fireEvent("hiddenchange", this, colIndex, hidden);
8910 * Sets the editor for a column.
8911 * @param {Number} col The column index
8912 * @param {Object} editor The editor object
8914 setEditor : function(col, editor){
8915 this.config[col].editor = editor;
8918 * Add a column (experimental...) - defaults to adding to the end..
8919 * @param {Object} config
8921 addColumn : function(c)
8924 var i = this.config.length;
8927 if(typeof c.dataIndex == "undefined"){
8930 if(typeof c.renderer == "string"){
8931 c.renderer = Roo.util.Format[c.renderer];
8933 if(typeof c.id == "undefined"){
8936 if(c.editor && c.editor.xtype){
8937 c.editor = Roo.factory(c.editor, Roo.grid);
8939 if(c.editor && c.editor.isFormField){
8940 c.editor = new Roo.grid.GridEditor(c.editor);
8942 this.lookup[c.id] = c;
8947 Roo.grid.ColumnModel.defaultRenderer = function(value)
8949 if(typeof value == "object") {
8952 if(typeof value == "string" && value.length < 1){
8956 return String.format("{0}", value);
8959 // Alias for backwards compatibility
8960 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8963 * Ext JS Library 1.1.1
8964 * Copyright(c) 2006-2007, Ext JS, LLC.
8966 * Originally Released Under LGPL - original licence link has changed is not relivant.
8969 * <script type="text/javascript">
8973 * @class Roo.LoadMask
8974 * A simple utility class for generically masking elements while loading data. If the element being masked has
8975 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8976 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8977 * element's UpdateManager load indicator and will be destroyed after the initial load.
8979 * Create a new LoadMask
8980 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8981 * @param {Object} config The config object
8983 Roo.LoadMask = function(el, config){
8984 this.el = Roo.get(el);
8985 Roo.apply(this, config);
8987 this.store.on('beforeload', this.onBeforeLoad, this);
8988 this.store.on('load', this.onLoad, this);
8989 this.store.on('loadexception', this.onLoadException, this);
8990 this.removeMask = false;
8992 var um = this.el.getUpdateManager();
8993 um.showLoadIndicator = false; // disable the default indicator
8994 um.on('beforeupdate', this.onBeforeLoad, this);
8995 um.on('update', this.onLoad, this);
8996 um.on('failure', this.onLoad, this);
8997 this.removeMask = true;
9001 Roo.LoadMask.prototype = {
9003 * @cfg {Boolean} removeMask
9004 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9005 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9010 * The text to display in a centered loading message box (defaults to 'Loading...')
9014 * @cfg {String} msgCls
9015 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9017 msgCls : 'x-mask-loading',
9020 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9026 * Disables the mask to prevent it from being displayed
9028 disable : function(){
9029 this.disabled = true;
9033 * Enables the mask so that it can be displayed
9035 enable : function(){
9036 this.disabled = false;
9039 onLoadException : function()
9043 if (typeof(arguments[3]) != 'undefined') {
9044 Roo.MessageBox.alert("Error loading",arguments[3]);
9048 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9049 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9056 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9061 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9065 onBeforeLoad : function(){
9067 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9072 destroy : function(){
9074 this.store.un('beforeload', this.onBeforeLoad, this);
9075 this.store.un('load', this.onLoad, this);
9076 this.store.un('loadexception', this.onLoadException, this);
9078 var um = this.el.getUpdateManager();
9079 um.un('beforeupdate', this.onBeforeLoad, this);
9080 um.un('update', this.onLoad, this);
9081 um.un('failure', this.onLoad, this);
9085 * @class Roo.bootstrap.Table
9087 * @extends Roo.bootstrap.Component
9088 * @children Roo.bootstrap.TableBody
9089 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9090 * Similar to Roo.grid.Grid
9092 var table = Roo.factory({
9094 xns : Roo.bootstrap,
9095 autoSizeColumns: true,
9102 sortInfo : { direction : 'ASC', field: 'name' },
9104 xtype : 'HttpProxy',
9107 url : 'https://example.com/some.data.url.json'
9110 xtype : 'JsonReader',
9112 fields : [ 'id', 'name', whatever' ],
9119 xtype : 'ColumnModel',
9123 dataIndex : 'is_in_group',
9126 renderer : function(v, x , r) {
9128 return String.format("{0}", v)
9134 xtype : 'RowSelectionModel',
9135 xns : Roo.bootstrap.Table
9136 // you can add listeners to catch selection change here....
9142 grid.render(Roo.get("some-div"));
9145 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9150 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9151 * @cfg {Roo.data.Store} store The data store to use
9152 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9154 * @cfg {String} cls table class
9157 * @cfg {string} empty_results Text to display for no results
9158 * @cfg {boolean} striped Should the rows be alternative striped
9159 * @cfg {boolean} bordered Add borders to the table
9160 * @cfg {boolean} hover Add hover highlighting
9161 * @cfg {boolean} condensed Format condensed
9162 * @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,
9163 * also adds table-responsive (see bootstrap docs for details)
9164 * @cfg {Boolean} loadMask (true|false) default false
9165 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9166 * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9167 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9168 * @cfg {Boolean} rowSelection (true|false) default false
9169 * @cfg {Boolean} cellSelection (true|false) default false
9170 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9171 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9172 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9173 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9174 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9175 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9178 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9181 * Create a new Table
9182 * @param {Object} config The config object
9185 Roo.bootstrap.Table = function(config)
9187 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9190 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9191 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9192 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9193 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9195 this.view = this; // compat with grid.
9197 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9199 this.sm.grid = this;
9200 this.selModel = Roo.factory(this.sm, Roo.grid);
9201 this.sm = this.selModel;
9202 this.sm.xmodule = this.xmodule || false;
9205 if (this.cm && typeof(this.cm.config) == 'undefined') {
9206 this.colModel = new Roo.grid.ColumnModel(this.cm);
9207 this.cm = this.colModel;
9208 this.cm.xmodule = this.xmodule || false;
9211 this.store= Roo.factory(this.store, Roo.data);
9212 this.ds = this.store;
9213 this.ds.xmodule = this.xmodule || false;
9216 if (this.footer && this.store) {
9217 this.footer.dataSource = this.ds;
9218 this.footer = Roo.factory(this.footer);
9225 * Fires when a cell is clicked
9226 * @param {Roo.bootstrap.Table} this
9227 * @param {Roo.Element} el
9228 * @param {Number} rowIndex
9229 * @param {Number} columnIndex
9230 * @param {Roo.EventObject} e
9234 * @event celldblclick
9235 * Fires when a cell is double clicked
9236 * @param {Roo.bootstrap.Table} this
9237 * @param {Roo.Element} el
9238 * @param {Number} rowIndex
9239 * @param {Number} columnIndex
9240 * @param {Roo.EventObject} e
9242 "celldblclick" : true,
9245 * Fires when a row is clicked
9246 * @param {Roo.bootstrap.Table} this
9247 * @param {Roo.Element} el
9248 * @param {Number} rowIndex
9249 * @param {Roo.EventObject} e
9253 * @event rowdblclick
9254 * Fires when a row is double clicked
9255 * @param {Roo.bootstrap.Table} this
9256 * @param {Roo.Element} el
9257 * @param {Number} rowIndex
9258 * @param {Roo.EventObject} e
9260 "rowdblclick" : true,
9263 * Fires when a mouseover occur
9264 * @param {Roo.bootstrap.Table} this
9265 * @param {Roo.Element} el
9266 * @param {Number} rowIndex
9267 * @param {Number} columnIndex
9268 * @param {Roo.EventObject} e
9273 * Fires when a mouseout occur
9274 * @param {Roo.bootstrap.Table} this
9275 * @param {Roo.Element} el
9276 * @param {Number} rowIndex
9277 * @param {Number} columnIndex
9278 * @param {Roo.EventObject} e
9283 * Fires when a row is rendered, so you can change add a style to it.
9284 * @param {Roo.bootstrap.Table} this
9285 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9289 * @event rowsrendered
9290 * Fires when all the rows have been rendered
9291 * @param {Roo.bootstrap.Table} this
9293 'rowsrendered' : true,
9295 * @event contextmenu
9296 * The raw contextmenu event for the entire grid.
9297 * @param {Roo.EventObject} e
9299 "contextmenu" : true,
9301 * @event rowcontextmenu
9302 * Fires when a row is right clicked
9303 * @param {Roo.bootstrap.Table} this
9304 * @param {Number} rowIndex
9305 * @param {Roo.EventObject} e
9307 "rowcontextmenu" : true,
9309 * @event cellcontextmenu
9310 * Fires when a cell is right clicked
9311 * @param {Roo.bootstrap.Table} this
9312 * @param {Number} rowIndex
9313 * @param {Number} cellIndex
9314 * @param {Roo.EventObject} e
9316 "cellcontextmenu" : true,
9318 * @event headercontextmenu
9319 * Fires when a header is right clicked
9320 * @param {Roo.bootstrap.Table} this
9321 * @param {Number} columnIndex
9322 * @param {Roo.EventObject} e
9324 "headercontextmenu" : true,
9327 * The raw mousedown event for the entire grid.
9328 * @param {Roo.EventObject} e
9335 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9353 enableColumnResize: true,
9354 disableAutoSize: false,
9356 rowSelection : false,
9357 cellSelection : false,
9360 minColumnWidth : 50,
9362 // Roo.Element - the tbody
9363 bodyEl: false, // <tbody> Roo.Element - thead element
9364 headEl: false, // <thead> Roo.Element - thead element
9365 resizeProxy : false, // proxy element for dragging?
9369 container: false, // used by gridpanel...
9375 auto_hide_footer : false,
9377 view: false, // actually points to this..
9379 getAutoCreate : function()
9381 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9388 // this get's auto added by panel.Grid
9389 if (this.scrollBody) {
9390 cfg.cls += ' table-body-fixed';
9393 cfg.cls += ' table-striped';
9397 cfg.cls += ' table-hover';
9399 if (this.bordered) {
9400 cfg.cls += ' table-bordered';
9402 if (this.condensed) {
9403 cfg.cls += ' table-condensed';
9406 if (this.responsive) {
9407 cfg.cls += ' table-responsive';
9411 cfg.cls+= ' ' +this.cls;
9417 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9420 if(this.store || this.cm){
9421 if(this.headerShow){
9422 cfg.cn.push(this.renderHeader());
9425 cfg.cn.push(this.renderBody());
9427 if(this.footerShow || this.footerRow){
9428 cfg.cn.push(this.renderFooter());
9431 // where does this come from?
9432 //cfg.cls+= ' TableGrid';
9435 return { cn : [ cfg ] };
9438 initEvents : function()
9440 if(!this.store || !this.cm){
9443 if (this.selModel) {
9444 this.selModel.initEvents();
9448 //Roo.log('initEvents with ds!!!!');
9450 this.bodyEl = this.el.select('tbody', true).first();
9451 this.headEl = this.el.select('thead', true).first();
9452 this.mainFoot = this.el.select('tfoot', true).first();
9457 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9458 e.on('click', this.sort, this);
9462 // why is this done????? = it breaks dialogs??
9463 //this.parent().el.setStyle('position', 'relative');
9467 this.footer.parentId = this.id;
9468 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9471 this.el.select('tfoot tr td').first().addClass('hide');
9476 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9479 this.store.on('load', this.onLoad, this);
9480 this.store.on('beforeload', this.onBeforeLoad, this);
9481 this.store.on('update', this.onUpdate, this);
9482 this.store.on('add', this.onAdd, this);
9483 this.store.on("clear", this.clear, this);
9485 this.el.on("contextmenu", this.onContextMenu, this);
9488 this.cm.on("headerchange", this.onHeaderChange, this);
9489 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9491 //?? does bodyEl get replaced on render?
9492 this.bodyEl.on("click", this.onClick, this);
9493 this.bodyEl.on("dblclick", this.onDblClick, this);
9494 this.bodyEl.on('scroll', this.onBodyScroll, this);
9496 // guessing mainbody will work - this relays usually caught by selmodel at present.
9497 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9500 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9503 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9504 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9509 // Compatibility with grid - we implement all the view features at present.
9510 getView : function()
9515 initCSS : function()
9517 if(this.disableAutoSize) {
9521 var cm = this.cm, styles = [];
9522 this.CSS.removeStyleSheet(this.id + '-cssrules');
9523 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9524 // we can honour xs/sm/md/xl as widths...
9525 // we first have to decide what widht we are currently at...
9526 var sz = Roo.getGridSize();
9530 var cols = []; // visable cols.
9532 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9533 var w = cm.getColumnWidth(i, false);
9535 cols.push( { rel : false, abs : 0 });
9539 cols.push( { rel : false, abs : w });
9541 last = i; // not really..
9544 var w = cm.getColumnWidth(i, sz);
9549 cols.push( { rel : w, abs : false });
9552 var avail = this.bodyEl.dom.clientWidth - total_abs;
9554 var unitWidth = Math.floor(avail / total);
9555 var rem = avail - (unitWidth * total);
9557 var hidden, width, pos = 0 , splithide , left;
9558 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9560 hidden = 'display:none;';
9562 width = 'width:0px;';
9564 if(!cm.isHidden(i)){
9568 // we can honour xs/sm/md/xl ?
9569 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9571 hidden = 'display:none;';
9573 // width should return a small number...
9575 w+=rem; // add the remaining with..
9578 left = "left:" + (pos -4) + "px;";
9579 width = "width:" + w+ "px;";
9582 if (this.responsive) {
9585 hidden = cm.isHidden(i) ? 'display:none;' : '';
9586 splithide = 'display: none;';
9589 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9592 splithide = 'display:none;';
9595 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9596 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9597 // this is the popover version..
9598 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9603 //Roo.log(styles.join(''));
9604 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9610 onContextMenu : function(e, t)
9612 this.processEvent("contextmenu", e);
9615 processEvent : function(name, e)
9617 if (name != 'touchstart' ) {
9618 this.fireEvent(name, e);
9621 var t = e.getTarget();
9623 var cell = Roo.get(t);
9629 if(cell.findParent('tfoot', false, true)){
9633 if(cell.findParent('thead', false, true)){
9635 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9636 cell = Roo.get(t).findParent('th', false, true);
9638 Roo.log("failed to find th in thead?");
9639 Roo.log(e.getTarget());
9644 var cellIndex = cell.dom.cellIndex;
9646 var ename = name == 'touchstart' ? 'click' : name;
9647 this.fireEvent("header" + ename, this, cellIndex, e);
9652 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9653 cell = Roo.get(t).findParent('td', false, true);
9655 Roo.log("failed to find th in tbody?");
9656 Roo.log(e.getTarget());
9661 var row = cell.findParent('tr', false, true);
9662 var cellIndex = cell.dom.cellIndex;
9663 var rowIndex = row.dom.rowIndex - 1;
9667 this.fireEvent("row" + name, this, rowIndex, e);
9671 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9677 onMouseover : function(e, el)
9679 var cell = Roo.get(el);
9685 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9686 cell = cell.findParent('td', false, true);
9689 var row = cell.findParent('tr', false, true);
9690 var cellIndex = cell.dom.cellIndex;
9691 var rowIndex = row.dom.rowIndex - 1; // start from 0
9693 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9697 onMouseout : function(e, el)
9699 var cell = Roo.get(el);
9705 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9706 cell = cell.findParent('td', false, true);
9709 var row = cell.findParent('tr', false, true);
9710 var cellIndex = cell.dom.cellIndex;
9711 var rowIndex = row.dom.rowIndex - 1; // start from 0
9713 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9717 onClick : function(e, el)
9719 var cell = Roo.get(el);
9721 if(!cell || (!this.cellSelection && !this.rowSelection)){
9725 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9726 cell = cell.findParent('td', false, true);
9729 if(!cell || typeof(cell) == 'undefined'){
9733 var row = cell.findParent('tr', false, true);
9735 if(!row || typeof(row) == 'undefined'){
9739 var cellIndex = cell.dom.cellIndex;
9740 var rowIndex = this.getRowIndex(row);
9742 // why??? - should these not be based on SelectionModel?
9743 //if(this.cellSelection){
9744 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9747 //if(this.rowSelection){
9748 this.fireEvent('rowclick', this, row, rowIndex, e);
9753 onDblClick : function(e,el)
9755 var cell = Roo.get(el);
9757 if(!cell || (!this.cellSelection && !this.rowSelection)){
9761 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9762 cell = cell.findParent('td', false, true);
9765 if(!cell || typeof(cell) == 'undefined'){
9769 var row = cell.findParent('tr', false, true);
9771 if(!row || typeof(row) == 'undefined'){
9775 var cellIndex = cell.dom.cellIndex;
9776 var rowIndex = this.getRowIndex(row);
9778 if(this.cellSelection){
9779 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9782 if(this.rowSelection){
9783 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9786 findRowIndex : function(el)
9788 var cell = Roo.get(el);
9792 var row = cell.findParent('tr', false, true);
9794 if(!row || typeof(row) == 'undefined'){
9797 return this.getRowIndex(row);
9799 sort : function(e,el)
9801 var col = Roo.get(el);
9803 if(!col.hasClass('sortable')){
9807 var sort = col.attr('sort');
9810 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9814 this.store.sortInfo = {field : sort, direction : dir};
9817 Roo.log("calling footer first");
9818 this.footer.onClick('first');
9821 this.store.load({ params : { start : 0 } });
9825 renderHeader : function()
9833 this.totalWidth = 0;
9835 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9837 var config = cm.config[i];
9841 cls : 'x-hcol-' + i,
9844 html: cm.getColumnHeader(i)
9847 var tooltip = cm.getColumnTooltip(i);
9849 c.tooltip = tooltip;
9855 if(typeof(config.sortable) != 'undefined' && config.sortable){
9856 c.cls += ' sortable';
9857 c.html = '<i class="fa"></i>' + c.html;
9860 // could use BS4 hidden-..-down
9862 if(typeof(config.lgHeader) != 'undefined'){
9863 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9866 if(typeof(config.mdHeader) != 'undefined'){
9867 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9870 if(typeof(config.smHeader) != 'undefined'){
9871 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9874 if(typeof(config.xsHeader) != 'undefined'){
9875 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9882 if(typeof(config.tooltip) != 'undefined'){
9883 c.tooltip = config.tooltip;
9886 if(typeof(config.colspan) != 'undefined'){
9887 c.colspan = config.colspan;
9890 // hidden is handled by CSS now
9892 if(typeof(config.dataIndex) != 'undefined'){
9893 c.sort = config.dataIndex;
9898 if(typeof(config.align) != 'undefined' && config.align.length){
9899 c.style += ' text-align:' + config.align + ';';
9902 /* width is done in CSS
9903 *if(typeof(config.width) != 'undefined'){
9904 c.style += ' width:' + config.width + 'px;';
9905 this.totalWidth += config.width;
9907 this.totalWidth += 100; // assume minimum of 100 per column?
9911 if(typeof(config.cls) != 'undefined'){
9912 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9914 // this is the bit that doesnt reall work at all...
9916 if (this.responsive) {
9919 ['xs','sm','md','lg'].map(function(size){
9921 if(typeof(config[size]) == 'undefined'){
9925 if (!config[size]) { // 0 = hidden
9926 // BS 4 '0' is treated as hide that column and below.
9927 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9931 c.cls += ' col-' + size + '-' + config[size] + (
9932 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9940 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9951 renderBody : function()
9961 colspan : this.cm.getColumnCount()
9971 renderFooter : function()
9981 colspan : this.cm.getColumnCount()
9993 // Roo.log('ds onload');
9998 var ds = this.store;
10000 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10001 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10002 if (_this.store.sortInfo) {
10004 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10005 e.select('i', true).addClass(['fa-arrow-up']);
10008 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10009 e.select('i', true).addClass(['fa-arrow-down']);
10014 var tbody = this.bodyEl;
10016 if(ds.getCount() > 0){
10017 ds.data.each(function(d,rowIndex){
10018 var row = this.renderRow(cm, ds, rowIndex);
10020 tbody.createChild(row);
10024 if(row.cellObjects.length){
10025 Roo.each(row.cellObjects, function(r){
10026 _this.renderCellObject(r);
10031 } else if (this.empty_results.length) {
10032 this.el.mask(this.empty_results, 'no-spinner');
10035 var tfoot = this.el.select('tfoot', true).first();
10037 if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10039 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10041 var total = this.ds.getTotalCount();
10043 if(this.footer.pageSize < total){
10044 this.mainFoot.show();
10048 if(!this.footerShow && this.footerRow) {
10055 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10056 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10059 cls : ' x-fcol-' + i,
10067 tfoot.dom.innerHTML = '';
10069 tfoot.createChild(tr);
10072 Roo.each(this.el.select('tbody td', true).elements, function(e){
10073 e.on('mouseover', _this.onMouseover, _this);
10076 Roo.each(this.el.select('tbody td', true).elements, function(e){
10077 e.on('mouseout', _this.onMouseout, _this);
10079 this.fireEvent('rowsrendered', this);
10083 this.initCSS(); /// resize cols
10089 onUpdate : function(ds,record)
10091 this.refreshRow(record);
10095 onRemove : function(ds, record, index, isUpdate){
10096 if(isUpdate !== true){
10097 this.fireEvent("beforerowremoved", this, index, record);
10099 var bt = this.bodyEl.dom;
10101 var rows = this.el.select('tbody > tr', true).elements;
10103 if(typeof(rows[index]) != 'undefined'){
10104 bt.removeChild(rows[index].dom);
10107 // if(bt.rows[index]){
10108 // bt.removeChild(bt.rows[index]);
10111 if(isUpdate !== true){
10112 //this.stripeRows(index);
10113 //this.syncRowHeights(index, index);
10115 this.fireEvent("rowremoved", this, index, record);
10119 onAdd : function(ds, records, rowIndex)
10121 //Roo.log('on Add called');
10122 // - note this does not handle multiple adding very well..
10123 var bt = this.bodyEl.dom;
10124 for (var i =0 ; i < records.length;i++) {
10125 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10126 //Roo.log(records[i]);
10127 //Roo.log(this.store.getAt(rowIndex+i));
10128 this.insertRow(this.store, rowIndex + i, false);
10135 refreshRow : function(record){
10136 var ds = this.store, index;
10137 if(typeof record == 'number'){
10139 record = ds.getAt(index);
10141 index = ds.indexOf(record);
10143 return; // should not happen - but seems to
10146 this.insertRow(ds, index, true);
10148 this.onRemove(ds, record, index+1, true);
10150 //this.syncRowHeights(index, index);
10152 this.fireEvent("rowupdated", this, index, record);
10154 // private - called by RowSelection
10155 onRowSelect : function(rowIndex){
10156 var row = this.getRowDom(rowIndex);
10157 row.addClass(['bg-info','info']);
10159 // private - called by RowSelection
10160 onRowDeselect : function(rowIndex)
10162 if (rowIndex < 0) {
10165 var row = this.getRowDom(rowIndex);
10166 row.removeClass(['bg-info','info']);
10169 * Focuses the specified row.
10170 * @param {Number} row The row index
10172 focusRow : function(row)
10174 //Roo.log('GridView.focusRow');
10175 var x = this.bodyEl.dom.scrollLeft;
10176 this.focusCell(row, 0, false);
10177 this.bodyEl.dom.scrollLeft = x;
10181 * Focuses the specified cell.
10182 * @param {Number} row The row index
10183 * @param {Number} col The column index
10184 * @param {Boolean} hscroll false to disable horizontal scrolling
10186 focusCell : function(row, col, hscroll)
10188 //Roo.log('GridView.focusCell');
10189 var el = this.ensureVisible(row, col, hscroll);
10190 // not sure what focusEL achives = it's a <a> pos relative
10191 //this.focusEl.alignTo(el, "tl-tl");
10193 // this.focusEl.focus();
10195 // this.focusEl.focus.defer(1, this.focusEl);
10200 * Scrolls the specified cell into view
10201 * @param {Number} row The row index
10202 * @param {Number} col The column index
10203 * @param {Boolean} hscroll false to disable horizontal scrolling
10205 ensureVisible : function(row, col, hscroll)
10207 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10208 //return null; //disable for testing.
10209 if(typeof row != "number"){
10210 row = row.rowIndex;
10212 if(row < 0 && row >= this.ds.getCount()){
10215 col = (col !== undefined ? col : 0);
10217 while(cm.isHidden(col)){
10221 var el = this.getCellDom(row, col);
10225 var c = this.bodyEl.dom;
10227 var ctop = parseInt(el.offsetTop, 10);
10228 var cleft = parseInt(el.offsetLeft, 10);
10229 var cbot = ctop + el.offsetHeight;
10230 var cright = cleft + el.offsetWidth;
10232 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10233 var ch = 0; //?? header is not withing the area?
10234 var stop = parseInt(c.scrollTop, 10);
10235 var sleft = parseInt(c.scrollLeft, 10);
10236 var sbot = stop + ch;
10237 var sright = sleft + c.clientWidth;
10239 Roo.log('GridView.ensureVisible:' +
10241 ' c.clientHeight:' + c.clientHeight +
10242 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10250 c.scrollTop = ctop;
10251 //Roo.log("set scrolltop to ctop DISABLE?");
10252 }else if(cbot > sbot){
10253 //Roo.log("set scrolltop to cbot-ch");
10254 c.scrollTop = cbot-ch;
10257 if(hscroll !== false){
10259 c.scrollLeft = cleft;
10260 }else if(cright > sright){
10261 c.scrollLeft = cright-c.clientWidth;
10269 insertRow : function(dm, rowIndex, isUpdate){
10272 this.fireEvent("beforerowsinserted", this, rowIndex);
10274 //var s = this.getScrollState();
10275 var row = this.renderRow(this.cm, this.store, rowIndex);
10276 // insert before rowIndex..
10277 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10281 if(row.cellObjects.length){
10282 Roo.each(row.cellObjects, function(r){
10283 _this.renderCellObject(r);
10288 this.fireEvent("rowsinserted", this, rowIndex);
10289 //this.syncRowHeights(firstRow, lastRow);
10290 //this.stripeRows(firstRow);
10297 getRowDom : function(rowIndex)
10299 var rows = this.el.select('tbody > tr', true).elements;
10301 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10304 getCellDom : function(rowIndex, colIndex)
10306 var row = this.getRowDom(rowIndex);
10307 if (row === false) {
10310 var cols = row.select('td', true).elements;
10311 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10315 // returns the object tree for a tr..
10318 renderRow : function(cm, ds, rowIndex)
10320 var d = ds.getAt(rowIndex);
10324 cls : 'x-row-' + rowIndex,
10328 var cellObjects = [];
10330 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10331 var config = cm.config[i];
10333 var renderer = cm.getRenderer(i);
10337 if(typeof(renderer) !== 'undefined'){
10338 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10340 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10341 // and are rendered into the cells after the row is rendered - using the id for the element.
10343 if(typeof(value) === 'object'){
10353 rowIndex : rowIndex,
10358 this.fireEvent('rowclass', this, rowcfg);
10362 // this might end up displaying HTML?
10363 // this is too messy... - better to only do it on columsn you know are going to be too long
10364 //tooltip : (typeof(value) === 'object') ? '' : value,
10365 cls : rowcfg.rowClass + ' x-col-' + i,
10367 html: (typeof(value) === 'object') ? '' : value
10374 if(typeof(config.colspan) != 'undefined'){
10375 td.colspan = config.colspan;
10380 if(typeof(config.align) != 'undefined' && config.align.length){
10381 td.style += ' text-align:' + config.align + ';';
10383 if(typeof(config.valign) != 'undefined' && config.valign.length){
10384 td.style += ' vertical-align:' + config.valign + ';';
10387 if(typeof(config.width) != 'undefined'){
10388 td.style += ' width:' + config.width + 'px;';
10392 if(typeof(config.cursor) != 'undefined'){
10393 td.style += ' cursor:' + config.cursor + ';';
10396 if(typeof(config.cls) != 'undefined'){
10397 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10399 if (this.responsive) {
10400 ['xs','sm','md','lg'].map(function(size){
10402 if(typeof(config[size]) == 'undefined'){
10408 if (!config[size]) { // 0 = hidden
10409 // BS 4 '0' is treated as hide that column and below.
10410 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10414 td.cls += ' col-' + size + '-' + config[size] + (
10415 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10425 row.cellObjects = cellObjects;
10433 onBeforeLoad : function()
10435 this.el.unmask(); // if needed.
10442 this.el.select('tbody', true).first().dom.innerHTML = '';
10445 * Show or hide a row.
10446 * @param {Number} rowIndex to show or hide
10447 * @param {Boolean} state hide
10449 setRowVisibility : function(rowIndex, state)
10451 var bt = this.bodyEl.dom;
10453 var rows = this.el.select('tbody > tr', true).elements;
10455 if(typeof(rows[rowIndex]) == 'undefined'){
10458 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10463 getSelectionModel : function(){
10464 if(!this.selModel){
10465 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10467 return this.selModel;
10470 * Render the Roo.bootstrap object from renderder
10472 renderCellObject : function(r)
10476 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10478 var t = r.cfg.render(r.container);
10481 Roo.each(r.cfg.cn, function(c){
10483 container: t.getChildContainer(),
10486 _this.renderCellObject(child);
10491 * get the Row Index from a dom element.
10492 * @param {Roo.Element} row The row to look for
10493 * @returns {Number} the row
10495 getRowIndex : function(row)
10499 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10510 * get the header TH element for columnIndex
10511 * @param {Number} columnIndex
10512 * @returns {Roo.Element}
10514 getHeaderIndex: function(colIndex)
10516 var cols = this.headEl.select('th', true).elements;
10517 return cols[colIndex];
10520 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10521 * @param {domElement} cell to look for
10522 * @returns {Number} the column
10524 getCellIndex : function(cell)
10526 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10528 return parseInt(id[1], 10);
10533 * Returns the grid's underlying element = used by panel.Grid
10534 * @return {Element} The element
10536 getGridEl : function(){
10540 * Forces a resize - used by panel.Grid
10541 * @return {Element} The element
10543 autoSize : function()
10545 if(this.disableAutoSize) {
10548 //var ctr = Roo.get(this.container.dom.parentElement);
10549 var ctr = Roo.get(this.el.dom);
10551 var thd = this.getGridEl().select('thead',true).first();
10552 var tbd = this.getGridEl().select('tbody', true).first();
10553 var tfd = this.getGridEl().select('tfoot', true).first();
10555 var cw = ctr.getWidth();
10556 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10560 tbd.setWidth(ctr.getWidth());
10561 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10562 // this needs fixing for various usage - currently only hydra job advers I think..
10564 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10566 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10569 cw = Math.max(cw, this.totalWidth);
10570 this.getGridEl().select('tbody tr',true).setWidth(cw);
10573 // resize 'expandable coloumn?
10575 return; // we doe not have a view in this design..
10578 onBodyScroll: function()
10580 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10582 this.headEl.setStyle({
10583 'position' : 'relative',
10584 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10590 var scrollHeight = this.bodyEl.dom.scrollHeight;
10592 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10594 var height = this.bodyEl.getHeight();
10596 if(scrollHeight - height == scrollTop) {
10598 var total = this.ds.getTotalCount();
10600 if(this.footer.cursor + this.footer.pageSize < total){
10602 this.footer.ds.load({
10604 start : this.footer.cursor + this.footer.pageSize,
10605 limit : this.footer.pageSize
10614 onColumnSplitterMoved : function(i, diff)
10616 this.userResized = true;
10618 var cm = this.colModel;
10620 var w = this.getHeaderIndex(i).getWidth() + diff;
10623 cm.setColumnWidth(i, w, true);
10625 //var cid = cm.getColumnId(i); << not used in this version?
10626 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10628 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10629 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10630 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10632 //this.updateSplitters();
10633 //this.layout(); << ??
10634 this.fireEvent("columnresize", i, w);
10636 onHeaderChange : function()
10638 var header = this.renderHeader();
10639 var table = this.el.select('table', true).first();
10641 this.headEl.remove();
10642 this.headEl = table.createChild(header, this.bodyEl, false);
10644 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10645 e.on('click', this.sort, this);
10648 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10649 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10654 onHiddenChange : function(colModel, colIndex, hidden)
10657 this.cm.setHidden()
10658 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10659 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10661 this.CSS.updateRule(thSelector, "display", "");
10662 this.CSS.updateRule(tdSelector, "display", "");
10665 this.CSS.updateRule(thSelector, "display", "none");
10666 this.CSS.updateRule(tdSelector, "display", "none");
10669 // onload calls initCSS()
10670 this.onHeaderChange();
10674 setColumnWidth: function(col_index, width)
10676 // width = "md-2 xs-2..."
10677 if(!this.colModel.config[col_index]) {
10681 var w = width.split(" ");
10683 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10685 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10688 for(var j = 0; j < w.length; j++) {
10694 var size_cls = w[j].split("-");
10696 if(!Number.isInteger(size_cls[1] * 1)) {
10700 if(!this.colModel.config[col_index][size_cls[0]]) {
10704 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10708 h_row[0].classList.replace(
10709 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10710 "col-"+size_cls[0]+"-"+size_cls[1]
10713 for(var i = 0; i < rows.length; i++) {
10715 var size_cls = w[j].split("-");
10717 if(!Number.isInteger(size_cls[1] * 1)) {
10721 if(!this.colModel.config[col_index][size_cls[0]]) {
10725 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10729 rows[i].classList.replace(
10730 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10731 "col-"+size_cls[0]+"-"+size_cls[1]
10735 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10740 // currently only used to find the split on drag..
10741 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10746 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10747 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10756 * @class Roo.bootstrap.TableCell
10757 * @extends Roo.bootstrap.Component
10758 * @children Roo.bootstrap.Component
10759 * @parent Roo.bootstrap.TableRow
10760 * Bootstrap TableCell class
10762 * @cfg {String} html cell contain text
10763 * @cfg {String} cls cell class
10764 * @cfg {String} tag cell tag (td|th) default td
10765 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10766 * @cfg {String} align Aligns the content in a cell
10767 * @cfg {String} axis Categorizes cells
10768 * @cfg {String} bgcolor Specifies the background color of a cell
10769 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10770 * @cfg {Number} colspan Specifies the number of columns a cell should span
10771 * @cfg {String} headers Specifies one or more header cells a cell is related to
10772 * @cfg {Number} height Sets the height of a cell
10773 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10774 * @cfg {Number} rowspan Sets the number of rows a cell should span
10775 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10776 * @cfg {String} valign Vertical aligns the content in a cell
10777 * @cfg {Number} width Specifies the width of a cell
10780 * Create a new TableCell
10781 * @param {Object} config The config object
10784 Roo.bootstrap.TableCell = function(config){
10785 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10808 getAutoCreate : function(){
10809 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10816 cfg.tag = this.tag;
10829 cfg.align=this.align
10834 if (this.bgcolor) {
10835 cfg.bgcolor=this.bgcolor
10837 if (this.charoff) {
10838 cfg.charoff=this.charoff
10840 if (this.colspan) {
10841 cfg.colspan=this.colspan
10843 if (this.headers) {
10844 cfg.headers=this.headers
10847 cfg.height=this.height
10850 cfg.nowrap=this.nowrap
10852 if (this.rowspan) {
10853 cfg.rowspan=this.rowspan
10856 cfg.scope=this.scope
10859 cfg.valign=this.valign
10862 cfg.width=this.width
10881 * @class Roo.bootstrap.TableRow
10882 * @extends Roo.bootstrap.Component
10883 * @children Roo.bootstrap.TableCell
10884 * @parent Roo.bootstrap.TableBody
10885 * Bootstrap TableRow class
10886 * @cfg {String} cls row class
10887 * @cfg {String} align Aligns the content in a table row
10888 * @cfg {String} bgcolor Specifies a background color for a table row
10889 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10890 * @cfg {String} valign Vertical aligns the content in a table row
10893 * Create a new TableRow
10894 * @param {Object} config The config object
10897 Roo.bootstrap.TableRow = function(config){
10898 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10901 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10909 getAutoCreate : function(){
10910 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10917 cfg.cls = this.cls;
10920 cfg.align = this.align;
10923 cfg.bgcolor = this.bgcolor;
10926 cfg.charoff = this.charoff;
10929 cfg.valign = this.valign;
10947 * @class Roo.bootstrap.TableBody
10948 * @extends Roo.bootstrap.Component
10949 * @children Roo.bootstrap.TableRow
10950 * @parent Roo.bootstrap.Table
10951 * Bootstrap TableBody class
10952 * @cfg {String} cls element class
10953 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10954 * @cfg {String} align Aligns the content inside the element
10955 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10956 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10959 * Create a new TableBody
10960 * @param {Object} config The config object
10963 Roo.bootstrap.TableBody = function(config){
10964 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10967 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10975 getAutoCreate : function(){
10976 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10986 cfg.tag = this.tag;
10990 cfg.align = this.align;
10993 cfg.charoff = this.charoff;
10996 cfg.valign = this.valign;
11003 // initEvents : function()
11006 // if(!this.store){
11010 // this.store = Roo.factory(this.store, Roo.data);
11011 // this.store.on('load', this.onLoad, this);
11013 // this.store.load();
11017 // onLoad: function ()
11019 // this.fireEvent('load', this);
11029 * Ext JS Library 1.1.1
11030 * Copyright(c) 2006-2007, Ext JS, LLC.
11032 * Originally Released Under LGPL - original licence link has changed is not relivant.
11035 * <script type="text/javascript">
11038 // as we use this in bootstrap.
11039 Roo.namespace('Roo.form');
11041 * @class Roo.form.Action
11042 * Internal Class used to handle form actions
11044 * @param {Roo.form.BasicForm} el The form element or its id
11045 * @param {Object} config Configuration options
11050 // define the action interface
11051 Roo.form.Action = function(form, options){
11053 this.options = options || {};
11056 * Client Validation Failed
11059 Roo.form.Action.CLIENT_INVALID = 'client';
11061 * Server Validation Failed
11064 Roo.form.Action.SERVER_INVALID = 'server';
11066 * Connect to Server Failed
11069 Roo.form.Action.CONNECT_FAILURE = 'connect';
11071 * Reading Data from Server Failed
11074 Roo.form.Action.LOAD_FAILURE = 'load';
11076 Roo.form.Action.prototype = {
11078 failureType : undefined,
11079 response : undefined,
11080 result : undefined,
11082 // interface method
11083 run : function(options){
11087 // interface method
11088 success : function(response){
11092 // interface method
11093 handleResponse : function(response){
11097 // default connection failure
11098 failure : function(response){
11100 this.response = response;
11101 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11102 this.form.afterAction(this, false);
11105 processResponse : function(response){
11106 this.response = response;
11107 if(!response.responseText){
11110 this.result = this.handleResponse(response);
11111 return this.result;
11114 // utility functions used internally
11115 getUrl : function(appendParams){
11116 var url = this.options.url || this.form.url || this.form.el.dom.action;
11118 var p = this.getParams();
11120 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11126 getMethod : function(){
11127 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11130 getParams : function(){
11131 var bp = this.form.baseParams;
11132 var p = this.options.params;
11134 if(typeof p == "object"){
11135 p = Roo.urlEncode(Roo.applyIf(p, bp));
11136 }else if(typeof p == 'string' && bp){
11137 p += '&' + Roo.urlEncode(bp);
11140 p = Roo.urlEncode(bp);
11145 createCallback : function(){
11147 success: this.success,
11148 failure: this.failure,
11150 timeout: (this.form.timeout*1000),
11151 upload: this.form.fileUpload ? this.success : undefined
11156 Roo.form.Action.Submit = function(form, options){
11157 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11160 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11163 haveProgress : false,
11164 uploadComplete : false,
11166 // uploadProgress indicator.
11167 uploadProgress : function()
11169 if (!this.form.progressUrl) {
11173 if (!this.haveProgress) {
11174 Roo.MessageBox.progress("Uploading", "Uploading");
11176 if (this.uploadComplete) {
11177 Roo.MessageBox.hide();
11181 this.haveProgress = true;
11183 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11185 var c = new Roo.data.Connection();
11187 url : this.form.progressUrl,
11192 success : function(req){
11193 //console.log(data);
11197 rdata = Roo.decode(req.responseText)
11199 Roo.log("Invalid data from server..");
11203 if (!rdata || !rdata.success) {
11205 Roo.MessageBox.alert(Roo.encode(rdata));
11208 var data = rdata.data;
11210 if (this.uploadComplete) {
11211 Roo.MessageBox.hide();
11216 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11217 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11220 this.uploadProgress.defer(2000,this);
11223 failure: function(data) {
11224 Roo.log('progress url failed ');
11235 // run get Values on the form, so it syncs any secondary forms.
11236 this.form.getValues();
11238 var o = this.options;
11239 var method = this.getMethod();
11240 var isPost = method == 'POST';
11241 if(o.clientValidation === false || this.form.isValid()){
11243 if (this.form.progressUrl) {
11244 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11245 (new Date() * 1) + '' + Math.random());
11250 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11251 form:this.form.el.dom,
11252 url:this.getUrl(!isPost),
11254 params:isPost ? this.getParams() : null,
11255 isUpload: this.form.fileUpload,
11256 formData : this.form.formData
11259 this.uploadProgress();
11261 }else if (o.clientValidation !== false){ // client validation failed
11262 this.failureType = Roo.form.Action.CLIENT_INVALID;
11263 this.form.afterAction(this, false);
11267 success : function(response)
11269 this.uploadComplete= true;
11270 if (this.haveProgress) {
11271 Roo.MessageBox.hide();
11275 var result = this.processResponse(response);
11276 if(result === true || result.success){
11277 this.form.afterAction(this, true);
11281 this.form.markInvalid(result.errors);
11282 this.failureType = Roo.form.Action.SERVER_INVALID;
11284 this.form.afterAction(this, false);
11286 failure : function(response)
11288 this.uploadComplete= true;
11289 if (this.haveProgress) {
11290 Roo.MessageBox.hide();
11293 this.response = response;
11294 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11295 this.form.afterAction(this, false);
11298 handleResponse : function(response){
11299 if(this.form.errorReader){
11300 var rs = this.form.errorReader.read(response);
11303 for(var i = 0, len = rs.records.length; i < len; i++) {
11304 var r = rs.records[i];
11305 errors[i] = r.data;
11308 if(errors.length < 1){
11312 success : rs.success,
11318 var rt = response.responseText;
11319 if (rt.match(/^\<!--\[CDATA\[/)) {
11320 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11321 rt = rt.replace(/\]\]--\>$/,'');
11324 ret = Roo.decode(rt);
11328 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11338 Roo.form.Action.Load = function(form, options){
11339 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11340 this.reader = this.form.reader;
11343 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11348 Roo.Ajax.request(Roo.apply(
11349 this.createCallback(), {
11350 method:this.getMethod(),
11351 url:this.getUrl(false),
11352 params:this.getParams()
11356 success : function(response){
11358 var result = this.processResponse(response);
11359 if(result === true || !result.success || !result.data){
11360 this.failureType = Roo.form.Action.LOAD_FAILURE;
11361 this.form.afterAction(this, false);
11364 this.form.clearInvalid();
11365 this.form.setValues(result.data);
11366 this.form.afterAction(this, true);
11369 handleResponse : function(response){
11370 if(this.form.reader){
11371 var rs = this.form.reader.read(response);
11372 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11374 success : rs.success,
11378 return Roo.decode(response.responseText);
11382 Roo.form.Action.ACTION_TYPES = {
11383 'load' : Roo.form.Action.Load,
11384 'submit' : Roo.form.Action.Submit
11393 * @class Roo.bootstrap.form.Form
11394 * @extends Roo.bootstrap.Component
11395 * @children Roo.bootstrap.Component
11396 * Bootstrap Form class
11397 * @cfg {String} method GET | POST (default POST)
11398 * @cfg {String} labelAlign top | left (default top)
11399 * @cfg {String} align left | right - for navbars
11400 * @cfg {Boolean} loadMask load mask when submit (default true)
11404 * Create a new Form
11405 * @param {Object} config The config object
11409 Roo.bootstrap.form.Form = function(config){
11411 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11413 Roo.bootstrap.form.Form.popover.apply();
11417 * @event clientvalidation
11418 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11419 * @param {Form} this
11420 * @param {Boolean} valid true if the form has passed client-side validation
11422 clientvalidation: true,
11424 * @event beforeaction
11425 * Fires before any action is performed. Return false to cancel the action.
11426 * @param {Form} this
11427 * @param {Action} action The action to be performed
11429 beforeaction: true,
11431 * @event actionfailed
11432 * Fires when an action fails.
11433 * @param {Form} this
11434 * @param {Action} action The action that failed
11436 actionfailed : true,
11438 * @event actioncomplete
11439 * Fires when an action is completed.
11440 * @param {Form} this
11441 * @param {Action} action The action that completed
11443 actioncomplete : true
11447 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11450 * @cfg {String} method
11451 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11455 * @cfg {String} url
11456 * The URL to use for form actions if one isn't supplied in the action options.
11459 * @cfg {Boolean} fileUpload
11460 * Set to true if this form is a file upload.
11464 * @cfg {Object} baseParams
11465 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11469 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11473 * @cfg {Sting} align (left|right) for navbar forms
11478 activeAction : null,
11481 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11482 * element by passing it or its id or mask the form itself by passing in true.
11485 waitMsgTarget : false,
11490 * @cfg {Boolean} errorMask (true|false) default false
11495 * @cfg {Number} maskOffset Default 100
11500 * @cfg {Boolean} maskBody
11504 getAutoCreate : function(){
11508 method : this.method || 'POST',
11509 id : this.id || Roo.id(),
11512 if (this.parent().xtype.match(/^Nav/)) {
11513 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11517 if (this.labelAlign == 'left' ) {
11518 cfg.cls += ' form-horizontal';
11524 initEvents : function()
11526 this.el.on('submit', this.onSubmit, this);
11527 // this was added as random key presses on the form where triggering form submit.
11528 this.el.on('keypress', function(e) {
11529 if (e.getCharCode() != 13) {
11532 // we might need to allow it for textareas.. and some other items.
11533 // check e.getTarget().
11535 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11539 Roo.log("keypress blocked");
11541 e.preventDefault();
11547 onSubmit : function(e){
11552 * Returns true if client-side validation on the form is successful.
11555 isValid : function(){
11556 var items = this.getItems();
11558 var target = false;
11560 items.each(function(f){
11566 Roo.log('invalid field: ' + f.name);
11570 if(!target && f.el.isVisible(true)){
11576 if(this.errorMask && !valid){
11577 Roo.bootstrap.form.Form.popover.mask(this, target);
11584 * Returns true if any fields in this form have changed since their original load.
11587 isDirty : function(){
11589 var items = this.getItems();
11590 items.each(function(f){
11600 * Performs a predefined action (submit or load) or custom actions you define on this form.
11601 * @param {String} actionName The name of the action type
11602 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11603 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11604 * accept other config options):
11606 Property Type Description
11607 ---------------- --------------- ----------------------------------------------------------------------------------
11608 url String The url for the action (defaults to the form's url)
11609 method String The form method to use (defaults to the form's method, or POST if not defined)
11610 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11611 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11612 validate the form on the client (defaults to false)
11614 * @return {BasicForm} this
11616 doAction : function(action, options){
11617 if(typeof action == 'string'){
11618 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11620 if(this.fireEvent('beforeaction', this, action) !== false){
11621 this.beforeAction(action);
11622 action.run.defer(100, action);
11628 beforeAction : function(action){
11629 var o = action.options;
11634 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11636 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11639 // not really supported yet.. ??
11641 //if(this.waitMsgTarget === true){
11642 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11643 //}else if(this.waitMsgTarget){
11644 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11645 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11647 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11653 afterAction : function(action, success){
11654 this.activeAction = null;
11655 var o = action.options;
11660 Roo.get(document.body).unmask();
11666 //if(this.waitMsgTarget === true){
11667 // this.el.unmask();
11668 //}else if(this.waitMsgTarget){
11669 // this.waitMsgTarget.unmask();
11671 // Roo.MessageBox.updateProgress(1);
11672 // Roo.MessageBox.hide();
11679 Roo.callback(o.success, o.scope, [this, action]);
11680 this.fireEvent('actioncomplete', this, action);
11684 // failure condition..
11685 // we have a scenario where updates need confirming.
11686 // eg. if a locking scenario exists..
11687 // we look for { errors : { needs_confirm : true }} in the response.
11689 (typeof(action.result) != 'undefined') &&
11690 (typeof(action.result.errors) != 'undefined') &&
11691 (typeof(action.result.errors.needs_confirm) != 'undefined')
11694 Roo.log("not supported yet");
11697 Roo.MessageBox.confirm(
11698 "Change requires confirmation",
11699 action.result.errorMsg,
11704 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11714 Roo.callback(o.failure, o.scope, [this, action]);
11715 // show an error message if no failed handler is set..
11716 if (!this.hasListener('actionfailed')) {
11717 Roo.log("need to add dialog support");
11719 Roo.MessageBox.alert("Error",
11720 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11721 action.result.errorMsg :
11722 "Saving Failed, please check your entries or try again"
11727 this.fireEvent('actionfailed', this, action);
11732 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11733 * @param {String} id The value to search for
11736 findField : function(id){
11737 var items = this.getItems();
11738 var field = items.get(id);
11740 items.each(function(f){
11741 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11748 return field || null;
11751 * Mark fields in this form invalid in bulk.
11752 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11753 * @return {BasicForm} this
11755 markInvalid : function(errors){
11756 if(errors instanceof Array){
11757 for(var i = 0, len = errors.length; i < len; i++){
11758 var fieldError = errors[i];
11759 var f = this.findField(fieldError.id);
11761 f.markInvalid(fieldError.msg);
11767 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11768 field.markInvalid(errors[id]);
11772 //Roo.each(this.childForms || [], function (f) {
11773 // f.markInvalid(errors);
11780 * Set values for fields in this form in bulk.
11781 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11782 * @return {BasicForm} this
11784 setValues : function(values){
11785 if(values instanceof Array){ // array of objects
11786 for(var i = 0, len = values.length; i < len; i++){
11788 var f = this.findField(v.id);
11790 f.setValue(v.value);
11791 if(this.trackResetOnLoad){
11792 f.originalValue = f.getValue();
11796 }else{ // object hash
11799 if(typeof values[id] != 'function' && (field = this.findField(id))){
11801 if (field.setFromData &&
11802 field.valueField &&
11803 field.displayField &&
11804 // combos' with local stores can
11805 // be queried via setValue()
11806 // to set their value..
11807 (field.store && !field.store.isLocal)
11811 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11812 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11813 field.setFromData(sd);
11815 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11817 field.setFromData(values);
11820 field.setValue(values[id]);
11824 if(this.trackResetOnLoad){
11825 field.originalValue = field.getValue();
11831 //Roo.each(this.childForms || [], function (f) {
11832 // f.setValues(values);
11839 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11840 * they are returned as an array.
11841 * @param {Boolean} asString
11844 getValues : function(asString){
11845 //if (this.childForms) {
11846 // copy values from the child forms
11847 // Roo.each(this.childForms, function (f) {
11848 // this.setValues(f.getValues());
11854 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11855 if(asString === true){
11858 return Roo.urlDecode(fs);
11862 * Returns the fields in this form as an object with key/value pairs.
11863 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11866 getFieldValues : function(with_hidden)
11868 var items = this.getItems();
11870 items.each(function(f){
11872 if (!f.getName()) {
11876 var v = f.getValue();
11878 if (f.inputType =='radio') {
11879 if (typeof(ret[f.getName()]) == 'undefined') {
11880 ret[f.getName()] = ''; // empty..
11883 if (!f.el.dom.checked) {
11887 v = f.el.dom.value;
11891 if(f.xtype == 'MoneyField'){
11892 ret[f.currencyName] = f.getCurrency();
11895 // not sure if this supported any more..
11896 if ((typeof(v) == 'object') && f.getRawValue) {
11897 v = f.getRawValue() ; // dates..
11899 // combo boxes where name != hiddenName...
11900 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11901 ret[f.name] = f.getRawValue();
11903 ret[f.getName()] = v;
11910 * Clears all invalid messages in this form.
11911 * @return {BasicForm} this
11913 clearInvalid : function(){
11914 var items = this.getItems();
11916 items.each(function(f){
11924 * Resets this form.
11925 * @return {BasicForm} this
11927 reset : function(){
11928 var items = this.getItems();
11929 items.each(function(f){
11933 Roo.each(this.childForms || [], function (f) {
11941 getItems : function()
11943 var r=new Roo.util.MixedCollection(false, function(o){
11944 return o.id || (o.id = Roo.id());
11946 var iter = function(el) {
11953 Roo.each(el.items,function(e) {
11962 hideFields : function(items)
11964 Roo.each(items, function(i){
11966 var f = this.findField(i);
11977 showFields : function(items)
11979 Roo.each(items, function(i){
11981 var f = this.findField(i);
11994 Roo.apply(Roo.bootstrap.form.Form, {
12010 intervalID : false,
12016 if(this.isApplied){
12021 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12022 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12023 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12024 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12027 this.maskEl.top.enableDisplayMode("block");
12028 this.maskEl.left.enableDisplayMode("block");
12029 this.maskEl.bottom.enableDisplayMode("block");
12030 this.maskEl.right.enableDisplayMode("block");
12032 this.toolTip = new Roo.bootstrap.Tooltip({
12033 cls : 'roo-form-error-popover',
12035 'left' : ['r-l', [-2,0], 'right'],
12036 'right' : ['l-r', [2,0], 'left'],
12037 'bottom' : ['tl-bl', [0,2], 'top'],
12038 'top' : [ 'bl-tl', [0,-2], 'bottom']
12042 this.toolTip.render(Roo.get(document.body));
12044 this.toolTip.el.enableDisplayMode("block");
12046 Roo.get(document.body).on('click', function(){
12050 Roo.get(document.body).on('touchstart', function(){
12054 this.isApplied = true
12057 mask : function(form, target)
12061 this.target = target;
12063 if(!this.form.errorMask || !target.el){
12067 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12069 Roo.log(scrollable);
12071 var ot = this.target.el.calcOffsetsTo(scrollable);
12073 var scrollTo = ot[1] - this.form.maskOffset;
12075 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12077 scrollable.scrollTo('top', scrollTo);
12079 var box = this.target.el.getBox();
12081 var zIndex = Roo.bootstrap.Modal.zIndex++;
12084 this.maskEl.top.setStyle('position', 'absolute');
12085 this.maskEl.top.setStyle('z-index', zIndex);
12086 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12087 this.maskEl.top.setLeft(0);
12088 this.maskEl.top.setTop(0);
12089 this.maskEl.top.show();
12091 this.maskEl.left.setStyle('position', 'absolute');
12092 this.maskEl.left.setStyle('z-index', zIndex);
12093 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12094 this.maskEl.left.setLeft(0);
12095 this.maskEl.left.setTop(box.y - this.padding);
12096 this.maskEl.left.show();
12098 this.maskEl.bottom.setStyle('position', 'absolute');
12099 this.maskEl.bottom.setStyle('z-index', zIndex);
12100 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12101 this.maskEl.bottom.setLeft(0);
12102 this.maskEl.bottom.setTop(box.bottom + this.padding);
12103 this.maskEl.bottom.show();
12105 this.maskEl.right.setStyle('position', 'absolute');
12106 this.maskEl.right.setStyle('z-index', zIndex);
12107 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12108 this.maskEl.right.setLeft(box.right + this.padding);
12109 this.maskEl.right.setTop(box.y - this.padding);
12110 this.maskEl.right.show();
12112 this.toolTip.bindEl = this.target.el;
12114 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12116 var tip = this.target.blankText;
12118 if(this.target.getValue() !== '' ) {
12120 if (this.target.invalidText.length) {
12121 tip = this.target.invalidText;
12122 } else if (this.target.regexText.length){
12123 tip = this.target.regexText;
12127 this.toolTip.show(tip);
12129 this.intervalID = window.setInterval(function() {
12130 Roo.bootstrap.form.Form.popover.unmask();
12133 window.onwheel = function(){ return false;};
12135 (function(){ this.isMasked = true; }).defer(500, this);
12139 unmask : function()
12141 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12145 this.maskEl.top.setStyle('position', 'absolute');
12146 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12147 this.maskEl.top.hide();
12149 this.maskEl.left.setStyle('position', 'absolute');
12150 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12151 this.maskEl.left.hide();
12153 this.maskEl.bottom.setStyle('position', 'absolute');
12154 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12155 this.maskEl.bottom.hide();
12157 this.maskEl.right.setStyle('position', 'absolute');
12158 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12159 this.maskEl.right.hide();
12161 this.toolTip.hide();
12163 this.toolTip.el.hide();
12165 window.onwheel = function(){ return true;};
12167 if(this.intervalID){
12168 window.clearInterval(this.intervalID);
12169 this.intervalID = false;
12172 this.isMasked = false;
12182 * Ext JS Library 1.1.1
12183 * Copyright(c) 2006-2007, Ext JS, LLC.
12185 * Originally Released Under LGPL - original licence link has changed is not relivant.
12188 * <script type="text/javascript">
12191 * @class Roo.form.VTypes
12192 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12195 Roo.form.VTypes = function(){
12196 // closure these in so they are only created once.
12197 var alpha = /^[a-zA-Z_]+$/;
12198 var alphanum = /^[a-zA-Z0-9_]+$/;
12199 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12200 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12202 // All these messages and functions are configurable
12205 * The function used to validate email addresses
12206 * @param {String} value The email address
12208 email : function(v){
12209 return email.test(v);
12212 * The error text to display when the email validation function returns false
12215 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12217 * The keystroke filter mask to be applied on email input
12220 emailMask : /[a-z0-9_\.\-@]/i,
12223 * The function used to validate URLs
12224 * @param {String} value The URL
12227 return url.test(v);
12230 * The error text to display when the url validation function returns false
12233 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12236 * The function used to validate alpha values
12237 * @param {String} value The value
12239 alpha : function(v){
12240 return alpha.test(v);
12243 * The error text to display when the alpha validation function returns false
12246 alphaText : 'This field should only contain letters and _',
12248 * The keystroke filter mask to be applied on alpha input
12251 alphaMask : /[a-z_]/i,
12254 * The function used to validate alphanumeric values
12255 * @param {String} value The value
12257 alphanum : function(v){
12258 return alphanum.test(v);
12261 * The error text to display when the alphanumeric validation function returns false
12264 alphanumText : 'This field should only contain letters, numbers and _',
12266 * The keystroke filter mask to be applied on alphanumeric input
12269 alphanumMask : /[a-z0-9_]/i
12279 * @class Roo.bootstrap.form.Input
12280 * @extends Roo.bootstrap.Component
12281 * Bootstrap Input class
12282 * @cfg {Boolean} disabled is it disabled
12283 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12284 * @cfg {String} name name of the input
12285 * @cfg {string} fieldLabel - the label associated
12286 * @cfg {string} placeholder - placeholder to put in text.
12287 * @cfg {string} before - input group add on before
12288 * @cfg {string} after - input group add on after
12289 * @cfg {string} size - (lg|sm) or leave empty..
12290 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12291 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12292 * @cfg {Number} md colspan out of 12 for computer-sized screens
12293 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12294 * @cfg {string} value default value of the input
12295 * @cfg {Number} labelWidth set the width of label
12296 * @cfg {Number} labellg set the width of label (1-12)
12297 * @cfg {Number} labelmd set the width of label (1-12)
12298 * @cfg {Number} labelsm set the width of label (1-12)
12299 * @cfg {Number} labelxs set the width of label (1-12)
12300 * @cfg {String} labelAlign (top|left)
12301 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12302 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12303 * @cfg {String} indicatorpos (left|right) default left
12304 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12305 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12306 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12307 * @cfg {Roo.bootstrap.Button} before Button to show before
12308 * @cfg {Roo.bootstrap.Button} afterButton to show before
12309 * @cfg {String} align (left|center|right) Default left
12310 * @cfg {Boolean} forceFeedback (true|false) Default false
12313 * Create a new Input
12314 * @param {Object} config The config object
12317 Roo.bootstrap.form.Input = function(config){
12319 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12324 * Fires when this field receives input focus.
12325 * @param {Roo.form.Field} this
12330 * Fires when this field loses input focus.
12331 * @param {Roo.form.Field} this
12335 * @event specialkey
12336 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12337 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12338 * @param {Roo.form.Field} this
12339 * @param {Roo.EventObject} e The event object
12344 * Fires just before the field blurs if the field value has changed.
12345 * @param {Roo.form.Field} this
12346 * @param {Mixed} newValue The new value
12347 * @param {Mixed} oldValue The original value
12352 * Fires after the field has been marked as invalid.
12353 * @param {Roo.form.Field} this
12354 * @param {String} msg The validation message
12359 * Fires after the field has been validated with no errors.
12360 * @param {Roo.form.Field} this
12365 * Fires after the key up
12366 * @param {Roo.form.Field} this
12367 * @param {Roo.EventObject} e The event Object
12372 * Fires after the user pastes into input
12373 * @param {Roo.form.Field} this
12374 * @param {Roo.EventObject} e The event Object
12380 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12382 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12383 automatic validation (defaults to "keyup").
12385 validationEvent : "keyup",
12387 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12389 validateOnBlur : true,
12391 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12393 validationDelay : 250,
12395 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12397 focusClass : "x-form-focus", // not needed???
12401 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12403 invalidClass : "has-warning",
12406 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12408 validClass : "has-success",
12411 * @cfg {Boolean} hasFeedback (true|false) default true
12413 hasFeedback : true,
12416 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12418 invalidFeedbackClass : "glyphicon-warning-sign",
12421 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12423 validFeedbackClass : "glyphicon-ok",
12426 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12428 selectOnFocus : false,
12431 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12435 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12440 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12442 disableKeyFilter : false,
12445 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12449 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12453 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12455 blankText : "Please complete this mandatory field",
12458 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12462 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12464 maxLength : Number.MAX_VALUE,
12466 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12468 minLengthText : "The minimum length for this field is {0}",
12470 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12472 maxLengthText : "The maximum length for this field is {0}",
12476 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12477 * If available, this function will be called only after the basic validators all return true, and will be passed the
12478 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12482 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12483 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12484 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12488 * @cfg {String} regexText -- Depricated - use Invalid Text
12493 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12499 autocomplete: false,
12503 inputType : 'text',
12506 placeholder: false,
12511 preventMark: false,
12512 isFormField : true,
12515 labelAlign : false,
12518 formatedValue : false,
12519 forceFeedback : false,
12521 indicatorpos : 'left',
12531 parentLabelAlign : function()
12534 while (parent.parent()) {
12535 parent = parent.parent();
12536 if (typeof(parent.labelAlign) !='undefined') {
12537 return parent.labelAlign;
12544 getAutoCreate : function()
12551 if(this.inputType != 'hidden'){
12552 cfg.cls = 'form-group' //input-group
12558 type : this.inputType,
12559 value : this.value,
12560 cls : 'form-control',
12561 placeholder : this.placeholder || '',
12562 autocomplete : this.autocomplete || 'new-password'
12564 if (this.inputType == 'file') {
12565 input.style = 'overflow:hidden'; // why not in CSS?
12568 if(this.capture.length){
12569 input.capture = this.capture;
12572 if(this.accept.length){
12573 input.accept = this.accept + "/*";
12577 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12580 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12581 input.maxLength = this.maxLength;
12584 if (this.disabled) {
12585 input.disabled=true;
12588 if (this.readOnly) {
12589 input.readonly=true;
12593 input.name = this.name;
12597 input.cls += ' input-' + this.size;
12601 ['xs','sm','md','lg'].map(function(size){
12602 if (settings[size]) {
12603 cfg.cls += ' col-' + size + '-' + settings[size];
12607 var inputblock = input;
12611 cls: 'glyphicon form-control-feedback'
12614 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12617 cls : 'has-feedback',
12625 if (this.before || this.after) {
12628 cls : 'input-group',
12632 if (this.before && typeof(this.before) == 'string') {
12634 inputblock.cn.push({
12636 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12640 if (this.before && typeof(this.before) == 'object') {
12641 this.before = Roo.factory(this.before);
12643 inputblock.cn.push({
12645 cls : 'roo-input-before input-group-prepend input-group-' +
12646 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12650 inputblock.cn.push(input);
12652 if (this.after && typeof(this.after) == 'string') {
12653 inputblock.cn.push({
12655 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12659 if (this.after && typeof(this.after) == 'object') {
12660 this.after = Roo.factory(this.after);
12662 inputblock.cn.push({
12664 cls : 'roo-input-after input-group-append input-group-' +
12665 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12669 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12670 inputblock.cls += ' has-feedback';
12671 inputblock.cn.push(feedback);
12677 cfg = this.getAutoCreateLabel( cfg, inputblock );
12682 if (this.parentType === 'Navbar' && this.parent().bar) {
12683 cfg.cls += ' navbar-form';
12686 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12687 // on BS4 we do this only if not form
12688 cfg.cls += ' navbar-form';
12696 * autocreate the label - also used by textara... ?? and others?
12698 getAutoCreateLabel : function( cfg, inputblock )
12700 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12704 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12705 tooltip : 'This field is required'
12707 if (this.allowBlank ) {
12708 indicator.style = this.allowBlank ? ' display:none' : '';
12710 if (align ==='left' && this.fieldLabel.length) {
12712 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12719 cls : 'control-label col-form-label',
12720 html : this.fieldLabel
12731 var labelCfg = cfg.cn[1];
12732 var contentCfg = cfg.cn[2];
12734 if(this.indicatorpos == 'right'){
12739 cls : 'control-label col-form-label',
12743 html : this.fieldLabel
12757 labelCfg = cfg.cn[0];
12758 contentCfg = cfg.cn[1];
12762 if(this.labelWidth > 12){
12763 labelCfg.style = "width: " + this.labelWidth + 'px';
12766 if(this.labelWidth < 13 && this.labelmd == 0){
12767 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12770 if(this.labellg > 0){
12771 labelCfg.cls += ' col-lg-' + this.labellg;
12772 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12775 if(this.labelmd > 0){
12776 labelCfg.cls += ' col-md-' + this.labelmd;
12777 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12780 if(this.labelsm > 0){
12781 labelCfg.cls += ' col-sm-' + this.labelsm;
12782 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12785 if(this.labelxs > 0){
12786 labelCfg.cls += ' col-xs-' + this.labelxs;
12787 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12791 } else if ( this.fieldLabel.length) {
12798 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12799 tooltip : 'This field is required',
12800 style : this.allowBlank ? ' display:none' : ''
12804 //cls : 'input-group-addon',
12805 html : this.fieldLabel
12813 if(this.indicatorpos == 'right'){
12818 //cls : 'input-group-addon',
12819 html : this.fieldLabel
12824 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12825 tooltip : 'This field is required',
12826 style : this.allowBlank ? ' display:none' : ''
12850 * return the real input element.
12852 inputEl: function ()
12854 return this.el.select('input.form-control',true).first();
12857 tooltipEl : function()
12859 return this.inputEl();
12862 indicatorEl : function()
12864 if (Roo.bootstrap.version == 4) {
12865 return false; // not enabled in v4 yet.
12868 var indicator = this.el.select('i.roo-required-indicator',true).first();
12878 setDisabled : function(v)
12880 var i = this.inputEl().dom;
12882 i.removeAttribute('disabled');
12886 i.setAttribute('disabled','true');
12888 initEvents : function()
12891 this.inputEl().on("keydown" , this.fireKey, this);
12892 this.inputEl().on("focus", this.onFocus, this);
12893 this.inputEl().on("blur", this.onBlur, this);
12895 this.inputEl().relayEvent('keyup', this);
12896 this.inputEl().relayEvent('paste', this);
12898 this.indicator = this.indicatorEl();
12900 if(this.indicator){
12901 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12904 // reference to original value for reset
12905 this.originalValue = this.getValue();
12906 //Roo.form.TextField.superclass.initEvents.call(this);
12907 if(this.validationEvent == 'keyup'){
12908 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12909 this.inputEl().on('keyup', this.filterValidation, this);
12911 else if(this.validationEvent !== false){
12912 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12915 if(this.selectOnFocus){
12916 this.on("focus", this.preFocus, this);
12919 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12920 this.inputEl().on("keypress", this.filterKeys, this);
12922 this.inputEl().relayEvent('keypress', this);
12925 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12926 this.el.on("click", this.autoSize, this);
12929 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12930 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12933 if (typeof(this.before) == 'object') {
12934 this.before.render(this.el.select('.roo-input-before',true).first());
12936 if (typeof(this.after) == 'object') {
12937 this.after.render(this.el.select('.roo-input-after',true).first());
12940 this.inputEl().on('change', this.onChange, this);
12943 filterValidation : function(e){
12944 if(!e.isNavKeyPress()){
12945 this.validationTask.delay(this.validationDelay);
12949 * Validates the field value
12950 * @return {Boolean} True if the value is valid, else false
12952 validate : function(){
12953 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12954 if(this.disabled || this.validateValue(this.getRawValue())){
12959 this.markInvalid();
12965 * Validates a value according to the field's validation rules and marks the field as invalid
12966 * if the validation fails
12967 * @param {Mixed} value The value to validate
12968 * @return {Boolean} True if the value is valid, else false
12970 validateValue : function(value)
12972 if(this.getVisibilityEl().hasClass('hidden')){
12976 if(value.length < 1) { // if it's blank
12977 if(this.allowBlank){
12983 if(value.length < this.minLength){
12986 if(value.length > this.maxLength){
12990 var vt = Roo.form.VTypes;
12991 if(!vt[this.vtype](value, this)){
12995 if(typeof this.validator == "function"){
12996 var msg = this.validator(value);
12997 if (typeof(msg) == 'string') {
12998 this.invalidText = msg;
13005 if(this.regex && !this.regex.test(value)){
13013 fireKey : function(e){
13014 //Roo.log('field ' + e.getKey());
13015 if(e.isNavKeyPress()){
13016 this.fireEvent("specialkey", this, e);
13019 focus : function (selectText){
13021 this.inputEl().focus();
13022 if(selectText === true){
13023 this.inputEl().dom.select();
13029 onFocus : function(){
13030 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13031 // this.el.addClass(this.focusClass);
13033 if(!this.hasFocus){
13034 this.hasFocus = true;
13035 this.startValue = this.getValue();
13036 this.fireEvent("focus", this);
13040 beforeBlur : Roo.emptyFn,
13044 onBlur : function(){
13046 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13047 //this.el.removeClass(this.focusClass);
13049 this.hasFocus = false;
13050 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13053 var v = this.getValue();
13054 if(String(v) !== String(this.startValue)){
13055 this.fireEvent('change', this, v, this.startValue);
13057 this.fireEvent("blur", this);
13060 onChange : function(e)
13062 var v = this.getValue();
13063 if(String(v) !== String(this.startValue)){
13064 this.fireEvent('change', this, v, this.startValue);
13070 * Resets the current field value to the originally loaded value and clears any validation messages
13072 reset : function(){
13073 this.setValue(this.originalValue);
13077 * Returns the name of the field
13078 * @return {Mixed} name The name field
13080 getName: function(){
13084 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13085 * @return {Mixed} value The field value
13087 getValue : function(){
13089 var v = this.inputEl().getValue();
13094 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13095 * @return {Mixed} value The field value
13097 getRawValue : function(){
13098 var v = this.inputEl().getValue();
13104 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13105 * @param {Mixed} value The value to set
13107 setRawValue : function(v){
13108 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13111 selectText : function(start, end){
13112 var v = this.getRawValue();
13114 start = start === undefined ? 0 : start;
13115 end = end === undefined ? v.length : end;
13116 var d = this.inputEl().dom;
13117 if(d.setSelectionRange){
13118 d.setSelectionRange(start, end);
13119 }else if(d.createTextRange){
13120 var range = d.createTextRange();
13121 range.moveStart("character", start);
13122 range.moveEnd("character", v.length-end);
13129 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13130 * @param {Mixed} value The value to set
13132 setValue : function(v){
13135 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13141 processValue : function(value){
13142 if(this.stripCharsRe){
13143 var newValue = value.replace(this.stripCharsRe, '');
13144 if(newValue !== value){
13145 this.setRawValue(newValue);
13152 preFocus : function(){
13154 if(this.selectOnFocus){
13155 this.inputEl().dom.select();
13158 filterKeys : function(e){
13159 var k = e.getKey();
13160 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13163 var c = e.getCharCode(), cc = String.fromCharCode(c);
13164 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13167 if(!this.maskRe.test(cc)){
13172 * Clear any invalid styles/messages for this field
13174 clearInvalid : function(){
13176 if(!this.el || this.preventMark){ // not rendered
13181 this.el.removeClass([this.invalidClass, 'is-invalid']);
13183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13185 var feedback = this.el.select('.form-control-feedback', true).first();
13188 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13193 if(this.indicator){
13194 this.indicator.removeClass('visible');
13195 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13198 this.fireEvent('valid', this);
13202 * Mark this field as valid
13204 markValid : function()
13206 if(!this.el || this.preventMark){ // not rendered...
13210 this.el.removeClass([this.invalidClass, this.validClass]);
13211 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13213 var feedback = this.el.select('.form-control-feedback', true).first();
13216 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13219 if(this.indicator){
13220 this.indicator.removeClass('visible');
13221 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13229 if(this.allowBlank && !this.getRawValue().length){
13232 if (Roo.bootstrap.version == 3) {
13233 this.el.addClass(this.validClass);
13235 this.inputEl().addClass('is-valid');
13238 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13240 var feedback = this.el.select('.form-control-feedback', true).first();
13243 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13249 this.fireEvent('valid', this);
13253 * Mark this field as invalid
13254 * @param {String} msg The validation message
13256 markInvalid : function(msg)
13258 if(!this.el || this.preventMark){ // not rendered
13262 this.el.removeClass([this.invalidClass, this.validClass]);
13263 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13265 var feedback = this.el.select('.form-control-feedback', true).first();
13268 this.el.select('.form-control-feedback', true).first().removeClass(
13269 [this.invalidFeedbackClass, this.validFeedbackClass]);
13276 if(this.allowBlank && !this.getRawValue().length){
13280 if(this.indicator){
13281 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13282 this.indicator.addClass('visible');
13284 if (Roo.bootstrap.version == 3) {
13285 this.el.addClass(this.invalidClass);
13287 this.inputEl().addClass('is-invalid');
13292 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13294 var feedback = this.el.select('.form-control-feedback', true).first();
13297 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13299 if(this.getValue().length || this.forceFeedback){
13300 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13307 this.fireEvent('invalid', this, msg);
13310 SafariOnKeyDown : function(event)
13312 // this is a workaround for a password hang bug on chrome/ webkit.
13313 if (this.inputEl().dom.type != 'password') {
13317 var isSelectAll = false;
13319 if(this.inputEl().dom.selectionEnd > 0){
13320 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13322 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13323 event.preventDefault();
13328 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13330 event.preventDefault();
13331 // this is very hacky as keydown always get's upper case.
13333 var cc = String.fromCharCode(event.getCharCode());
13334 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13338 adjustWidth : function(tag, w){
13339 tag = tag.toLowerCase();
13340 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13341 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13342 if(tag == 'input'){
13345 if(tag == 'textarea'){
13348 }else if(Roo.isOpera){
13349 if(tag == 'input'){
13352 if(tag == 'textarea'){
13360 setFieldLabel : function(v)
13362 if(!this.rendered){
13366 if(this.indicatorEl()){
13367 var ar = this.el.select('label > span',true);
13369 if (ar.elements.length) {
13370 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13371 this.fieldLabel = v;
13375 var br = this.el.select('label',true);
13377 if(br.elements.length) {
13378 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13379 this.fieldLabel = v;
13383 Roo.log('Cannot Found any of label > span || label in input');
13387 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13388 this.fieldLabel = v;
13403 * @class Roo.bootstrap.form.TextArea
13404 * @extends Roo.bootstrap.form.Input
13405 * Bootstrap TextArea class
13406 * @cfg {Number} cols Specifies the visible width of a text area
13407 * @cfg {Number} rows Specifies the visible number of lines in a text area
13408 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13409 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13410 * @cfg {string} html text
13413 * Create a new TextArea
13414 * @param {Object} config The config object
13417 Roo.bootstrap.form.TextArea = function(config){
13418 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13422 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13432 getAutoCreate : function(){
13434 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13440 if(this.inputType != 'hidden'){
13441 cfg.cls = 'form-group' //input-group
13449 value : this.value || '',
13450 html: this.html || '',
13451 cls : 'form-control',
13452 placeholder : this.placeholder || ''
13456 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13457 input.maxLength = this.maxLength;
13461 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13465 input.cols = this.cols;
13468 if (this.readOnly) {
13469 input.readonly = true;
13473 input.name = this.name;
13477 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13481 ['xs','sm','md','lg'].map(function(size){
13482 if (settings[size]) {
13483 cfg.cls += ' col-' + size + '-' + settings[size];
13487 var inputblock = input;
13489 if(this.hasFeedback && !this.allowBlank){
13493 cls: 'glyphicon form-control-feedback'
13497 cls : 'has-feedback',
13506 if (this.before || this.after) {
13509 cls : 'input-group',
13513 inputblock.cn.push({
13515 cls : 'input-group-addon',
13520 inputblock.cn.push(input);
13522 if(this.hasFeedback && !this.allowBlank){
13523 inputblock.cls += ' has-feedback';
13524 inputblock.cn.push(feedback);
13528 inputblock.cn.push({
13530 cls : 'input-group-addon',
13538 cfg = this.getAutoCreateLabel( cfg, inputblock );
13542 if (this.disabled) {
13543 input.disabled=true;
13550 * return the real textarea element.
13552 inputEl: function ()
13554 return this.el.select('textarea.form-control',true).first();
13558 * Clear any invalid styles/messages for this field
13560 clearInvalid : function()
13563 if(!this.el || this.preventMark){ // not rendered
13567 var label = this.el.select('label', true).first();
13568 var icon = this.el.select('i.fa-star', true).first();
13573 this.el.removeClass( this.validClass);
13574 this.inputEl().removeClass('is-invalid');
13576 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13578 var feedback = this.el.select('.form-control-feedback', true).first();
13581 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13586 this.fireEvent('valid', this);
13590 * Mark this field as valid
13592 markValid : function()
13594 if(!this.el || this.preventMark){ // not rendered
13598 this.el.removeClass([this.invalidClass, this.validClass]);
13599 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13601 var feedback = this.el.select('.form-control-feedback', true).first();
13604 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13607 if(this.disabled || this.allowBlank){
13611 var label = this.el.select('label', true).first();
13612 var icon = this.el.select('i.fa-star', true).first();
13617 if (Roo.bootstrap.version == 3) {
13618 this.el.addClass(this.validClass);
13620 this.inputEl().addClass('is-valid');
13624 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13626 var feedback = this.el.select('.form-control-feedback', true).first();
13629 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13630 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13635 this.fireEvent('valid', this);
13639 * Mark this field as invalid
13640 * @param {String} msg The validation message
13642 markInvalid : function(msg)
13644 if(!this.el || this.preventMark){ // not rendered
13648 this.el.removeClass([this.invalidClass, this.validClass]);
13649 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13651 var feedback = this.el.select('.form-control-feedback', true).first();
13654 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13657 if(this.disabled || this.allowBlank){
13661 var label = this.el.select('label', true).first();
13662 var icon = this.el.select('i.fa-star', true).first();
13664 if(!this.getValue().length && label && !icon){
13665 this.el.createChild({
13667 cls : 'text-danger fa fa-lg fa-star',
13668 tooltip : 'This field is required',
13669 style : 'margin-right:5px;'
13673 if (Roo.bootstrap.version == 3) {
13674 this.el.addClass(this.invalidClass);
13676 this.inputEl().addClass('is-invalid');
13679 // fixme ... this may be depricated need to test..
13680 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13682 var feedback = this.el.select('.form-control-feedback', true).first();
13685 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13687 if(this.getValue().length || this.forceFeedback){
13688 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13695 this.fireEvent('invalid', this, msg);
13703 * trigger field - base class for combo..
13708 * @class Roo.bootstrap.form.TriggerField
13709 * @extends Roo.bootstrap.form.Input
13710 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13711 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13712 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13713 * for which you can provide a custom implementation. For example:
13715 var trigger = new Roo.bootstrap.form.TriggerField();
13716 trigger.onTriggerClick = myTriggerFn;
13717 trigger.applyTo('my-field');
13720 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13721 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13722 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13723 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13724 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13727 * Create a new TriggerField.
13728 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13729 * to the base TextField)
13731 Roo.bootstrap.form.TriggerField = function(config){
13732 this.mimicing = false;
13733 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13736 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13738 * @cfg {String} triggerClass A CSS class to apply to the trigger
13741 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13746 * @cfg {Boolean} removable (true|false) special filter default false
13750 /** @cfg {Boolean} grow @hide */
13751 /** @cfg {Number} growMin @hide */
13752 /** @cfg {Number} growMax @hide */
13758 autoSize: Roo.emptyFn,
13762 deferHeight : true,
13765 actionMode : 'wrap',
13770 getAutoCreate : function(){
13772 var align = this.labelAlign || this.parentLabelAlign();
13777 cls: 'form-group' //input-group
13784 type : this.inputType,
13785 cls : 'form-control',
13786 autocomplete: 'new-password',
13787 placeholder : this.placeholder || ''
13791 input.name = this.name;
13794 input.cls += ' input-' + this.size;
13797 if (this.disabled) {
13798 input.disabled=true;
13801 var inputblock = input;
13803 if(this.hasFeedback && !this.allowBlank){
13807 cls: 'glyphicon form-control-feedback'
13810 if(this.removable && !this.editable ){
13812 cls : 'has-feedback',
13818 cls : 'roo-combo-removable-btn close'
13825 cls : 'has-feedback',
13834 if(this.removable && !this.editable ){
13836 cls : 'roo-removable',
13842 cls : 'roo-combo-removable-btn close'
13849 if (this.before || this.after) {
13852 cls : 'input-group',
13856 inputblock.cn.push({
13858 cls : 'input-group-addon input-group-prepend input-group-text',
13863 inputblock.cn.push(input);
13865 if(this.hasFeedback && !this.allowBlank){
13866 inputblock.cls += ' has-feedback';
13867 inputblock.cn.push(feedback);
13871 inputblock.cn.push({
13873 cls : 'input-group-addon input-group-append input-group-text',
13882 var ibwrap = inputblock;
13887 cls: 'roo-select2-choices',
13891 cls: 'roo-select2-search-field',
13903 cls: 'roo-select2-container input-group',
13908 cls: 'form-hidden-field'
13914 if(!this.multiple && this.showToggleBtn){
13920 if (this.caret != false) {
13923 cls: 'fa fa-' + this.caret
13930 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13932 Roo.bootstrap.version == 3 ? caret : '',
13935 cls: 'combobox-clear',
13949 combobox.cls += ' roo-select2-container-multi';
13953 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13954 tooltip : 'This field is required'
13957 if (this.allowBlank) {
13960 style : 'display:none'
13966 if (align ==='left' && this.fieldLabel.length) {
13968 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13975 cls : 'control-label',
13976 html : this.fieldLabel
13988 var labelCfg = cfg.cn[1];
13989 var contentCfg = cfg.cn[2];
13991 if(this.indicatorpos == 'right'){
13996 cls : 'control-label',
14000 html : this.fieldLabel
14014 labelCfg = cfg.cn[0];
14015 contentCfg = cfg.cn[1];
14018 if(this.labelWidth > 12){
14019 labelCfg.style = "width: " + this.labelWidth + 'px';
14022 if(this.labelWidth < 13 && this.labelmd == 0){
14023 this.labelmd = this.labelWidth;
14026 if(this.labellg > 0){
14027 labelCfg.cls += ' col-lg-' + this.labellg;
14028 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14031 if(this.labelmd > 0){
14032 labelCfg.cls += ' col-md-' + this.labelmd;
14033 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14036 if(this.labelsm > 0){
14037 labelCfg.cls += ' col-sm-' + this.labelsm;
14038 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14041 if(this.labelxs > 0){
14042 labelCfg.cls += ' col-xs-' + this.labelxs;
14043 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14046 } else if ( this.fieldLabel.length) {
14047 // Roo.log(" label");
14052 //cls : 'input-group-addon',
14053 html : this.fieldLabel
14061 if(this.indicatorpos == 'right'){
14069 html : this.fieldLabel
14083 // Roo.log(" no label && no align");
14090 ['xs','sm','md','lg'].map(function(size){
14091 if (settings[size]) {
14092 cfg.cls += ' col-' + size + '-' + settings[size];
14103 onResize : function(w, h){
14104 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14105 // if(typeof w == 'number'){
14106 // var x = w - this.trigger.getWidth();
14107 // this.inputEl().setWidth(this.adjustWidth('input', x));
14108 // this.trigger.setStyle('left', x+'px');
14113 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14116 getResizeEl : function(){
14117 return this.inputEl();
14121 getPositionEl : function(){
14122 return this.inputEl();
14126 alignErrorIcon : function(){
14127 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14131 initEvents : function(){
14135 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14136 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14137 if(!this.multiple && this.showToggleBtn){
14138 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14139 if(this.hideTrigger){
14140 this.trigger.setDisplayed(false);
14142 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14146 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14149 if(this.removable && !this.editable && !this.tickable){
14150 var close = this.closeTriggerEl();
14153 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14154 close.on('click', this.removeBtnClick, this, close);
14158 //this.trigger.addClassOnOver('x-form-trigger-over');
14159 //this.trigger.addClassOnClick('x-form-trigger-click');
14162 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14166 closeTriggerEl : function()
14168 var close = this.el.select('.roo-combo-removable-btn', true).first();
14169 return close ? close : false;
14172 removeBtnClick : function(e, h, el)
14174 e.preventDefault();
14176 if(this.fireEvent("remove", this) !== false){
14178 this.fireEvent("afterremove", this)
14182 createList : function()
14184 this.list = Roo.get(document.body).createChild({
14185 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14186 cls: 'typeahead typeahead-long dropdown-menu shadow',
14187 style: 'display:none'
14190 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14195 initTrigger : function(){
14200 onDestroy : function(){
14202 this.trigger.removeAllListeners();
14203 // this.trigger.remove();
14206 // this.wrap.remove();
14208 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14212 onFocus : function(){
14213 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14215 if(!this.mimicing){
14216 this.wrap.addClass('x-trigger-wrap-focus');
14217 this.mimicing = true;
14218 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14219 if(this.monitorTab){
14220 this.el.on("keydown", this.checkTab, this);
14227 checkTab : function(e){
14228 if(e.getKey() == e.TAB){
14229 this.triggerBlur();
14234 onBlur : function(){
14239 mimicBlur : function(e, t){
14241 if(!this.wrap.contains(t) && this.validateBlur()){
14242 this.triggerBlur();
14248 triggerBlur : function(){
14249 this.mimicing = false;
14250 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14251 if(this.monitorTab){
14252 this.el.un("keydown", this.checkTab, this);
14254 //this.wrap.removeClass('x-trigger-wrap-focus');
14255 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14259 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14260 validateBlur : function(e, t){
14265 onDisable : function(){
14266 this.inputEl().dom.disabled = true;
14267 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14269 // this.wrap.addClass('x-item-disabled');
14274 onEnable : function(){
14275 this.inputEl().dom.disabled = false;
14276 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14278 // this.el.removeClass('x-item-disabled');
14283 onShow : function(){
14284 var ae = this.getActionEl();
14287 ae.dom.style.display = '';
14288 ae.dom.style.visibility = 'visible';
14294 onHide : function(){
14295 var ae = this.getActionEl();
14296 ae.dom.style.display = 'none';
14300 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14301 * by an implementing function.
14303 * @param {EventObject} e
14305 onTriggerClick : Roo.emptyFn
14313 * @class Roo.bootstrap.form.CardUploader
14314 * @extends Roo.bootstrap.Button
14315 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14316 * @cfg {Number} errorTimeout default 3000
14317 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14318 * @cfg {Array} html The button text.
14322 * Create a new CardUploader
14323 * @param {Object} config The config object
14326 Roo.bootstrap.form.CardUploader = function(config){
14330 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14333 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14341 * When a image is clicked on - and needs to display a slideshow or similar..
14342 * @param {Roo.bootstrap.Card} this
14343 * @param {Object} The image information data
14349 * When a the download link is clicked
14350 * @param {Roo.bootstrap.Card} this
14351 * @param {Object} The image information data contains
14358 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14361 errorTimeout : 3000,
14365 fileCollection : false,
14368 getAutoCreate : function()
14372 cls :'form-group' ,
14377 //cls : 'input-group-addon',
14378 html : this.fieldLabel
14386 value : this.value,
14387 cls : 'd-none form-control'
14392 multiple : 'multiple',
14394 cls : 'd-none roo-card-upload-selector'
14398 cls : 'roo-card-uploader-button-container w-100 mb-2'
14401 cls : 'card-columns roo-card-uploader-container'
14411 getChildContainer : function() /// what children are added to.
14413 return this.containerEl;
14416 getButtonContainer : function() /// what children are added to.
14418 return this.el.select(".roo-card-uploader-button-container").first();
14421 initEvents : function()
14424 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14428 xns: Roo.bootstrap,
14431 container_method : 'getButtonContainer' ,
14432 html : this.html, // fix changable?
14435 'click' : function(btn, e) {
14444 this.urlAPI = (window.createObjectURL && window) ||
14445 (window.URL && URL.revokeObjectURL && URL) ||
14446 (window.webkitURL && webkitURL);
14451 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14453 this.selectorEl.on('change', this.onFileSelected, this);
14456 this.images.forEach(function(img) {
14459 this.images = false;
14461 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14467 onClick : function(e)
14469 e.preventDefault();
14471 this.selectorEl.dom.click();
14475 onFileSelected : function(e)
14477 e.preventDefault();
14479 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14483 Roo.each(this.selectorEl.dom.files, function(file){
14484 this.addFile(file);
14493 addFile : function(file)
14496 if(typeof(file) === 'string'){
14497 throw "Add file by name?"; // should not happen
14501 if(!file || !this.urlAPI){
14511 var url = _this.urlAPI.createObjectURL( file);
14514 id : Roo.bootstrap.form.CardUploader.ID--,
14515 is_uploaded : false,
14519 mimetype : file.type,
14527 * addCard - add an Attachment to the uploader
14528 * @param data - the data about the image to upload
14532 title : "Title of file",
14533 is_uploaded : false,
14534 src : "http://.....",
14535 srcfile : { the File upload object },
14536 mimetype : file.type,
14539 .. any other data...
14545 addCard : function (data)
14547 // hidden input element?
14548 // if the file is not an image...
14549 //then we need to use something other that and header_image
14554 xns : Roo.bootstrap,
14555 xtype : 'CardFooter',
14558 xns : Roo.bootstrap,
14564 xns : Roo.bootstrap,
14566 html : String.format("<small>{0}</small>", data.title),
14567 cls : 'col-10 text-left',
14572 click : function() {
14574 t.fireEvent( "download", t, data );
14580 xns : Roo.bootstrap,
14582 style: 'max-height: 28px; ',
14588 click : function() {
14589 t.removeCard(data.id)
14601 var cn = this.addxtype(
14604 xns : Roo.bootstrap,
14607 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14608 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14609 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14614 initEvents : function() {
14615 Roo.bootstrap.Card.prototype.initEvents.call(this);
14617 this.imgEl = this.el.select('.card-img-top').first();
14619 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14620 this.imgEl.set({ 'pointer' : 'cursor' });
14623 this.getCardFooter().addClass('p-1');
14630 // dont' really need ot update items.
14631 // this.items.push(cn);
14632 this.fileCollection.add(cn);
14634 if (!data.srcfile) {
14635 this.updateInput();
14640 var reader = new FileReader();
14641 reader.addEventListener("load", function() {
14642 data.srcdata = reader.result;
14645 reader.readAsDataURL(data.srcfile);
14650 removeCard : function(id)
14653 var card = this.fileCollection.get(id);
14654 card.data.is_deleted = 1;
14655 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14656 //this.fileCollection.remove(card);
14657 //this.items = this.items.filter(function(e) { return e != card });
14658 // dont' really need ot update items.
14659 card.el.dom.parentNode.removeChild(card.el.dom);
14660 this.updateInput();
14666 this.fileCollection.each(function(card) {
14667 if (card.el.dom && card.el.dom.parentNode) {
14668 card.el.dom.parentNode.removeChild(card.el.dom);
14671 this.fileCollection.clear();
14672 this.updateInput();
14675 updateInput : function()
14678 this.fileCollection.each(function(e) {
14682 this.inputEl().dom.value = JSON.stringify(data);
14692 Roo.bootstrap.form.CardUploader.ID = -1;/*
14694 * Ext JS Library 1.1.1
14695 * Copyright(c) 2006-2007, Ext JS, LLC.
14697 * Originally Released Under LGPL - original licence link has changed is not relivant.
14700 * <script type="text/javascript">
14705 * @class Roo.data.SortTypes
14707 * Defines the default sorting (casting?) comparison functions used when sorting data.
14709 Roo.data.SortTypes = {
14711 * Default sort that does nothing
14712 * @param {Mixed} s The value being converted
14713 * @return {Mixed} The comparison value
14715 none : function(s){
14720 * The regular expression used to strip tags
14724 stripTagsRE : /<\/?[^>]+>/gi,
14727 * Strips all HTML tags to sort on text only
14728 * @param {Mixed} s The value being converted
14729 * @return {String} The comparison value
14731 asText : function(s){
14732 return String(s).replace(this.stripTagsRE, "");
14736 * Strips all HTML tags to sort on text only - Case insensitive
14737 * @param {Mixed} s The value being converted
14738 * @return {String} The comparison value
14740 asUCText : function(s){
14741 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14745 * Case insensitive string
14746 * @param {Mixed} s The value being converted
14747 * @return {String} The comparison value
14749 asUCString : function(s) {
14750 return String(s).toUpperCase();
14755 * @param {Mixed} s The value being converted
14756 * @return {Number} The comparison value
14758 asDate : function(s) {
14762 if(s instanceof Date){
14763 return s.getTime();
14765 return Date.parse(String(s));
14770 * @param {Mixed} s The value being converted
14771 * @return {Float} The comparison value
14773 asFloat : function(s) {
14774 var val = parseFloat(String(s).replace(/,/g, ""));
14783 * @param {Mixed} s The value being converted
14784 * @return {Number} The comparison value
14786 asInt : function(s) {
14787 var val = parseInt(String(s).replace(/,/g, ""));
14795 * Ext JS Library 1.1.1
14796 * Copyright(c) 2006-2007, Ext JS, LLC.
14798 * Originally Released Under LGPL - original licence link has changed is not relivant.
14801 * <script type="text/javascript">
14805 * @class Roo.data.Record
14806 * Instances of this class encapsulate both record <em>definition</em> information, and record
14807 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14808 * to access Records cached in an {@link Roo.data.Store} object.<br>
14810 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14811 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14814 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14816 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14817 * {@link #create}. The parameters are the same.
14818 * @param {Array} data An associative Array of data values keyed by the field name.
14819 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14820 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14821 * not specified an integer id is generated.
14823 Roo.data.Record = function(data, id){
14824 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14829 * Generate a constructor for a specific record layout.
14830 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14831 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14832 * Each field definition object may contain the following properties: <ul>
14833 * <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,
14834 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14835 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14836 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14837 * is being used, then this is a string containing the javascript expression to reference the data relative to
14838 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14839 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14840 * this may be omitted.</p></li>
14841 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14842 * <ul><li>auto (Default, implies no conversion)</li>
14847 * <li>date</li></ul></p></li>
14848 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14849 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14850 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14851 * by the Reader into an object that will be stored in the Record. It is passed the
14852 * following parameters:<ul>
14853 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14855 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14857 * <br>usage:<br><pre><code>
14858 var TopicRecord = Roo.data.Record.create(
14859 {name: 'title', mapping: 'topic_title'},
14860 {name: 'author', mapping: 'username'},
14861 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14862 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14863 {name: 'lastPoster', mapping: 'user2'},
14864 {name: 'excerpt', mapping: 'post_text'}
14867 var myNewRecord = new TopicRecord({
14868 title: 'Do my job please',
14871 lastPost: new Date(),
14872 lastPoster: 'Animal',
14873 excerpt: 'No way dude!'
14875 myStore.add(myNewRecord);
14880 Roo.data.Record.create = function(o){
14881 var f = function(){
14882 f.superclass.constructor.apply(this, arguments);
14884 Roo.extend(f, Roo.data.Record);
14885 var p = f.prototype;
14886 p.fields = new Roo.util.MixedCollection(false, function(field){
14889 for(var i = 0, len = o.length; i < len; i++){
14890 p.fields.add(new Roo.data.Field(o[i]));
14892 f.getField = function(name){
14893 return p.fields.get(name);
14898 Roo.data.Record.AUTO_ID = 1000;
14899 Roo.data.Record.EDIT = 'edit';
14900 Roo.data.Record.REJECT = 'reject';
14901 Roo.data.Record.COMMIT = 'commit';
14903 Roo.data.Record.prototype = {
14905 * Readonly flag - true if this record has been modified.
14914 join : function(store){
14915 this.store = store;
14919 * Set the named field to the specified value.
14920 * @param {String} name The name of the field to set.
14921 * @param {Object} value The value to set the field to.
14923 set : function(name, value){
14924 if(this.data[name] == value){
14928 if(!this.modified){
14929 this.modified = {};
14931 if(typeof this.modified[name] == 'undefined'){
14932 this.modified[name] = this.data[name];
14934 this.data[name] = value;
14935 if(!this.editing && this.store){
14936 this.store.afterEdit(this);
14941 * Get the value of the named field.
14942 * @param {String} name The name of the field to get the value of.
14943 * @return {Object} The value of the field.
14945 get : function(name){
14946 return this.data[name];
14950 beginEdit : function(){
14951 this.editing = true;
14952 this.modified = {};
14956 cancelEdit : function(){
14957 this.editing = false;
14958 delete this.modified;
14962 endEdit : function(){
14963 this.editing = false;
14964 if(this.dirty && this.store){
14965 this.store.afterEdit(this);
14970 * Usually called by the {@link Roo.data.Store} which owns the Record.
14971 * Rejects all changes made to the Record since either creation, or the last commit operation.
14972 * Modified fields are reverted to their original values.
14974 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14975 * of reject operations.
14977 reject : function(){
14978 var m = this.modified;
14980 if(typeof m[n] != "function"){
14981 this.data[n] = m[n];
14984 this.dirty = false;
14985 delete this.modified;
14986 this.editing = false;
14988 this.store.afterReject(this);
14993 * Usually called by the {@link Roo.data.Store} which owns the Record.
14994 * Commits all changes made to the Record since either creation, or the last commit operation.
14996 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14997 * of commit operations.
14999 commit : function(){
15000 this.dirty = false;
15001 delete this.modified;
15002 this.editing = false;
15004 this.store.afterCommit(this);
15009 hasError : function(){
15010 return this.error != null;
15014 clearError : function(){
15019 * Creates a copy of this record.
15020 * @param {String} id (optional) A new record id if you don't want to use this record's id
15023 copy : function(newId) {
15024 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15028 * Ext JS Library 1.1.1
15029 * Copyright(c) 2006-2007, Ext JS, LLC.
15031 * Originally Released Under LGPL - original licence link has changed is not relivant.
15034 * <script type="text/javascript">
15040 * @class Roo.data.Store
15041 * @extends Roo.util.Observable
15042 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15043 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15045 * 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
15046 * has no knowledge of the format of the data returned by the Proxy.<br>
15048 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15049 * instances from the data object. These records are cached and made available through accessor functions.
15051 * Creates a new Store.
15052 * @param {Object} config A config object containing the objects needed for the Store to access data,
15053 * and read the data into Records.
15055 Roo.data.Store = function(config){
15056 this.data = new Roo.util.MixedCollection(false);
15057 this.data.getKey = function(o){
15060 this.baseParams = {};
15062 this.paramNames = {
15067 "multisort" : "_multisort"
15070 if(config && config.data){
15071 this.inlineData = config.data;
15072 delete config.data;
15075 Roo.apply(this, config);
15077 if(this.reader){ // reader passed
15078 this.reader = Roo.factory(this.reader, Roo.data);
15079 this.reader.xmodule = this.xmodule || false;
15080 if(!this.recordType){
15081 this.recordType = this.reader.recordType;
15083 if(this.reader.onMetaChange){
15084 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15088 if(this.recordType){
15089 this.fields = this.recordType.prototype.fields;
15091 this.modified = [];
15095 * @event datachanged
15096 * Fires when the data cache has changed, and a widget which is using this Store
15097 * as a Record cache should refresh its view.
15098 * @param {Store} this
15100 datachanged : true,
15102 * @event metachange
15103 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15104 * @param {Store} this
15105 * @param {Object} meta The JSON metadata
15110 * Fires when Records have been added to the Store
15111 * @param {Store} this
15112 * @param {Roo.data.Record[]} records The array of Records added
15113 * @param {Number} index The index at which the record(s) were added
15118 * Fires when a Record has been removed from the Store
15119 * @param {Store} this
15120 * @param {Roo.data.Record} record The Record that was removed
15121 * @param {Number} index The index at which the record was removed
15126 * Fires when a Record has been updated
15127 * @param {Store} this
15128 * @param {Roo.data.Record} record The Record that was updated
15129 * @param {String} operation The update operation being performed. Value may be one of:
15131 Roo.data.Record.EDIT
15132 Roo.data.Record.REJECT
15133 Roo.data.Record.COMMIT
15139 * Fires when the data cache has been cleared.
15140 * @param {Store} this
15144 * @event beforeload
15145 * Fires before a request is made for a new data object. If the beforeload handler returns false
15146 * the load action will be canceled.
15147 * @param {Store} this
15148 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15152 * @event beforeloadadd
15153 * Fires after a new set of Records has been loaded.
15154 * @param {Store} this
15155 * @param {Roo.data.Record[]} records The Records that were loaded
15156 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15158 beforeloadadd : true,
15161 * Fires after a new set of Records has been loaded, before they are added to the store.
15162 * @param {Store} this
15163 * @param {Roo.data.Record[]} records The Records that were loaded
15164 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15165 * @params {Object} return from reader
15169 * @event loadexception
15170 * Fires if an exception occurs in the Proxy during loading.
15171 * Called with the signature of the Proxy's "loadexception" event.
15172 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15175 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15176 * @param {Object} load options
15177 * @param {Object} jsonData from your request (normally this contains the Exception)
15179 loadexception : true
15183 this.proxy = Roo.factory(this.proxy, Roo.data);
15184 this.proxy.xmodule = this.xmodule || false;
15185 this.relayEvents(this.proxy, ["loadexception"]);
15187 this.sortToggle = {};
15188 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15190 Roo.data.Store.superclass.constructor.call(this);
15192 if(this.inlineData){
15193 this.loadData(this.inlineData);
15194 delete this.inlineData;
15198 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15200 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15201 * without a remote query - used by combo/forms at present.
15205 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15208 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15211 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15212 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15215 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15216 * on any HTTP request
15219 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15222 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15226 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15227 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15229 remoteSort : false,
15232 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15233 * loaded or when a record is removed. (defaults to false).
15235 pruneModifiedRecords : false,
15238 lastOptions : null,
15241 * Add Records to the Store and fires the add event.
15242 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15244 add : function(records){
15245 records = [].concat(records);
15246 for(var i = 0, len = records.length; i < len; i++){
15247 records[i].join(this);
15249 var index = this.data.length;
15250 this.data.addAll(records);
15251 this.fireEvent("add", this, records, index);
15255 * Remove a Record from the Store and fires the remove event.
15256 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15258 remove : function(record){
15259 var index = this.data.indexOf(record);
15260 this.data.removeAt(index);
15262 if(this.pruneModifiedRecords){
15263 this.modified.remove(record);
15265 this.fireEvent("remove", this, record, index);
15269 * Remove all Records from the Store and fires the clear event.
15271 removeAll : function(){
15273 if(this.pruneModifiedRecords){
15274 this.modified = [];
15276 this.fireEvent("clear", this);
15280 * Inserts Records to the Store at the given index and fires the add event.
15281 * @param {Number} index The start index at which to insert the passed Records.
15282 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15284 insert : function(index, records){
15285 records = [].concat(records);
15286 for(var i = 0, len = records.length; i < len; i++){
15287 this.data.insert(index, records[i]);
15288 records[i].join(this);
15290 this.fireEvent("add", this, records, index);
15294 * Get the index within the cache of the passed Record.
15295 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15296 * @return {Number} The index of the passed Record. Returns -1 if not found.
15298 indexOf : function(record){
15299 return this.data.indexOf(record);
15303 * Get the index within the cache of the Record with the passed id.
15304 * @param {String} id The id of the Record to find.
15305 * @return {Number} The index of the Record. Returns -1 if not found.
15307 indexOfId : function(id){
15308 return this.data.indexOfKey(id);
15312 * Get the Record with the specified id.
15313 * @param {String} id The id of the Record to find.
15314 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15316 getById : function(id){
15317 return this.data.key(id);
15321 * Get the Record at the specified index.
15322 * @param {Number} index The index of the Record to find.
15323 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15325 getAt : function(index){
15326 return this.data.itemAt(index);
15330 * Returns a range of Records between specified indices.
15331 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15332 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15333 * @return {Roo.data.Record[]} An array of Records
15335 getRange : function(start, end){
15336 return this.data.getRange(start, end);
15340 storeOptions : function(o){
15341 o = Roo.apply({}, o);
15344 this.lastOptions = o;
15348 * Loads the Record cache from the configured Proxy using the configured Reader.
15350 * If using remote paging, then the first load call must specify the <em>start</em>
15351 * and <em>limit</em> properties in the options.params property to establish the initial
15352 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15354 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15355 * and this call will return before the new data has been loaded. Perform any post-processing
15356 * in a callback function, or in a "load" event handler.</strong>
15358 * @param {Object} options An object containing properties which control loading options:<ul>
15359 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15360 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15363 data : data, // array of key=>value data like JsonReader
15364 total : data.length,
15370 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15371 * passed the following arguments:<ul>
15372 * <li>r : Roo.data.Record[]</li>
15373 * <li>options: Options object from the load call</li>
15374 * <li>success: Boolean success indicator</li></ul></li>
15375 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15376 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15379 load : function(options){
15380 options = options || {};
15381 if(this.fireEvent("beforeload", this, options) !== false){
15382 this.storeOptions(options);
15383 var p = Roo.apply(options.params || {}, this.baseParams);
15384 // if meta was not loaded from remote source.. try requesting it.
15385 if (!this.reader.metaFromRemote) {
15386 p._requestMeta = 1;
15388 if(this.sortInfo && this.remoteSort){
15389 var pn = this.paramNames;
15390 p[pn["sort"]] = this.sortInfo.field;
15391 p[pn["dir"]] = this.sortInfo.direction;
15393 if (this.multiSort) {
15394 var pn = this.paramNames;
15395 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15398 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15403 * Reloads the Record cache from the configured Proxy using the configured Reader and
15404 * the options from the last load operation performed.
15405 * @param {Object} options (optional) An object containing properties which may override the options
15406 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15407 * the most recently used options are reused).
15409 reload : function(options){
15410 this.load(Roo.applyIf(options||{}, this.lastOptions));
15414 // Called as a callback by the Reader during a load operation.
15415 loadRecords : function(o, options, success){
15418 if(success !== false){
15419 this.fireEvent("load", this, [], options, o);
15421 if(options.callback){
15422 options.callback.call(options.scope || this, [], options, false);
15426 // if data returned failure - throw an exception.
15427 if (o.success === false) {
15428 // show a message if no listener is registered.
15429 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15430 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15432 // loadmask wil be hooked into this..
15433 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15436 var r = o.records, t = o.totalRecords || r.length;
15438 this.fireEvent("beforeloadadd", this, r, options, o);
15440 if(!options || options.add !== true){
15441 if(this.pruneModifiedRecords){
15442 this.modified = [];
15444 for(var i = 0, len = r.length; i < len; i++){
15448 this.data = this.snapshot;
15449 delete this.snapshot;
15452 this.data.addAll(r);
15453 this.totalLength = t;
15455 this.fireEvent("datachanged", this);
15457 this.totalLength = Math.max(t, this.data.length+r.length);
15461 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15463 var e = new Roo.data.Record({});
15465 e.set(this.parent.displayField, this.parent.emptyTitle);
15466 e.set(this.parent.valueField, '');
15471 this.fireEvent("load", this, r, options, o);
15472 if(options.callback){
15473 options.callback.call(options.scope || this, r, options, true);
15479 * Loads data from a passed data block. A Reader which understands the format of the data
15480 * must have been configured in the constructor.
15481 * @param {Object} data The data block from which to read the Records. The format of the data expected
15482 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15483 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15485 loadData : function(o, append){
15486 var r = this.reader.readRecords(o);
15487 this.loadRecords(r, {add: append}, true);
15491 * using 'cn' the nested child reader read the child array into it's child stores.
15492 * @param {Object} rec The record with a 'children array
15494 loadDataFromChildren : function(rec)
15496 this.loadData(this.reader.toLoadData(rec));
15501 * Gets the number of cached records.
15503 * <em>If using paging, this may not be the total size of the dataset. If the data object
15504 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15505 * the data set size</em>
15507 getCount : function(){
15508 return this.data.length || 0;
15512 * Gets the total number of records in the dataset as returned by the server.
15514 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15515 * the dataset size</em>
15517 getTotalCount : function(){
15518 return this.totalLength || 0;
15522 * Returns the sort state of the Store as an object with two properties:
15524 field {String} The name of the field by which the Records are sorted
15525 direction {String} The sort order, "ASC" or "DESC"
15528 getSortState : function(){
15529 return this.sortInfo;
15533 applySort : function(){
15534 if(this.sortInfo && !this.remoteSort){
15535 var s = this.sortInfo, f = s.field;
15536 var st = this.fields.get(f).sortType;
15537 var fn = function(r1, r2){
15538 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15539 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15541 this.data.sort(s.direction, fn);
15542 if(this.snapshot && this.snapshot != this.data){
15543 this.snapshot.sort(s.direction, fn);
15549 * Sets the default sort column and order to be used by the next load operation.
15550 * @param {String} fieldName The name of the field to sort by.
15551 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15553 setDefaultSort : function(field, dir){
15554 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15558 * Sort the Records.
15559 * If remote sorting is used, the sort is performed on the server, and the cache is
15560 * reloaded. If local sorting is used, the cache is sorted internally.
15561 * @param {String} fieldName The name of the field to sort by.
15562 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15564 sort : function(fieldName, dir){
15565 var f = this.fields.get(fieldName);
15567 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15569 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15570 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15575 this.sortToggle[f.name] = dir;
15576 this.sortInfo = {field: f.name, direction: dir};
15577 if(!this.remoteSort){
15579 this.fireEvent("datachanged", this);
15581 this.load(this.lastOptions);
15586 * Calls the specified function for each of the Records in the cache.
15587 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15588 * Returning <em>false</em> aborts and exits the iteration.
15589 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15591 each : function(fn, scope){
15592 this.data.each(fn, scope);
15596 * Gets all records modified since the last commit. Modified records are persisted across load operations
15597 * (e.g., during paging).
15598 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15600 getModifiedRecords : function(){
15601 return this.modified;
15605 createFilterFn : function(property, value, anyMatch){
15606 if(!value.exec){ // not a regex
15607 value = String(value);
15608 if(value.length == 0){
15611 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15613 return function(r){
15614 return value.test(r.data[property]);
15619 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15620 * @param {String} property A field on your records
15621 * @param {Number} start The record index to start at (defaults to 0)
15622 * @param {Number} end The last record index to include (defaults to length - 1)
15623 * @return {Number} The sum
15625 sum : function(property, start, end){
15626 var rs = this.data.items, v = 0;
15627 start = start || 0;
15628 end = (end || end === 0) ? end : rs.length-1;
15630 for(var i = start; i <= end; i++){
15631 v += (rs[i].data[property] || 0);
15637 * Filter the records by a specified property.
15638 * @param {String} field A field on your records
15639 * @param {String/RegExp} value Either a string that the field
15640 * should start with or a RegExp to test against the field
15641 * @param {Boolean} anyMatch True to match any part not just the beginning
15643 filter : function(property, value, anyMatch){
15644 var fn = this.createFilterFn(property, value, anyMatch);
15645 return fn ? this.filterBy(fn) : this.clearFilter();
15649 * Filter by a function. The specified function will be called with each
15650 * record in this data source. If the function returns true the record is included,
15651 * otherwise it is filtered.
15652 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15653 * @param {Object} scope (optional) The scope of the function (defaults to this)
15655 filterBy : function(fn, scope){
15656 this.snapshot = this.snapshot || this.data;
15657 this.data = this.queryBy(fn, scope||this);
15658 this.fireEvent("datachanged", this);
15662 * Query the records by a specified property.
15663 * @param {String} field A field on your records
15664 * @param {String/RegExp} value Either a string that the field
15665 * should start with or a RegExp to test against the field
15666 * @param {Boolean} anyMatch True to match any part not just the beginning
15667 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15669 query : function(property, value, anyMatch){
15670 var fn = this.createFilterFn(property, value, anyMatch);
15671 return fn ? this.queryBy(fn) : this.data.clone();
15675 * Query by a function. The specified function will be called with each
15676 * record in this data source. If the function returns true the record is included
15678 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15679 * @param {Object} scope (optional) The scope of the function (defaults to this)
15680 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15682 queryBy : function(fn, scope){
15683 var data = this.snapshot || this.data;
15684 return data.filterBy(fn, scope||this);
15688 * Collects unique values for a particular dataIndex from this store.
15689 * @param {String} dataIndex The property to collect
15690 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15691 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15692 * @return {Array} An array of the unique values
15694 collect : function(dataIndex, allowNull, bypassFilter){
15695 var d = (bypassFilter === true && this.snapshot) ?
15696 this.snapshot.items : this.data.items;
15697 var v, sv, r = [], l = {};
15698 for(var i = 0, len = d.length; i < len; i++){
15699 v = d[i].data[dataIndex];
15701 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15710 * Revert to a view of the Record cache with no filtering applied.
15711 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15713 clearFilter : function(suppressEvent){
15714 if(this.snapshot && this.snapshot != this.data){
15715 this.data = this.snapshot;
15716 delete this.snapshot;
15717 if(suppressEvent !== true){
15718 this.fireEvent("datachanged", this);
15724 afterEdit : function(record){
15725 if(this.modified.indexOf(record) == -1){
15726 this.modified.push(record);
15728 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15732 afterReject : function(record){
15733 this.modified.remove(record);
15734 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15738 afterCommit : function(record){
15739 this.modified.remove(record);
15740 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15744 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15745 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15747 commitChanges : function(){
15748 var m = this.modified.slice(0);
15749 this.modified = [];
15750 for(var i = 0, len = m.length; i < len; i++){
15756 * Cancel outstanding changes on all changed records.
15758 rejectChanges : function(){
15759 var m = this.modified.slice(0);
15760 this.modified = [];
15761 for(var i = 0, len = m.length; i < len; i++){
15766 onMetaChange : function(meta, rtype, o){
15767 this.recordType = rtype;
15768 this.fields = rtype.prototype.fields;
15769 delete this.snapshot;
15770 this.sortInfo = meta.sortInfo || this.sortInfo;
15771 this.modified = [];
15772 this.fireEvent('metachange', this, this.reader.meta);
15775 moveIndex : function(data, type)
15777 var index = this.indexOf(data);
15779 var newIndex = index + type;
15783 this.insert(newIndex, data);
15788 * Ext JS Library 1.1.1
15789 * Copyright(c) 2006-2007, Ext JS, LLC.
15791 * Originally Released Under LGPL - original licence link has changed is not relivant.
15794 * <script type="text/javascript">
15798 * @class Roo.data.SimpleStore
15799 * @extends Roo.data.Store
15800 * Small helper class to make creating Stores from Array data easier.
15801 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15802 * @cfg {Array} fields An array of field definition objects, or field name strings.
15803 * @cfg {Object} an existing reader (eg. copied from another store)
15804 * @cfg {Array} data The multi-dimensional array of data
15805 * @cfg {Roo.data.DataProxy} proxy [not-required]
15806 * @cfg {Roo.data.Reader} reader [not-required]
15808 * @param {Object} config
15810 Roo.data.SimpleStore = function(config)
15812 Roo.data.SimpleStore.superclass.constructor.call(this, {
15814 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15817 Roo.data.Record.create(config.fields)
15819 proxy : new Roo.data.MemoryProxy(config.data)
15823 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15825 * Ext JS Library 1.1.1
15826 * Copyright(c) 2006-2007, Ext JS, LLC.
15828 * Originally Released Under LGPL - original licence link has changed is not relivant.
15831 * <script type="text/javascript">
15836 * @extends Roo.data.Store
15837 * @class Roo.data.JsonStore
15838 * Small helper class to make creating Stores for JSON data easier. <br/>
15840 var store = new Roo.data.JsonStore({
15841 url: 'get-images.php',
15843 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15846 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15847 * JsonReader and HttpProxy (unless inline data is provided).</b>
15848 * @cfg {Array} fields An array of field definition objects, or field name strings.
15850 * @param {Object} config
15852 Roo.data.JsonStore = function(c){
15853 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15854 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15855 reader: new Roo.data.JsonReader(c, c.fields)
15858 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15860 * Ext JS Library 1.1.1
15861 * Copyright(c) 2006-2007, Ext JS, LLC.
15863 * Originally Released Under LGPL - original licence link has changed is not relivant.
15866 * <script type="text/javascript">
15870 Roo.data.Field = function(config){
15871 if(typeof config == "string"){
15872 config = {name: config};
15874 Roo.apply(this, config);
15877 this.type = "auto";
15880 var st = Roo.data.SortTypes;
15881 // named sortTypes are supported, here we look them up
15882 if(typeof this.sortType == "string"){
15883 this.sortType = st[this.sortType];
15886 // set default sortType for strings and dates
15887 if(!this.sortType){
15890 this.sortType = st.asUCString;
15893 this.sortType = st.asDate;
15896 this.sortType = st.none;
15901 var stripRe = /[\$,%]/g;
15903 // prebuilt conversion function for this field, instead of
15904 // switching every time we're reading a value
15906 var cv, dateFormat = this.dateFormat;
15911 cv = function(v){ return v; };
15914 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15918 return v !== undefined && v !== null && v !== '' ?
15919 parseInt(String(v).replace(stripRe, ""), 10) : '';
15924 return v !== undefined && v !== null && v !== '' ?
15925 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15930 cv = function(v){ return v === true || v === "true" || v == 1; };
15937 if(v instanceof Date){
15941 if(dateFormat == "timestamp"){
15942 return new Date(v*1000);
15944 return Date.parseDate(v, dateFormat);
15946 var parsed = Date.parse(v);
15947 return parsed ? new Date(parsed) : null;
15956 Roo.data.Field.prototype = {
15964 * Ext JS Library 1.1.1
15965 * Copyright(c) 2006-2007, Ext JS, LLC.
15967 * Originally Released Under LGPL - original licence link has changed is not relivant.
15970 * <script type="text/javascript">
15973 // Base class for reading structured data from a data source. This class is intended to be
15974 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15977 * @class Roo.data.DataReader
15979 * Base class for reading structured data from a data source. This class is intended to be
15980 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15983 Roo.data.DataReader = function(meta, recordType){
15987 this.recordType = recordType instanceof Array ?
15988 Roo.data.Record.create(recordType) : recordType;
15991 Roo.data.DataReader.prototype = {
15994 readerType : 'Data',
15996 * Create an empty record
15997 * @param {Object} data (optional) - overlay some values
15998 * @return {Roo.data.Record} record created.
16000 newRow : function(d) {
16002 this.recordType.prototype.fields.each(function(c) {
16004 case 'int' : da[c.name] = 0; break;
16005 case 'date' : da[c.name] = new Date(); break;
16006 case 'float' : da[c.name] = 0.0; break;
16007 case 'boolean' : da[c.name] = false; break;
16008 default : da[c.name] = ""; break;
16012 return new this.recordType(Roo.apply(da, d));
16018 * Ext JS Library 1.1.1
16019 * Copyright(c) 2006-2007, Ext JS, LLC.
16021 * Originally Released Under LGPL - original licence link has changed is not relivant.
16024 * <script type="text/javascript">
16028 * @class Roo.data.DataProxy
16029 * @extends Roo.util.Observable
16031 * This class is an abstract base class for implementations which provide retrieval of
16032 * unformatted data objects.<br>
16034 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16035 * (of the appropriate type which knows how to parse the data object) to provide a block of
16036 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16038 * Custom implementations must implement the load method as described in
16039 * {@link Roo.data.HttpProxy#load}.
16041 Roo.data.DataProxy = function(){
16044 * @event beforeload
16045 * Fires before a network request is made to retrieve a data object.
16046 * @param {Object} This DataProxy object.
16047 * @param {Object} params The params parameter to the load function.
16052 * Fires before the load method's callback is called.
16053 * @param {Object} This DataProxy object.
16054 * @param {Object} o The data object.
16055 * @param {Object} arg The callback argument object passed to the load function.
16059 * @event loadexception
16060 * Fires if an Exception occurs during data retrieval.
16061 * @param {Object} This DataProxy object.
16062 * @param {Object} o The data object.
16063 * @param {Object} arg The callback argument object passed to the load function.
16064 * @param {Object} e The Exception.
16066 loadexception : true
16068 Roo.data.DataProxy.superclass.constructor.call(this);
16071 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16074 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16078 * Ext JS Library 1.1.1
16079 * Copyright(c) 2006-2007, Ext JS, LLC.
16081 * Originally Released Under LGPL - original licence link has changed is not relivant.
16084 * <script type="text/javascript">
16087 * @class Roo.data.MemoryProxy
16088 * @extends Roo.data.DataProxy
16089 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16090 * to the Reader when its load method is called.
16092 * @param {Object} config A config object containing the objects needed for the Store to access data,
16094 Roo.data.MemoryProxy = function(config){
16096 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16097 data = config.data;
16099 Roo.data.MemoryProxy.superclass.constructor.call(this);
16103 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16106 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16109 * Load data from the requested source (in this case an in-memory
16110 * data object passed to the constructor), read the data object into
16111 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16112 * process that block using the passed callback.
16113 * @param {Object} params This parameter is not used by the MemoryProxy class.
16114 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16115 * object into a block of Roo.data.Records.
16116 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16117 * The function must be passed <ul>
16118 * <li>The Record block object</li>
16119 * <li>The "arg" argument from the load function</li>
16120 * <li>A boolean success indicator</li>
16122 * @param {Object} scope The scope in which to call the callback
16123 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16125 load : function(params, reader, callback, scope, arg){
16126 params = params || {};
16129 result = reader.readRecords(params.data ? params.data :this.data);
16131 this.fireEvent("loadexception", this, arg, null, e);
16132 callback.call(scope, null, arg, false);
16135 callback.call(scope, result, arg, true);
16139 update : function(params, records){
16144 * Ext JS Library 1.1.1
16145 * Copyright(c) 2006-2007, Ext JS, LLC.
16147 * Originally Released Under LGPL - original licence link has changed is not relivant.
16150 * <script type="text/javascript">
16153 * @class Roo.data.HttpProxy
16154 * @extends Roo.data.DataProxy
16155 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16156 * configured to reference a certain URL.<br><br>
16158 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16159 * from which the running page was served.<br><br>
16161 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16163 * Be aware that to enable the browser to parse an XML document, the server must set
16164 * the Content-Type header in the HTTP response to "text/xml".
16166 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16167 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16168 * will be used to make the request.
16170 Roo.data.HttpProxy = function(conn){
16171 Roo.data.HttpProxy.superclass.constructor.call(this);
16172 // is conn a conn config or a real conn?
16174 this.useAjax = !conn || !conn.events;
16178 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16179 // thse are take from connection...
16182 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16185 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16186 * extra parameters to each request made by this object. (defaults to undefined)
16189 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16190 * to each request made by this object. (defaults to undefined)
16193 * @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)
16196 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16199 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16205 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16209 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16210 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16211 * a finer-grained basis than the DataProxy events.
16213 getConnection : function(){
16214 return this.useAjax ? Roo.Ajax : this.conn;
16218 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16219 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16220 * process that block using the passed callback.
16221 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16222 * for the request to the remote server.
16223 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16224 * object into a block of Roo.data.Records.
16225 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16226 * The function must be passed <ul>
16227 * <li>The Record block object</li>
16228 * <li>The "arg" argument from the load function</li>
16229 * <li>A boolean success indicator</li>
16231 * @param {Object} scope The scope in which to call the callback
16232 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16234 load : function(params, reader, callback, scope, arg){
16235 if(this.fireEvent("beforeload", this, params) !== false){
16237 params : params || {},
16239 callback : callback,
16244 callback : this.loadResponse,
16248 Roo.applyIf(o, this.conn);
16249 if(this.activeRequest){
16250 Roo.Ajax.abort(this.activeRequest);
16252 this.activeRequest = Roo.Ajax.request(o);
16254 this.conn.request(o);
16257 callback.call(scope||this, null, arg, false);
16262 loadResponse : function(o, success, response){
16263 delete this.activeRequest;
16265 this.fireEvent("loadexception", this, o, response);
16266 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16271 result = o.reader.read(response);
16274 o.raw = { errorMsg : response.responseText };
16275 this.fireEvent("loadexception", this, o, response, e);
16276 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16280 this.fireEvent("load", this, o, o.request.arg);
16281 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16285 update : function(dataSet){
16290 updateResponse : function(dataSet){
16295 * Ext JS Library 1.1.1
16296 * Copyright(c) 2006-2007, Ext JS, LLC.
16298 * Originally Released Under LGPL - original licence link has changed is not relivant.
16301 * <script type="text/javascript">
16305 * @class Roo.data.ScriptTagProxy
16306 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16307 * other than the originating domain of the running page.<br><br>
16309 * <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
16310 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16312 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16313 * source code that is used as the source inside a <script> tag.<br><br>
16315 * In order for the browser to process the returned data, the server must wrap the data object
16316 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16317 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16318 * depending on whether the callback name was passed:
16321 boolean scriptTag = false;
16322 String cb = request.getParameter("callback");
16325 response.setContentType("text/javascript");
16327 response.setContentType("application/x-json");
16329 Writer out = response.getWriter();
16331 out.write(cb + "(");
16333 out.print(dataBlock.toJsonString());
16340 * @param {Object} config A configuration object.
16342 Roo.data.ScriptTagProxy = function(config){
16343 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16344 Roo.apply(this, config);
16345 this.head = document.getElementsByTagName("head")[0];
16348 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16350 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16352 * @cfg {String} url The URL from which to request the data object.
16355 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16359 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16360 * the server the name of the callback function set up by the load call to process the returned data object.
16361 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16362 * javascript output which calls this named function passing the data object as its only parameter.
16364 callbackParam : "callback",
16366 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16367 * name to the request.
16372 * Load data from the configured URL, read the data object into
16373 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16374 * process that block using the passed callback.
16375 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16376 * for the request to the remote server.
16377 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16378 * object into a block of Roo.data.Records.
16379 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16380 * The function must be passed <ul>
16381 * <li>The Record block object</li>
16382 * <li>The "arg" argument from the load function</li>
16383 * <li>A boolean success indicator</li>
16385 * @param {Object} scope The scope in which to call the callback
16386 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16388 load : function(params, reader, callback, scope, arg){
16389 if(this.fireEvent("beforeload", this, params) !== false){
16391 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16393 var url = this.url;
16394 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16396 url += "&_dc=" + (new Date().getTime());
16398 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16401 cb : "stcCallback"+transId,
16402 scriptId : "stcScript"+transId,
16406 callback : callback,
16412 window[trans.cb] = function(o){
16413 conn.handleResponse(o, trans);
16416 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16418 if(this.autoAbort !== false){
16422 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16424 var script = document.createElement("script");
16425 script.setAttribute("src", url);
16426 script.setAttribute("type", "text/javascript");
16427 script.setAttribute("id", trans.scriptId);
16428 this.head.appendChild(script);
16430 this.trans = trans;
16432 callback.call(scope||this, null, arg, false);
16437 isLoading : function(){
16438 return this.trans ? true : false;
16442 * Abort the current server request.
16444 abort : function(){
16445 if(this.isLoading()){
16446 this.destroyTrans(this.trans);
16451 destroyTrans : function(trans, isLoaded){
16452 this.head.removeChild(document.getElementById(trans.scriptId));
16453 clearTimeout(trans.timeoutId);
16455 window[trans.cb] = undefined;
16457 delete window[trans.cb];
16460 // if hasn't been loaded, wait for load to remove it to prevent script error
16461 window[trans.cb] = function(){
16462 window[trans.cb] = undefined;
16464 delete window[trans.cb];
16471 handleResponse : function(o, trans){
16472 this.trans = false;
16473 this.destroyTrans(trans, true);
16476 result = trans.reader.readRecords(o);
16478 this.fireEvent("loadexception", this, o, trans.arg, e);
16479 trans.callback.call(trans.scope||window, null, trans.arg, false);
16482 this.fireEvent("load", this, o, trans.arg);
16483 trans.callback.call(trans.scope||window, result, trans.arg, true);
16487 handleFailure : function(trans){
16488 this.trans = false;
16489 this.destroyTrans(trans, false);
16490 this.fireEvent("loadexception", this, null, trans.arg);
16491 trans.callback.call(trans.scope||window, null, trans.arg, false);
16495 * Ext JS Library 1.1.1
16496 * Copyright(c) 2006-2007, Ext JS, LLC.
16498 * Originally Released Under LGPL - original licence link has changed is not relivant.
16501 * <script type="text/javascript">
16505 * @class Roo.data.JsonReader
16506 * @extends Roo.data.DataReader
16507 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16508 * based on mappings in a provided Roo.data.Record constructor.
16510 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16511 * in the reply previously.
16516 var RecordDef = Roo.data.Record.create([
16517 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16518 {name: 'occupation'} // This field will use "occupation" as the mapping.
16520 var myReader = new Roo.data.JsonReader({
16521 totalProperty: "results", // The property which contains the total dataset size (optional)
16522 root: "rows", // The property which contains an Array of row objects
16523 id: "id" // The property within each row object that provides an ID for the record (optional)
16527 * This would consume a JSON file like this:
16529 { 'results': 2, 'rows': [
16530 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16531 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16534 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16535 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16536 * paged from the remote server.
16537 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16538 * @cfg {String} root name of the property which contains the Array of row objects.
16539 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16540 * @cfg {Array} fields Array of field definition objects
16542 * Create a new JsonReader
16543 * @param {Object} meta Metadata configuration options
16544 * @param {Object} recordType Either an Array of field definition objects,
16545 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16547 Roo.data.JsonReader = function(meta, recordType){
16550 // set some defaults:
16551 Roo.applyIf(meta, {
16552 totalProperty: 'total',
16553 successProperty : 'success',
16558 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16560 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16562 readerType : 'Json',
16565 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16566 * Used by Store query builder to append _requestMeta to params.
16569 metaFromRemote : false,
16571 * This method is only used by a DataProxy which has retrieved data from a remote server.
16572 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16573 * @return {Object} data A data block which is used by an Roo.data.Store object as
16574 * a cache of Roo.data.Records.
16576 read : function(response){
16577 var json = response.responseText;
16579 var o = /* eval:var:o */ eval("("+json+")");
16581 throw {message: "JsonReader.read: Json object not found"};
16587 this.metaFromRemote = true;
16588 this.meta = o.metaData;
16589 this.recordType = Roo.data.Record.create(o.metaData.fields);
16590 this.onMetaChange(this.meta, this.recordType, o);
16592 return this.readRecords(o);
16595 // private function a store will implement
16596 onMetaChange : function(meta, recordType, o){
16603 simpleAccess: function(obj, subsc) {
16610 getJsonAccessor: function(){
16612 return function(expr) {
16614 return(re.test(expr))
16615 ? new Function("obj", "return obj." + expr)
16620 return Roo.emptyFn;
16625 * Create a data block containing Roo.data.Records from an XML document.
16626 * @param {Object} o An object which contains an Array of row objects in the property specified
16627 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16628 * which contains the total size of the dataset.
16629 * @return {Object} data A data block which is used by an Roo.data.Store object as
16630 * a cache of Roo.data.Records.
16632 readRecords : function(o){
16634 * After any data loads, the raw JSON data is available for further custom processing.
16638 var s = this.meta, Record = this.recordType,
16639 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16641 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16643 if(s.totalProperty) {
16644 this.getTotal = this.getJsonAccessor(s.totalProperty);
16646 if(s.successProperty) {
16647 this.getSuccess = this.getJsonAccessor(s.successProperty);
16649 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16651 var g = this.getJsonAccessor(s.id);
16652 this.getId = function(rec) {
16654 return (r === undefined || r === "") ? null : r;
16657 this.getId = function(){return null;};
16660 for(var jj = 0; jj < fl; jj++){
16662 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16663 this.ef[jj] = this.getJsonAccessor(map);
16667 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16668 if(s.totalProperty){
16669 var vt = parseInt(this.getTotal(o), 10);
16674 if(s.successProperty){
16675 var vs = this.getSuccess(o);
16676 if(vs === false || vs === 'false'){
16681 for(var i = 0; i < c; i++){
16684 var id = this.getId(n);
16685 for(var j = 0; j < fl; j++){
16687 var v = this.ef[j](n);
16689 Roo.log('missing convert for ' + f.name);
16693 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16697 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16703 var record = new Record(values, id);
16705 records[i] = record;
16711 totalRecords : totalRecords
16714 // used when loading children.. @see loadDataFromChildren
16715 toLoadData: function(rec)
16717 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16718 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16719 return { data : data, total : data.length };
16724 * Ext JS Library 1.1.1
16725 * Copyright(c) 2006-2007, Ext JS, LLC.
16727 * Originally Released Under LGPL - original licence link has changed is not relivant.
16730 * <script type="text/javascript">
16734 * @class Roo.data.ArrayReader
16735 * @extends Roo.data.DataReader
16736 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16737 * Each element of that Array represents a row of data fields. The
16738 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16739 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16743 var RecordDef = Roo.data.Record.create([
16744 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16745 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16747 var myReader = new Roo.data.ArrayReader({
16748 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16752 * This would consume an Array like this:
16754 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16758 * Create a new JsonReader
16759 * @param {Object} meta Metadata configuration options.
16760 * @param {Object|Array} recordType Either an Array of field definition objects
16762 * @cfg {Array} fields Array of field definition objects
16763 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16764 * as specified to {@link Roo.data.Record#create},
16765 * or an {@link Roo.data.Record} object
16768 * created using {@link Roo.data.Record#create}.
16770 Roo.data.ArrayReader = function(meta, recordType)
16772 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16775 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16778 * Create a data block containing Roo.data.Records from an XML document.
16779 * @param {Object} o An Array of row objects which represents the dataset.
16780 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16781 * a cache of Roo.data.Records.
16783 readRecords : function(o)
16785 var sid = this.meta ? this.meta.id : null;
16786 var recordType = this.recordType, fields = recordType.prototype.fields;
16789 for(var i = 0; i < root.length; i++){
16792 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16793 for(var j = 0, jlen = fields.length; j < jlen; j++){
16794 var f = fields.items[j];
16795 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16796 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16798 values[f.name] = v;
16800 var record = new recordType(values, id);
16802 records[records.length] = record;
16806 totalRecords : records.length
16809 // used when loading children.. @see loadDataFromChildren
16810 toLoadData: function(rec)
16812 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16813 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16824 * @class Roo.bootstrap.form.ComboBox
16825 * @extends Roo.bootstrap.form.TriggerField
16826 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16827 * @cfg {Boolean} append (true|false) default false
16828 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16829 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16830 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16831 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16832 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16833 * @cfg {Boolean} animate default true
16834 * @cfg {Boolean} emptyResultText only for touch device
16835 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16836 * @cfg {String} emptyTitle default ''
16837 * @cfg {Number} width fixed with? experimental
16839 * Create a new ComboBox.
16840 * @param {Object} config Configuration options
16842 Roo.bootstrap.form.ComboBox = function(config){
16843 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16847 * Fires when the dropdown list is expanded
16848 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16853 * Fires when the dropdown list is collapsed
16854 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16858 * @event beforeselect
16859 * Fires before a list item is selected. Return false to cancel the selection.
16860 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16861 * @param {Roo.data.Record} record The data record returned from the underlying store
16862 * @param {Number} index The index of the selected item in the dropdown list
16864 'beforeselect' : true,
16867 * Fires when a list item is selected
16868 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16869 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16870 * @param {Number} index The index of the selected item in the dropdown list
16874 * @event beforequery
16875 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16876 * The event object passed has these properties:
16877 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16878 * @param {String} query The query
16879 * @param {Boolean} forceAll true to force "all" query
16880 * @param {Boolean} cancel true to cancel the query
16881 * @param {Object} e The query event object
16883 'beforequery': true,
16886 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16887 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16893 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16894 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16899 * Fires when the remove value from the combobox array
16900 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16904 * @event afterremove
16905 * Fires when the remove value from the combobox array
16906 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16908 'afterremove' : true,
16910 * @event specialfilter
16911 * Fires when specialfilter
16912 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16914 'specialfilter' : true,
16917 * Fires when tick the element
16918 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16922 * @event touchviewdisplay
16923 * Fires when touch view require special display (default is using displayField)
16924 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16925 * @param {Object} cfg set html .
16927 'touchviewdisplay' : true
16932 this.tickItems = [];
16934 this.selectedIndex = -1;
16935 if(this.mode == 'local'){
16936 if(config.queryDelay === undefined){
16937 this.queryDelay = 10;
16939 if(config.minChars === undefined){
16945 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16948 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16949 * rendering into an Roo.Editor, defaults to false)
16952 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16953 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16956 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16959 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16960 * the dropdown list (defaults to undefined, with no header element)
16964 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16968 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16970 listWidth: undefined,
16972 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16973 * mode = 'remote' or 'text' if mode = 'local')
16975 displayField: undefined,
16978 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16979 * mode = 'remote' or 'value' if mode = 'local').
16980 * Note: use of a valueField requires the user make a selection
16981 * in order for a value to be mapped.
16983 valueField: undefined,
16985 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16990 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16991 * field's data value (defaults to the underlying DOM element's name)
16993 hiddenName: undefined,
16995 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16999 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17001 selectedClass: 'active',
17004 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17008 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17009 * anchor positions (defaults to 'tl-bl')
17011 listAlign: 'tl-bl?',
17013 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17017 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17018 * query specified by the allQuery config option (defaults to 'query')
17020 triggerAction: 'query',
17022 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17023 * (defaults to 4, does not apply if editable = false)
17027 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17028 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17032 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17033 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17037 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17038 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17042 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17043 * when editable = true (defaults to false)
17045 selectOnFocus:false,
17047 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17049 queryParam: 'query',
17051 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17052 * when mode = 'remote' (defaults to 'Loading...')
17054 loadingText: 'Loading...',
17056 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17060 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17064 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17065 * traditional select (defaults to true)
17069 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17073 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17077 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17078 * listWidth has a higher value)
17082 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17083 * allow the user to set arbitrary text into the field (defaults to false)
17085 forceSelection:false,
17087 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17088 * if typeAhead = true (defaults to 250)
17090 typeAheadDelay : 250,
17092 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17093 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17095 valueNotFoundText : undefined,
17097 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17099 blockFocus : false,
17102 * @cfg {Boolean} disableClear Disable showing of clear button.
17104 disableClear : false,
17106 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17108 alwaysQuery : false,
17111 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17116 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17118 invalidClass : "has-warning",
17121 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17123 validClass : "has-success",
17126 * @cfg {Boolean} specialFilter (true|false) special filter default false
17128 specialFilter : false,
17131 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17133 mobileTouchView : true,
17136 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17138 useNativeIOS : false,
17141 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17143 mobile_restrict_height : false,
17145 ios_options : false,
17157 btnPosition : 'right',
17158 triggerList : true,
17159 showToggleBtn : true,
17161 emptyResultText: 'Empty',
17162 triggerText : 'Select',
17166 // element that contains real text value.. (when hidden is used..)
17168 getAutoCreate : function()
17173 * Render classic select for iso
17176 if(Roo.isIOS && this.useNativeIOS){
17177 cfg = this.getAutoCreateNativeIOS();
17185 if(Roo.isTouch && this.mobileTouchView){
17186 cfg = this.getAutoCreateTouchView();
17193 if(!this.tickable){
17194 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17199 * ComboBox with tickable selections
17202 var align = this.labelAlign || this.parentLabelAlign();
17205 cls : 'form-group roo-combobox-tickable' //input-group
17208 var btn_text_select = '';
17209 var btn_text_done = '';
17210 var btn_text_cancel = '';
17212 if (this.btn_text_show) {
17213 btn_text_select = 'Select';
17214 btn_text_done = 'Done';
17215 btn_text_cancel = 'Cancel';
17220 cls : 'tickable-buttons',
17225 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17226 //html : this.triggerText
17227 html: btn_text_select
17233 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17235 html: btn_text_done
17241 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17243 html: btn_text_cancel
17249 buttons.cn.unshift({
17251 cls: 'roo-select2-search-field-input'
17257 Roo.each(buttons.cn, function(c){
17259 c.cls += ' btn-' + _this.size;
17262 if (_this.disabled) {
17269 style : 'display: contents',
17274 cls: 'form-hidden-field'
17278 cls: 'roo-select2-choices',
17282 cls: 'roo-select2-search-field',
17293 cls: 'roo-select2-container input-group roo-select2-container-multi',
17299 // cls: 'typeahead typeahead-long dropdown-menu',
17300 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17305 if(this.hasFeedback && !this.allowBlank){
17309 cls: 'glyphicon form-control-feedback'
17312 combobox.cn.push(feedback);
17319 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17320 tooltip : 'This field is required'
17323 if (this.allowBlank) {
17326 style : 'display:none'
17329 if (align ==='left' && this.fieldLabel.length) {
17331 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17338 cls : 'control-label col-form-label',
17339 html : this.fieldLabel
17351 var labelCfg = cfg.cn[1];
17352 var contentCfg = cfg.cn[2];
17355 if(this.indicatorpos == 'right'){
17361 cls : 'control-label col-form-label',
17365 html : this.fieldLabel
17381 labelCfg = cfg.cn[0];
17382 contentCfg = cfg.cn[1];
17386 if(this.labelWidth > 12){
17387 labelCfg.style = "width: " + this.labelWidth + 'px';
17389 if(this.width * 1 > 0){
17390 contentCfg.style = "width: " + this.width + 'px';
17392 if(this.labelWidth < 13 && this.labelmd == 0){
17393 this.labelmd = this.labelWidth;
17396 if(this.labellg > 0){
17397 labelCfg.cls += ' col-lg-' + this.labellg;
17398 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17401 if(this.labelmd > 0){
17402 labelCfg.cls += ' col-md-' + this.labelmd;
17403 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17406 if(this.labelsm > 0){
17407 labelCfg.cls += ' col-sm-' + this.labelsm;
17408 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17411 if(this.labelxs > 0){
17412 labelCfg.cls += ' col-xs-' + this.labelxs;
17413 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17417 } else if ( this.fieldLabel.length) {
17418 // Roo.log(" label");
17423 //cls : 'input-group-addon',
17424 html : this.fieldLabel
17429 if(this.indicatorpos == 'right'){
17433 //cls : 'input-group-addon',
17434 html : this.fieldLabel
17444 // Roo.log(" no label && no align");
17451 ['xs','sm','md','lg'].map(function(size){
17452 if (settings[size]) {
17453 cfg.cls += ' col-' + size + '-' + settings[size];
17461 _initEventsCalled : false,
17464 initEvents: function()
17466 if (this._initEventsCalled) { // as we call render... prevent looping...
17469 this._initEventsCalled = true;
17472 throw "can not find store for combo";
17475 this.indicator = this.indicatorEl();
17477 this.store = Roo.factory(this.store, Roo.data);
17478 this.store.parent = this;
17480 // if we are building from html. then this element is so complex, that we can not really
17481 // use the rendered HTML.
17482 // so we have to trash and replace the previous code.
17483 if (Roo.XComponent.build_from_html) {
17484 // remove this element....
17485 var e = this.el.dom, k=0;
17486 while (e ) { e = e.previousSibling; ++k;}
17491 this.rendered = false;
17493 this.render(this.parent().getChildContainer(true), k);
17496 if(Roo.isIOS && this.useNativeIOS){
17497 this.initIOSView();
17505 if(Roo.isTouch && this.mobileTouchView){
17506 this.initTouchView();
17511 this.initTickableEvents();
17515 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17517 if(this.hiddenName){
17519 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17521 this.hiddenField.dom.value =
17522 this.hiddenValue !== undefined ? this.hiddenValue :
17523 this.value !== undefined ? this.value : '';
17525 // prevent input submission
17526 this.el.dom.removeAttribute('name');
17527 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17532 // this.el.dom.setAttribute('autocomplete', 'off');
17535 var cls = 'x-combo-list';
17537 //this.list = new Roo.Layer({
17538 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17544 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17545 _this.list.setWidth(lw);
17548 this.list.on('mouseover', this.onViewOver, this);
17549 this.list.on('mousemove', this.onViewMove, this);
17550 this.list.on('scroll', this.onViewScroll, this);
17553 this.list.swallowEvent('mousewheel');
17554 this.assetHeight = 0;
17557 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17558 this.assetHeight += this.header.getHeight();
17561 this.innerList = this.list.createChild({cls:cls+'-inner'});
17562 this.innerList.on('mouseover', this.onViewOver, this);
17563 this.innerList.on('mousemove', this.onViewMove, this);
17564 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17566 if(this.allowBlank && !this.pageSize && !this.disableClear){
17567 this.footer = this.list.createChild({cls:cls+'-ft'});
17568 this.pageTb = new Roo.Toolbar(this.footer);
17572 this.footer = this.list.createChild({cls:cls+'-ft'});
17573 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17574 {pageSize: this.pageSize});
17578 if (this.pageTb && this.allowBlank && !this.disableClear) {
17580 this.pageTb.add(new Roo.Toolbar.Fill(), {
17581 cls: 'x-btn-icon x-btn-clear',
17583 handler: function()
17586 _this.clearValue();
17587 _this.onSelect(false, -1);
17592 this.assetHeight += this.footer.getHeight();
17597 this.tpl = Roo.bootstrap.version == 4 ?
17598 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17599 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17602 this.view = new Roo.View(this.list, this.tpl, {
17603 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17605 //this.view.wrapEl.setDisplayed(false);
17606 this.view.on('click', this.onViewClick, this);
17609 this.store.on('beforeload', this.onBeforeLoad, this);
17610 this.store.on('load', this.onLoad, this);
17611 this.store.on('loadexception', this.onLoadException, this);
17613 if(this.resizable){
17614 this.resizer = new Roo.Resizable(this.list, {
17615 pinned:true, handles:'se'
17617 this.resizer.on('resize', function(r, w, h){
17618 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17619 this.listWidth = w;
17620 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17621 this.restrictHeight();
17623 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17626 if(!this.editable){
17627 this.editable = true;
17628 this.setEditable(false);
17633 if (typeof(this.events.add.listeners) != 'undefined') {
17635 this.addicon = this.wrap.createChild(
17636 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17638 this.addicon.on('click', function(e) {
17639 this.fireEvent('add', this);
17642 if (typeof(this.events.edit.listeners) != 'undefined') {
17644 this.editicon = this.wrap.createChild(
17645 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17646 if (this.addicon) {
17647 this.editicon.setStyle('margin-left', '40px');
17649 this.editicon.on('click', function(e) {
17651 // we fire even if inothing is selected..
17652 this.fireEvent('edit', this, this.lastData );
17658 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17659 "up" : function(e){
17660 this.inKeyMode = true;
17664 "down" : function(e){
17665 if(!this.isExpanded()){
17666 this.onTriggerClick();
17668 this.inKeyMode = true;
17673 "enter" : function(e){
17674 // this.onViewClick();
17678 if(this.fireEvent("specialkey", this, e)){
17679 this.onViewClick(false);
17685 "esc" : function(e){
17689 "tab" : function(e){
17692 if(this.fireEvent("specialkey", this, e)){
17693 this.onViewClick(false);
17701 doRelay : function(foo, bar, hname){
17702 if(hname == 'down' || this.scope.isExpanded()){
17703 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17712 this.queryDelay = Math.max(this.queryDelay || 10,
17713 this.mode == 'local' ? 10 : 250);
17716 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17718 if(this.typeAhead){
17719 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17721 if(this.editable !== false){
17722 this.inputEl().on("keyup", this.onKeyUp, this);
17724 if(this.forceSelection){
17725 this.inputEl().on('blur', this.doForce, this);
17729 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17730 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17734 initTickableEvents: function()
17738 if(this.hiddenName){
17740 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17742 this.hiddenField.dom.value =
17743 this.hiddenValue !== undefined ? this.hiddenValue :
17744 this.value !== undefined ? this.value : '';
17746 // prevent input submission
17747 this.el.dom.removeAttribute('name');
17748 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17753 // this.list = this.el.select('ul.dropdown-menu',true).first();
17755 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17756 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17757 if(this.triggerList){
17758 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17761 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17762 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17764 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17765 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17767 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17768 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17770 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17771 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17772 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17775 this.cancelBtn.hide();
17780 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17781 _this.list.setWidth(lw);
17784 this.list.on('mouseover', this.onViewOver, this);
17785 this.list.on('mousemove', this.onViewMove, this);
17787 this.list.on('scroll', this.onViewScroll, this);
17790 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17791 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17794 this.view = new Roo.View(this.list, this.tpl, {
17799 selectedClass: this.selectedClass
17802 //this.view.wrapEl.setDisplayed(false);
17803 this.view.on('click', this.onViewClick, this);
17807 this.store.on('beforeload', this.onBeforeLoad, this);
17808 this.store.on('load', this.onLoad, this);
17809 this.store.on('loadexception', this.onLoadException, this);
17812 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17813 "up" : function(e){
17814 this.inKeyMode = true;
17818 "down" : function(e){
17819 this.inKeyMode = true;
17823 "enter" : function(e){
17824 if(this.fireEvent("specialkey", this, e)){
17825 this.onViewClick(false);
17831 "esc" : function(e){
17832 this.onTickableFooterButtonClick(e, false, false);
17835 "tab" : function(e){
17836 this.fireEvent("specialkey", this, e);
17838 this.onTickableFooterButtonClick(e, false, false);
17845 doRelay : function(e, fn, key){
17846 if(this.scope.isExpanded()){
17847 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17856 this.queryDelay = Math.max(this.queryDelay || 10,
17857 this.mode == 'local' ? 10 : 250);
17860 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17862 if(this.typeAhead){
17863 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17866 if(this.editable !== false){
17867 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17870 this.indicator = this.indicatorEl();
17872 if(this.indicator){
17873 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17874 this.indicator.hide();
17879 onDestroy : function(){
17881 this.view.setStore(null);
17882 this.view.el.removeAllListeners();
17883 this.view.el.remove();
17884 this.view.purgeListeners();
17887 this.list.dom.innerHTML = '';
17891 this.store.un('beforeload', this.onBeforeLoad, this);
17892 this.store.un('load', this.onLoad, this);
17893 this.store.un('loadexception', this.onLoadException, this);
17895 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17899 fireKey : function(e){
17900 if(e.isNavKeyPress() && !this.list.isVisible()){
17901 this.fireEvent("specialkey", this, e);
17906 onResize: function(w, h)
17910 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17912 // if(typeof w != 'number'){
17913 // // we do not handle it!?!?
17916 // var tw = this.trigger.getWidth();
17917 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17918 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17920 // this.inputEl().setWidth( this.adjustWidth('input', x));
17922 // //this.trigger.setStyle('left', x+'px');
17924 // if(this.list && this.listWidth === undefined){
17925 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17926 // this.list.setWidth(lw);
17927 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17935 * Allow or prevent the user from directly editing the field text. If false is passed,
17936 * the user will only be able to select from the items defined in the dropdown list. This method
17937 * is the runtime equivalent of setting the 'editable' config option at config time.
17938 * @param {Boolean} value True to allow the user to directly edit the field text
17940 setEditable : function(value){
17941 if(value == this.editable){
17944 this.editable = value;
17946 this.inputEl().dom.setAttribute('readOnly', true);
17947 this.inputEl().on('mousedown', this.onTriggerClick, this);
17948 this.inputEl().addClass('x-combo-noedit');
17950 this.inputEl().dom.removeAttribute('readOnly');
17951 this.inputEl().un('mousedown', this.onTriggerClick, this);
17952 this.inputEl().removeClass('x-combo-noedit');
17958 onBeforeLoad : function(combo,opts){
17959 if(!this.hasFocus){
17963 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17965 this.restrictHeight();
17966 this.selectedIndex = -1;
17970 onLoad : function(){
17972 this.hasQuery = false;
17974 if(!this.hasFocus){
17978 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17979 this.loading.hide();
17982 if(this.store.getCount() > 0){
17985 this.restrictHeight();
17986 if(this.lastQuery == this.allQuery){
17987 if(this.editable && !this.tickable){
17988 this.inputEl().dom.select();
17992 !this.selectByValue(this.value, true) &&
17995 !this.store.lastOptions ||
17996 typeof(this.store.lastOptions.add) == 'undefined' ||
17997 this.store.lastOptions.add != true
18000 this.select(0, true);
18003 if(this.autoFocus){
18006 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18007 this.taTask.delay(this.typeAheadDelay);
18011 this.onEmptyResults();
18017 onLoadException : function()
18019 this.hasQuery = false;
18021 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18022 this.loading.hide();
18025 if(this.tickable && this.editable){
18030 // only causes errors at present
18031 //Roo.log(this.store.reader.jsonData);
18032 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18034 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18040 onTypeAhead : function(){
18041 if(this.store.getCount() > 0){
18042 var r = this.store.getAt(0);
18043 var newValue = r.data[this.displayField];
18044 var len = newValue.length;
18045 var selStart = this.getRawValue().length;
18047 if(selStart != len){
18048 this.setRawValue(newValue);
18049 this.selectText(selStart, newValue.length);
18055 onSelect : function(record, index){
18057 if(this.fireEvent('beforeselect', this, record, index) !== false){
18059 this.setFromData(index > -1 ? record.data : false);
18062 this.fireEvent('select', this, record, index);
18067 * Returns the currently selected field value or empty string if no value is set.
18068 * @return {String} value The selected value
18070 getValue : function()
18072 if(Roo.isIOS && this.useNativeIOS){
18073 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18077 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18080 if(this.valueField){
18081 return typeof this.value != 'undefined' ? this.value : '';
18083 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18087 getRawValue : function()
18089 if(Roo.isIOS && this.useNativeIOS){
18090 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18093 var v = this.inputEl().getValue();
18099 * Clears any text/value currently set in the field
18101 clearValue : function(){
18103 if(this.hiddenField){
18104 this.hiddenField.dom.value = '';
18107 this.setRawValue('');
18108 this.lastSelectionText = '';
18109 this.lastData = false;
18111 var close = this.closeTriggerEl();
18122 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18123 * will be displayed in the field. If the value does not match the data value of an existing item,
18124 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18125 * Otherwise the field will be blank (although the value will still be set).
18126 * @param {String} value The value to match
18128 setValue : function(v)
18130 if(Roo.isIOS && this.useNativeIOS){
18131 this.setIOSValue(v);
18141 if(this.valueField){
18142 var r = this.findRecord(this.valueField, v);
18144 text = r.data[this.displayField];
18145 }else if(this.valueNotFoundText !== undefined){
18146 text = this.valueNotFoundText;
18149 this.lastSelectionText = text;
18150 if(this.hiddenField){
18151 this.hiddenField.dom.value = v;
18153 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18156 var close = this.closeTriggerEl();
18159 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18165 * @property {Object} the last set data for the element
18170 * Sets the value of the field based on a object which is related to the record format for the store.
18171 * @param {Object} value the value to set as. or false on reset?
18173 setFromData : function(o){
18180 var dv = ''; // display value
18181 var vv = ''; // value value..
18183 if (this.displayField) {
18184 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18186 // this is an error condition!!!
18187 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18190 if(this.valueField){
18191 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18194 var close = this.closeTriggerEl();
18197 if(dv.length || vv * 1 > 0){
18199 this.blockFocus=true;
18205 if(this.hiddenField){
18206 this.hiddenField.dom.value = vv;
18208 this.lastSelectionText = dv;
18209 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18213 // no hidden field.. - we store the value in 'value', but still display
18214 // display field!!!!
18215 this.lastSelectionText = dv;
18216 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18223 reset : function(){
18224 // overridden so that last data is reset..
18231 this.setValue(this.originalValue);
18232 //this.clearInvalid();
18233 this.lastData = false;
18235 this.view.clearSelections();
18241 findRecord : function(prop, value){
18243 if(this.store.getCount() > 0){
18244 this.store.each(function(r){
18245 if(r.data[prop] == value){
18255 getName: function()
18257 // returns hidden if it's set..
18258 if (!this.rendered) {return ''};
18259 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18263 onViewMove : function(e, t){
18264 this.inKeyMode = false;
18268 onViewOver : function(e, t){
18269 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18272 var item = this.view.findItemFromChild(t);
18275 var index = this.view.indexOf(item);
18276 this.select(index, false);
18281 onViewClick : function(view, doFocus, el, e)
18283 var index = this.view.getSelectedIndexes()[0];
18285 var r = this.store.getAt(index);
18289 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18296 Roo.each(this.tickItems, function(v,k){
18298 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18300 _this.tickItems.splice(k, 1);
18302 if(typeof(e) == 'undefined' && view == false){
18303 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18315 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18316 this.tickItems.push(r.data);
18319 if(typeof(e) == 'undefined' && view == false){
18320 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18327 this.onSelect(r, index);
18329 if(doFocus !== false && !this.blockFocus){
18330 this.inputEl().focus();
18335 restrictHeight : function(){
18336 //this.innerList.dom.style.height = '';
18337 //var inner = this.innerList.dom;
18338 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18339 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18340 //this.list.beginUpdate();
18341 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18342 this.list.alignTo(this.inputEl(), this.listAlign);
18343 this.list.alignTo(this.inputEl(), this.listAlign);
18344 //this.list.endUpdate();
18348 onEmptyResults : function(){
18350 if(this.tickable && this.editable){
18351 this.hasFocus = false;
18352 this.restrictHeight();
18360 * Returns true if the dropdown list is expanded, else false.
18362 isExpanded : function(){
18363 return this.list.isVisible();
18367 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18368 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18369 * @param {String} value The data value of the item to select
18370 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18371 * selected item if it is not currently in view (defaults to true)
18372 * @return {Boolean} True if the value matched an item in the list, else false
18374 selectByValue : function(v, scrollIntoView){
18375 if(v !== undefined && v !== null){
18376 var r = this.findRecord(this.valueField || this.displayField, v);
18378 this.select(this.store.indexOf(r), scrollIntoView);
18386 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18387 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18388 * @param {Number} index The zero-based index of the list item to select
18389 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18390 * selected item if it is not currently in view (defaults to true)
18392 select : function(index, scrollIntoView){
18393 this.selectedIndex = index;
18394 this.view.select(index);
18395 if(scrollIntoView !== false){
18396 var el = this.view.getNode(index);
18398 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18401 this.list.scrollChildIntoView(el, false);
18407 selectNext : function(){
18408 var ct = this.store.getCount();
18410 if(this.selectedIndex == -1){
18412 }else if(this.selectedIndex < ct-1){
18413 this.select(this.selectedIndex+1);
18419 selectPrev : function(){
18420 var ct = this.store.getCount();
18422 if(this.selectedIndex == -1){
18424 }else if(this.selectedIndex != 0){
18425 this.select(this.selectedIndex-1);
18431 onKeyUp : function(e){
18432 if(this.editable !== false && !e.isSpecialKey()){
18433 this.lastKey = e.getKey();
18434 this.dqTask.delay(this.queryDelay);
18439 validateBlur : function(){
18440 return !this.list || !this.list.isVisible();
18444 initQuery : function(){
18446 var v = this.getRawValue();
18448 if(this.tickable && this.editable){
18449 v = this.tickableInputEl().getValue();
18456 doForce : function(){
18457 if(this.inputEl().dom.value.length > 0){
18458 this.inputEl().dom.value =
18459 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18465 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18466 * query allowing the query action to be canceled if needed.
18467 * @param {String} query The SQL query to execute
18468 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18469 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18470 * saved in the current store (defaults to false)
18472 doQuery : function(q, forceAll){
18474 if(q === undefined || q === null){
18479 forceAll: forceAll,
18483 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18488 forceAll = qe.forceAll;
18489 if(forceAll === true || (q.length >= this.minChars)){
18491 this.hasQuery = true;
18493 if(this.lastQuery != q || this.alwaysQuery){
18494 this.lastQuery = q;
18495 if(this.mode == 'local'){
18496 this.selectedIndex = -1;
18498 this.store.clearFilter();
18501 if(this.specialFilter){
18502 this.fireEvent('specialfilter', this);
18507 this.store.filter(this.displayField, q);
18510 this.store.fireEvent("datachanged", this.store);
18517 this.store.baseParams[this.queryParam] = q;
18519 var options = {params : this.getParams(q)};
18522 options.add = true;
18523 options.params.start = this.page * this.pageSize;
18526 this.store.load(options);
18529 * this code will make the page width larger, at the beginning, the list not align correctly,
18530 * we should expand the list on onLoad
18531 * so command out it
18536 this.selectedIndex = -1;
18541 this.loadNext = false;
18545 getParams : function(q){
18547 //p[this.queryParam] = q;
18551 p.limit = this.pageSize;
18557 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18559 collapse : function(){
18560 if(!this.isExpanded()){
18566 this.hasFocus = false;
18570 this.cancelBtn.hide();
18571 this.trigger.show();
18574 this.tickableInputEl().dom.value = '';
18575 this.tickableInputEl().blur();
18580 Roo.get(document).un('mousedown', this.collapseIf, this);
18581 Roo.get(document).un('mousewheel', this.collapseIf, this);
18582 if (!this.editable) {
18583 Roo.get(document).un('keydown', this.listKeyPress, this);
18585 this.fireEvent('collapse', this);
18591 collapseIf : function(e){
18592 var in_combo = e.within(this.el);
18593 var in_list = e.within(this.list);
18594 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18596 if (in_combo || in_list || is_list) {
18597 //e.stopPropagation();
18602 this.onTickableFooterButtonClick(e, false, false);
18610 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18612 expand : function(){
18614 if(this.isExpanded() || !this.hasFocus){
18618 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18619 this.list.setWidth(lw);
18625 this.restrictHeight();
18629 this.tickItems = Roo.apply([], this.item);
18632 this.cancelBtn.show();
18633 this.trigger.hide();
18636 this.tickableInputEl().focus();
18641 Roo.get(document).on('mousedown', this.collapseIf, this);
18642 Roo.get(document).on('mousewheel', this.collapseIf, this);
18643 if (!this.editable) {
18644 Roo.get(document).on('keydown', this.listKeyPress, this);
18647 this.fireEvent('expand', this);
18651 // Implements the default empty TriggerField.onTriggerClick function
18652 onTriggerClick : function(e)
18654 Roo.log('trigger click');
18656 if(this.disabled || !this.triggerList){
18661 this.loadNext = false;
18663 if(this.isExpanded()){
18665 if (!this.blockFocus) {
18666 this.inputEl().focus();
18670 this.hasFocus = true;
18671 if(this.triggerAction == 'all') {
18672 this.doQuery(this.allQuery, true);
18674 this.doQuery(this.getRawValue());
18676 if (!this.blockFocus) {
18677 this.inputEl().focus();
18682 onTickableTriggerClick : function(e)
18689 this.loadNext = false;
18690 this.hasFocus = true;
18692 if(this.triggerAction == 'all') {
18693 this.doQuery(this.allQuery, true);
18695 this.doQuery(this.getRawValue());
18699 onSearchFieldClick : function(e)
18701 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18702 this.onTickableFooterButtonClick(e, false, false);
18706 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18711 this.loadNext = false;
18712 this.hasFocus = true;
18714 if(this.triggerAction == 'all') {
18715 this.doQuery(this.allQuery, true);
18717 this.doQuery(this.getRawValue());
18721 listKeyPress : function(e)
18723 //Roo.log('listkeypress');
18724 // scroll to first matching element based on key pres..
18725 if (e.isSpecialKey()) {
18728 var k = String.fromCharCode(e.getKey()).toUpperCase();
18731 var csel = this.view.getSelectedNodes();
18732 var cselitem = false;
18734 var ix = this.view.indexOf(csel[0]);
18735 cselitem = this.store.getAt(ix);
18736 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18742 this.store.each(function(v) {
18744 // start at existing selection.
18745 if (cselitem.id == v.id) {
18751 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18752 match = this.store.indexOf(v);
18758 if (match === false) {
18759 return true; // no more action?
18762 this.view.select(match);
18763 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18764 sn.scrollIntoView(sn.dom.parentNode, false);
18767 onViewScroll : function(e, t){
18769 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){
18773 this.hasQuery = true;
18775 this.loading = this.list.select('.loading', true).first();
18777 if(this.loading === null){
18778 this.list.createChild({
18780 cls: 'loading roo-select2-more-results roo-select2-active',
18781 html: 'Loading more results...'
18784 this.loading = this.list.select('.loading', true).first();
18786 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18788 this.loading.hide();
18791 this.loading.show();
18796 this.loadNext = true;
18798 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18803 addItem : function(o)
18805 var dv = ''; // display value
18807 if (this.displayField) {
18808 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18810 // this is an error condition!!!
18811 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18818 var choice = this.choices.createChild({
18820 cls: 'roo-select2-search-choice',
18829 cls: 'roo-select2-search-choice-close fa fa-times',
18834 }, this.searchField);
18836 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18838 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18846 this.inputEl().dom.value = '';
18851 onRemoveItem : function(e, _self, o)
18853 e.preventDefault();
18855 this.lastItem = Roo.apply([], this.item);
18857 var index = this.item.indexOf(o.data) * 1;
18860 Roo.log('not this item?!');
18864 this.item.splice(index, 1);
18869 this.fireEvent('remove', this, e);
18875 syncValue : function()
18877 if(!this.item.length){
18884 Roo.each(this.item, function(i){
18885 if(_this.valueField){
18886 value.push(i[_this.valueField]);
18893 this.value = value.join(',');
18895 if(this.hiddenField){
18896 this.hiddenField.dom.value = this.value;
18899 this.store.fireEvent("datachanged", this.store);
18904 clearItem : function()
18906 if(!this.multiple){
18912 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18920 if(this.tickable && !Roo.isTouch){
18921 this.view.refresh();
18925 inputEl: function ()
18927 if(Roo.isIOS && this.useNativeIOS){
18928 return this.el.select('select.roo-ios-select', true).first();
18931 if(Roo.isTouch && this.mobileTouchView){
18932 return this.el.select('input.form-control',true).first();
18936 return this.searchField;
18939 return this.el.select('input.form-control',true).first();
18942 onTickableFooterButtonClick : function(e, btn, el)
18944 e.preventDefault();
18946 this.lastItem = Roo.apply([], this.item);
18948 if(btn && btn.name == 'cancel'){
18949 this.tickItems = Roo.apply([], this.item);
18958 Roo.each(this.tickItems, function(o){
18966 validate : function()
18968 if(this.getVisibilityEl().hasClass('hidden')){
18972 var v = this.getRawValue();
18975 v = this.getValue();
18978 if(this.disabled || this.allowBlank || v.length){
18983 this.markInvalid();
18987 tickableInputEl : function()
18989 if(!this.tickable || !this.editable){
18990 return this.inputEl();
18993 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18997 getAutoCreateTouchView : function()
19002 cls: 'form-group' //input-group
19008 type : this.inputType,
19009 cls : 'form-control x-combo-noedit',
19010 autocomplete: 'new-password',
19011 placeholder : this.placeholder || '',
19016 input.name = this.name;
19020 input.cls += ' input-' + this.size;
19023 if (this.disabled) {
19024 input.disabled = true;
19028 cls : 'roo-combobox-wrap',
19035 inputblock.cls += ' input-group';
19037 inputblock.cn.unshift({
19039 cls : 'input-group-addon input-group-prepend input-group-text',
19044 if(this.removable && !this.multiple){
19045 inputblock.cls += ' roo-removable';
19047 inputblock.cn.push({
19050 cls : 'roo-combo-removable-btn close'
19054 if(this.hasFeedback && !this.allowBlank){
19056 inputblock.cls += ' has-feedback';
19058 inputblock.cn.push({
19060 cls: 'glyphicon form-control-feedback'
19067 inputblock.cls += (this.before) ? '' : ' input-group';
19069 inputblock.cn.push({
19071 cls : 'input-group-addon input-group-append input-group-text',
19077 var ibwrap = inputblock;
19082 cls: 'roo-select2-choices',
19086 cls: 'roo-select2-search-field',
19099 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19104 cls: 'form-hidden-field'
19110 if(!this.multiple && this.showToggleBtn){
19116 if (this.caret != false) {
19119 cls: 'fa fa-' + this.caret
19126 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19128 Roo.bootstrap.version == 3 ? caret : '',
19131 cls: 'combobox-clear',
19145 combobox.cls += ' roo-select2-container-multi';
19148 var required = this.allowBlank ? {
19150 style: 'display: none'
19153 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19154 tooltip : 'This field is required'
19157 var align = this.labelAlign || this.parentLabelAlign();
19159 if (align ==='left' && this.fieldLabel.length) {
19165 cls : 'control-label col-form-label',
19166 html : this.fieldLabel
19170 cls : 'roo-combobox-wrap ',
19177 var labelCfg = cfg.cn[1];
19178 var contentCfg = cfg.cn[2];
19181 if(this.indicatorpos == 'right'){
19186 cls : 'control-label col-form-label',
19190 html : this.fieldLabel
19196 cls : "roo-combobox-wrap ",
19204 labelCfg = cfg.cn[0];
19205 contentCfg = cfg.cn[1];
19210 if(this.labelWidth > 12){
19211 labelCfg.style = "width: " + this.labelWidth + 'px';
19214 if(this.labelWidth < 13 && this.labelmd == 0){
19215 this.labelmd = this.labelWidth;
19218 if(this.labellg > 0){
19219 labelCfg.cls += ' col-lg-' + this.labellg;
19220 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19223 if(this.labelmd > 0){
19224 labelCfg.cls += ' col-md-' + this.labelmd;
19225 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19228 if(this.labelsm > 0){
19229 labelCfg.cls += ' col-sm-' + this.labelsm;
19230 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19233 if(this.labelxs > 0){
19234 labelCfg.cls += ' col-xs-' + this.labelxs;
19235 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19239 } else if ( this.fieldLabel.length) {
19244 cls : 'control-label',
19245 html : this.fieldLabel
19256 if(this.indicatorpos == 'right'){
19260 cls : 'control-label',
19261 html : this.fieldLabel,
19279 var settings = this;
19281 ['xs','sm','md','lg'].map(function(size){
19282 if (settings[size]) {
19283 cfg.cls += ' col-' + size + '-' + settings[size];
19290 initTouchView : function()
19292 this.renderTouchView();
19294 this.touchViewEl.on('scroll', function(){
19295 this.el.dom.scrollTop = 0;
19298 this.originalValue = this.getValue();
19300 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19302 this.inputEl().on("click", this.showTouchView, this);
19303 if (this.triggerEl) {
19304 this.triggerEl.on("click", this.showTouchView, this);
19308 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19309 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19311 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19313 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19314 this.store.on('load', this.onTouchViewLoad, this);
19315 this.store.on('loadexception', this.onTouchViewLoadException, this);
19317 if(this.hiddenName){
19319 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19321 this.hiddenField.dom.value =
19322 this.hiddenValue !== undefined ? this.hiddenValue :
19323 this.value !== undefined ? this.value : '';
19325 this.el.dom.removeAttribute('name');
19326 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19330 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19331 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19334 if(this.removable && !this.multiple){
19335 var close = this.closeTriggerEl();
19337 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19338 close.on('click', this.removeBtnClick, this, close);
19342 * fix the bug in Safari iOS8
19344 this.inputEl().on("focus", function(e){
19345 document.activeElement.blur();
19348 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19355 renderTouchView : function()
19357 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19358 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19360 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19361 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19363 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19364 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19365 this.touchViewBodyEl.setStyle('overflow', 'auto');
19367 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19368 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19370 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19371 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19375 showTouchView : function()
19381 this.touchViewHeaderEl.hide();
19383 if(this.modalTitle.length){
19384 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19385 this.touchViewHeaderEl.show();
19388 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19389 this.touchViewEl.show();
19391 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19393 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19394 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19396 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19398 if(this.modalTitle.length){
19399 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19402 this.touchViewBodyEl.setHeight(bodyHeight);
19406 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19408 this.touchViewEl.addClass(['in','show']);
19411 if(this._touchViewMask){
19412 Roo.get(document.body).addClass("x-body-masked");
19413 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19414 this._touchViewMask.setStyle('z-index', 10000);
19415 this._touchViewMask.addClass('show');
19418 this.doTouchViewQuery();
19422 hideTouchView : function()
19424 this.touchViewEl.removeClass(['in','show']);
19428 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19430 this.touchViewEl.setStyle('display', 'none');
19433 if(this._touchViewMask){
19434 this._touchViewMask.removeClass('show');
19435 Roo.get(document.body).removeClass("x-body-masked");
19439 setTouchViewValue : function()
19446 Roo.each(this.tickItems, function(o){
19451 this.hideTouchView();
19454 doTouchViewQuery : function()
19463 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19467 if(!this.alwaysQuery || this.mode == 'local'){
19468 this.onTouchViewLoad();
19475 onTouchViewBeforeLoad : function(combo,opts)
19481 onTouchViewLoad : function()
19483 if(this.store.getCount() < 1){
19484 this.onTouchViewEmptyResults();
19488 this.clearTouchView();
19490 var rawValue = this.getRawValue();
19492 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19494 this.tickItems = [];
19496 this.store.data.each(function(d, rowIndex){
19497 var row = this.touchViewListGroup.createChild(template);
19499 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19500 row.addClass(d.data.cls);
19503 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19506 html : d.data[this.displayField]
19509 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19510 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19513 row.removeClass('selected');
19514 if(!this.multiple && this.valueField &&
19515 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19518 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19519 row.addClass('selected');
19522 if(this.multiple && this.valueField &&
19523 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19527 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19528 this.tickItems.push(d.data);
19531 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19535 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19537 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19539 if(this.modalTitle.length){
19540 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19543 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19545 if(this.mobile_restrict_height && listHeight < bodyHeight){
19546 this.touchViewBodyEl.setHeight(listHeight);
19551 if(firstChecked && listHeight > bodyHeight){
19552 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19557 onTouchViewLoadException : function()
19559 this.hideTouchView();
19562 onTouchViewEmptyResults : function()
19564 this.clearTouchView();
19566 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19568 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19572 clearTouchView : function()
19574 this.touchViewListGroup.dom.innerHTML = '';
19577 onTouchViewClick : function(e, el, o)
19579 e.preventDefault();
19582 var rowIndex = o.rowIndex;
19584 var r = this.store.getAt(rowIndex);
19586 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19588 if(!this.multiple){
19589 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19590 c.dom.removeAttribute('checked');
19593 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19595 this.setFromData(r.data);
19597 var close = this.closeTriggerEl();
19603 this.hideTouchView();
19605 this.fireEvent('select', this, r, rowIndex);
19610 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19611 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19612 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19616 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19617 this.addItem(r.data);
19618 this.tickItems.push(r.data);
19622 getAutoCreateNativeIOS : function()
19625 cls: 'form-group' //input-group,
19630 cls : 'roo-ios-select'
19634 combobox.name = this.name;
19637 if (this.disabled) {
19638 combobox.disabled = true;
19641 var settings = this;
19643 ['xs','sm','md','lg'].map(function(size){
19644 if (settings[size]) {
19645 cfg.cls += ' col-' + size + '-' + settings[size];
19655 initIOSView : function()
19657 this.store.on('load', this.onIOSViewLoad, this);
19662 onIOSViewLoad : function()
19664 if(this.store.getCount() < 1){
19668 this.clearIOSView();
19670 if(this.allowBlank) {
19672 var default_text = '-- SELECT --';
19674 if(this.placeholder.length){
19675 default_text = this.placeholder;
19678 if(this.emptyTitle.length){
19679 default_text += ' - ' + this.emptyTitle + ' -';
19682 var opt = this.inputEl().createChild({
19685 html : default_text
19689 o[this.valueField] = 0;
19690 o[this.displayField] = default_text;
19692 this.ios_options.push({
19699 this.store.data.each(function(d, rowIndex){
19703 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19704 html = d.data[this.displayField];
19709 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19710 value = d.data[this.valueField];
19719 if(this.value == d.data[this.valueField]){
19720 option['selected'] = true;
19723 var opt = this.inputEl().createChild(option);
19725 this.ios_options.push({
19732 this.inputEl().on('change', function(){
19733 this.fireEvent('select', this);
19738 clearIOSView: function()
19740 this.inputEl().dom.innerHTML = '';
19742 this.ios_options = [];
19745 setIOSValue: function(v)
19749 if(!this.ios_options){
19753 Roo.each(this.ios_options, function(opts){
19755 opts.el.dom.removeAttribute('selected');
19757 if(opts.data[this.valueField] != v){
19761 opts.el.dom.setAttribute('selected', true);
19767 * @cfg {Boolean} grow
19771 * @cfg {Number} growMin
19775 * @cfg {Number} growMax
19784 Roo.apply(Roo.bootstrap.form.ComboBox, {
19788 cls: 'modal-header',
19810 cls: 'list-group-item',
19814 cls: 'roo-combobox-list-group-item-value'
19818 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19832 listItemCheckbox : {
19834 cls: 'list-group-item',
19838 cls: 'roo-combobox-list-group-item-value'
19842 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19858 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19863 cls: 'modal-footer',
19871 cls: 'col-xs-6 text-left',
19874 cls: 'btn btn-danger roo-touch-view-cancel',
19880 cls: 'col-xs-6 text-right',
19883 cls: 'btn btn-success roo-touch-view-ok',
19894 Roo.apply(Roo.bootstrap.form.ComboBox, {
19896 touchViewTemplate : {
19898 cls: 'modal fade roo-combobox-touch-view',
19902 cls: 'modal-dialog',
19903 style : 'position:fixed', // we have to fix position....
19907 cls: 'modal-content',
19909 Roo.bootstrap.form.ComboBox.header,
19910 Roo.bootstrap.form.ComboBox.body,
19911 Roo.bootstrap.form.ComboBox.footer
19920 * Ext JS Library 1.1.1
19921 * Copyright(c) 2006-2007, Ext JS, LLC.
19923 * Originally Released Under LGPL - original licence link has changed is not relivant.
19926 * <script type="text/javascript">
19931 * @extends Roo.util.Observable
19932 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19933 * This class also supports single and multi selection modes. <br>
19934 * Create a data model bound view:
19936 var store = new Roo.data.Store(...);
19938 var view = new Roo.View({
19940 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19942 singleSelect: true,
19943 selectedClass: "ydataview-selected",
19947 // listen for node click?
19948 view.on("click", function(vw, index, node, e){
19949 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19953 dataModel.load("foobar.xml");
19955 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19957 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19958 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19960 * Note: old style constructor is still suported (container, template, config)
19963 * Create a new View
19964 * @param {Object} config The config object
19967 Roo.View = function(config, depreciated_tpl, depreciated_config){
19969 this.parent = false;
19971 if (typeof(depreciated_tpl) == 'undefined') {
19972 // new way.. - universal constructor.
19973 Roo.apply(this, config);
19974 this.el = Roo.get(this.el);
19977 this.el = Roo.get(config);
19978 this.tpl = depreciated_tpl;
19979 Roo.apply(this, depreciated_config);
19981 this.wrapEl = this.el.wrap().wrap();
19982 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19985 if(typeof(this.tpl) == "string"){
19986 this.tpl = new Roo.Template(this.tpl);
19988 // support xtype ctors..
19989 this.tpl = new Roo.factory(this.tpl, Roo);
19993 this.tpl.compile();
19998 * @event beforeclick
19999 * Fires before a click is processed. Returns false to cancel the default action.
20000 * @param {Roo.View} this
20001 * @param {Number} index The index of the target node
20002 * @param {HTMLElement} node The target node
20003 * @param {Roo.EventObject} e The raw event object
20005 "beforeclick" : true,
20008 * Fires when a template node is clicked.
20009 * @param {Roo.View} this
20010 * @param {Number} index The index of the target node
20011 * @param {HTMLElement} node The target node
20012 * @param {Roo.EventObject} e The raw event object
20017 * Fires when a template node is double clicked.
20018 * @param {Roo.View} this
20019 * @param {Number} index The index of the target node
20020 * @param {HTMLElement} node The target node
20021 * @param {Roo.EventObject} e The raw event object
20025 * @event contextmenu
20026 * Fires when a template node is right clicked.
20027 * @param {Roo.View} this
20028 * @param {Number} index The index of the target node
20029 * @param {HTMLElement} node The target node
20030 * @param {Roo.EventObject} e The raw event object
20032 "contextmenu" : true,
20034 * @event selectionchange
20035 * Fires when the selected nodes change.
20036 * @param {Roo.View} this
20037 * @param {Array} selections Array of the selected nodes
20039 "selectionchange" : true,
20042 * @event beforeselect
20043 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20044 * @param {Roo.View} this
20045 * @param {HTMLElement} node The node to be selected
20046 * @param {Array} selections Array of currently selected nodes
20048 "beforeselect" : true,
20050 * @event preparedata
20051 * Fires on every row to render, to allow you to change the data.
20052 * @param {Roo.View} this
20053 * @param {Object} data to be rendered (change this)
20055 "preparedata" : true
20063 "click": this.onClick,
20064 "dblclick": this.onDblClick,
20065 "contextmenu": this.onContextMenu,
20069 this.selections = [];
20071 this.cmp = new Roo.CompositeElementLite([]);
20073 this.store = Roo.factory(this.store, Roo.data);
20074 this.setStore(this.store, true);
20077 if ( this.footer && this.footer.xtype) {
20079 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20081 this.footer.dataSource = this.store;
20082 this.footer.container = fctr;
20083 this.footer = Roo.factory(this.footer, Roo);
20084 fctr.insertFirst(this.el);
20086 // this is a bit insane - as the paging toolbar seems to detach the el..
20087 // dom.parentNode.parentNode.parentNode
20088 // they get detached?
20092 Roo.View.superclass.constructor.call(this);
20097 Roo.extend(Roo.View, Roo.util.Observable, {
20100 * @cfg {Roo.data.Store} store Data store to load data from.
20105 * @cfg {String|Roo.Element} el The container element.
20110 * @cfg {String|Roo.Template} tpl The template used by this View
20114 * @cfg {String} dataName the named area of the template to use as the data area
20115 * Works with domtemplates roo-name="name"
20119 * @cfg {String} selectedClass The css class to add to selected nodes
20121 selectedClass : "x-view-selected",
20123 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20128 * @cfg {String} text to display on mask (default Loading)
20132 * @cfg {Boolean} multiSelect Allow multiple selection
20134 multiSelect : false,
20136 * @cfg {Boolean} singleSelect Allow single selection
20138 singleSelect: false,
20141 * @cfg {Boolean} toggleSelect - selecting
20143 toggleSelect : false,
20146 * @cfg {Boolean} tickable - selecting
20151 * Returns the element this view is bound to.
20152 * @return {Roo.Element}
20154 getEl : function(){
20155 return this.wrapEl;
20161 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20163 refresh : function(){
20164 //Roo.log('refresh');
20167 // if we are using something like 'domtemplate', then
20168 // the what gets used is:
20169 // t.applySubtemplate(NAME, data, wrapping data..)
20170 // the outer template then get' applied with
20171 // the store 'extra data'
20172 // and the body get's added to the
20173 // roo-name="data" node?
20174 // <span class='roo-tpl-{name}'></span> ?????
20178 this.clearSelections();
20179 this.el.update("");
20181 var records = this.store.getRange();
20182 if(records.length < 1) {
20184 // is this valid?? = should it render a template??
20186 this.el.update(this.emptyText);
20190 if (this.dataName) {
20191 this.el.update(t.apply(this.store.meta)); //????
20192 el = this.el.child('.roo-tpl-' + this.dataName);
20195 for(var i = 0, len = records.length; i < len; i++){
20196 var data = this.prepareData(records[i].data, i, records[i]);
20197 this.fireEvent("preparedata", this, data, i, records[i]);
20199 var d = Roo.apply({}, data);
20202 Roo.apply(d, {'roo-id' : Roo.id()});
20206 Roo.each(this.parent.item, function(item){
20207 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20210 Roo.apply(d, {'roo-data-checked' : 'checked'});
20214 html[html.length] = Roo.util.Format.trim(
20216 t.applySubtemplate(this.dataName, d, this.store.meta) :
20223 el.update(html.join(""));
20224 this.nodes = el.dom.childNodes;
20225 this.updateIndexes(0);
20230 * Function to override to reformat the data that is sent to
20231 * the template for each node.
20232 * DEPRICATED - use the preparedata event handler.
20233 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20234 * a JSON object for an UpdateManager bound view).
20236 prepareData : function(data, index, record)
20238 this.fireEvent("preparedata", this, data, index, record);
20242 onUpdate : function(ds, record){
20243 // Roo.log('on update');
20244 this.clearSelections();
20245 var index = this.store.indexOf(record);
20246 var n = this.nodes[index];
20247 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20248 n.parentNode.removeChild(n);
20249 this.updateIndexes(index, index);
20255 onAdd : function(ds, records, index)
20257 //Roo.log(['on Add', ds, records, index] );
20258 this.clearSelections();
20259 if(this.nodes.length == 0){
20263 var n = this.nodes[index];
20264 for(var i = 0, len = records.length; i < len; i++){
20265 var d = this.prepareData(records[i].data, i, records[i]);
20267 this.tpl.insertBefore(n, d);
20270 this.tpl.append(this.el, d);
20273 this.updateIndexes(index);
20276 onRemove : function(ds, record, index){
20277 // Roo.log('onRemove');
20278 this.clearSelections();
20279 var el = this.dataName ?
20280 this.el.child('.roo-tpl-' + this.dataName) :
20283 el.dom.removeChild(this.nodes[index]);
20284 this.updateIndexes(index);
20288 * Refresh an individual node.
20289 * @param {Number} index
20291 refreshNode : function(index){
20292 this.onUpdate(this.store, this.store.getAt(index));
20295 updateIndexes : function(startIndex, endIndex){
20296 var ns = this.nodes;
20297 startIndex = startIndex || 0;
20298 endIndex = endIndex || ns.length - 1;
20299 for(var i = startIndex; i <= endIndex; i++){
20300 ns[i].nodeIndex = i;
20305 * Changes the data store this view uses and refresh the view.
20306 * @param {Store} store
20308 setStore : function(store, initial){
20309 if(!initial && this.store){
20310 this.store.un("datachanged", this.refresh);
20311 this.store.un("add", this.onAdd);
20312 this.store.un("remove", this.onRemove);
20313 this.store.un("update", this.onUpdate);
20314 this.store.un("clear", this.refresh);
20315 this.store.un("beforeload", this.onBeforeLoad);
20316 this.store.un("load", this.onLoad);
20317 this.store.un("loadexception", this.onLoad);
20321 store.on("datachanged", this.refresh, this);
20322 store.on("add", this.onAdd, this);
20323 store.on("remove", this.onRemove, this);
20324 store.on("update", this.onUpdate, this);
20325 store.on("clear", this.refresh, this);
20326 store.on("beforeload", this.onBeforeLoad, this);
20327 store.on("load", this.onLoad, this);
20328 store.on("loadexception", this.onLoad, this);
20336 * onbeforeLoad - masks the loading area.
20339 onBeforeLoad : function(store,opts)
20341 //Roo.log('onBeforeLoad');
20343 this.el.update("");
20345 this.el.mask(this.mask ? this.mask : "Loading" );
20347 onLoad : function ()
20354 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20355 * @param {HTMLElement} node
20356 * @return {HTMLElement} The template node
20358 findItemFromChild : function(node){
20359 var el = this.dataName ?
20360 this.el.child('.roo-tpl-' + this.dataName,true) :
20363 if(!node || node.parentNode == el){
20366 var p = node.parentNode;
20367 while(p && p != el){
20368 if(p.parentNode == el){
20377 onClick : function(e){
20378 var item = this.findItemFromChild(e.getTarget());
20380 var index = this.indexOf(item);
20381 if(this.onItemClick(item, index, e) !== false){
20382 this.fireEvent("click", this, index, item, e);
20385 this.clearSelections();
20390 onContextMenu : function(e){
20391 var item = this.findItemFromChild(e.getTarget());
20393 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20398 onDblClick : function(e){
20399 var item = this.findItemFromChild(e.getTarget());
20401 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20405 onItemClick : function(item, index, e)
20407 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20410 if (this.toggleSelect) {
20411 var m = this.isSelected(item) ? 'unselect' : 'select';
20414 _t[m](item, true, false);
20417 if(this.multiSelect || this.singleSelect){
20418 if(this.multiSelect && e.shiftKey && this.lastSelection){
20419 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20421 this.select(item, this.multiSelect && e.ctrlKey);
20422 this.lastSelection = item;
20425 if(!this.tickable){
20426 e.preventDefault();
20434 * Get the number of selected nodes.
20437 getSelectionCount : function(){
20438 return this.selections.length;
20442 * Get the currently selected nodes.
20443 * @return {Array} An array of HTMLElements
20445 getSelectedNodes : function(){
20446 return this.selections;
20450 * Get the indexes of the selected nodes.
20453 getSelectedIndexes : function(){
20454 var indexes = [], s = this.selections;
20455 for(var i = 0, len = s.length; i < len; i++){
20456 indexes.push(s[i].nodeIndex);
20462 * Clear all selections
20463 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20465 clearSelections : function(suppressEvent){
20466 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20467 this.cmp.elements = this.selections;
20468 this.cmp.removeClass(this.selectedClass);
20469 this.selections = [];
20470 if(!suppressEvent){
20471 this.fireEvent("selectionchange", this, this.selections);
20477 * Returns true if the passed node is selected
20478 * @param {HTMLElement/Number} node The node or node index
20479 * @return {Boolean}
20481 isSelected : function(node){
20482 var s = this.selections;
20486 node = this.getNode(node);
20487 return s.indexOf(node) !== -1;
20492 * @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
20493 * @param {Boolean} keepExisting (optional) true to keep existing selections
20494 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20496 select : function(nodeInfo, keepExisting, suppressEvent){
20497 if(nodeInfo instanceof Array){
20499 this.clearSelections(true);
20501 for(var i = 0, len = nodeInfo.length; i < len; i++){
20502 this.select(nodeInfo[i], true, true);
20506 var node = this.getNode(nodeInfo);
20507 if(!node || this.isSelected(node)){
20508 return; // already selected.
20511 this.clearSelections(true);
20514 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20515 Roo.fly(node).addClass(this.selectedClass);
20516 this.selections.push(node);
20517 if(!suppressEvent){
20518 this.fireEvent("selectionchange", this, this.selections);
20526 * @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
20527 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20528 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20530 unselect : function(nodeInfo, keepExisting, suppressEvent)
20532 if(nodeInfo instanceof Array){
20533 Roo.each(this.selections, function(s) {
20534 this.unselect(s, nodeInfo);
20538 var node = this.getNode(nodeInfo);
20539 if(!node || !this.isSelected(node)){
20540 //Roo.log("not selected");
20541 return; // not selected.
20545 Roo.each(this.selections, function(s) {
20547 Roo.fly(node).removeClass(this.selectedClass);
20554 this.selections= ns;
20555 this.fireEvent("selectionchange", this, this.selections);
20559 * Gets a template node.
20560 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20561 * @return {HTMLElement} The node or null if it wasn't found
20563 getNode : function(nodeInfo){
20564 if(typeof nodeInfo == "string"){
20565 return document.getElementById(nodeInfo);
20566 }else if(typeof nodeInfo == "number"){
20567 return this.nodes[nodeInfo];
20573 * Gets a range template nodes.
20574 * @param {Number} startIndex
20575 * @param {Number} endIndex
20576 * @return {Array} An array of nodes
20578 getNodes : function(start, end){
20579 var ns = this.nodes;
20580 start = start || 0;
20581 end = typeof end == "undefined" ? ns.length - 1 : end;
20584 for(var i = start; i <= end; i++){
20588 for(var i = start; i >= end; i--){
20596 * Finds the index of the passed node
20597 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20598 * @return {Number} The index of the node or -1
20600 indexOf : function(node){
20601 node = this.getNode(node);
20602 if(typeof node.nodeIndex == "number"){
20603 return node.nodeIndex;
20605 var ns = this.nodes;
20606 for(var i = 0, len = ns.length; i < len; i++){
20617 * based on jquery fullcalendar
20621 Roo.bootstrap = Roo.bootstrap || {};
20623 * @class Roo.bootstrap.Calendar
20624 * @extends Roo.bootstrap.Component
20625 * Bootstrap Calendar class
20626 * @cfg {Boolean} loadMask (true|false) default false
20627 * @cfg {Object} header generate the user specific header of the calendar, default false
20630 * Create a new Container
20631 * @param {Object} config The config object
20636 Roo.bootstrap.Calendar = function(config){
20637 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20641 * Fires when a date is selected
20642 * @param {DatePicker} this
20643 * @param {Date} date The selected date
20647 * @event monthchange
20648 * Fires when the displayed month changes
20649 * @param {DatePicker} this
20650 * @param {Date} date The selected month
20652 'monthchange': true,
20654 * @event evententer
20655 * Fires when mouse over an event
20656 * @param {Calendar} this
20657 * @param {event} Event
20659 'evententer': true,
20661 * @event eventleave
20662 * Fires when the mouse leaves an
20663 * @param {Calendar} this
20666 'eventleave': true,
20668 * @event eventclick
20669 * Fires when the mouse click an
20670 * @param {Calendar} this
20679 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20682 * @cfg {Roo.data.Store} store
20683 * The data source for the calendar
20687 * @cfg {Number} startDay
20688 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20696 getAutoCreate : function(){
20699 var fc_button = function(name, corner, style, content ) {
20700 return Roo.apply({},{
20702 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20704 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20707 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20718 style : 'width:100%',
20725 cls : 'fc-header-left',
20727 fc_button('prev', 'left', 'arrow', '‹' ),
20728 fc_button('next', 'right', 'arrow', '›' ),
20729 { tag: 'span', cls: 'fc-header-space' },
20730 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20738 cls : 'fc-header-center',
20742 cls: 'fc-header-title',
20745 html : 'month / year'
20753 cls : 'fc-header-right',
20755 /* fc_button('month', 'left', '', 'month' ),
20756 fc_button('week', '', '', 'week' ),
20757 fc_button('day', 'right', '', 'day' )
20769 header = this.header;
20772 var cal_heads = function() {
20774 // fixme - handle this.
20776 for (var i =0; i < Date.dayNames.length; i++) {
20777 var d = Date.dayNames[i];
20780 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20781 html : d.substring(0,3)
20785 ret[0].cls += ' fc-first';
20786 ret[6].cls += ' fc-last';
20789 var cal_cell = function(n) {
20792 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20797 cls: 'fc-day-number',
20801 cls: 'fc-day-content',
20805 style: 'position: relative;' // height: 17px;
20817 var cal_rows = function() {
20820 for (var r = 0; r < 6; r++) {
20827 for (var i =0; i < Date.dayNames.length; i++) {
20828 var d = Date.dayNames[i];
20829 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20832 row.cn[0].cls+=' fc-first';
20833 row.cn[0].cn[0].style = 'min-height:90px';
20834 row.cn[6].cls+=' fc-last';
20838 ret[0].cls += ' fc-first';
20839 ret[4].cls += ' fc-prev-last';
20840 ret[5].cls += ' fc-last';
20847 cls: 'fc-border-separate',
20848 style : 'width:100%',
20856 cls : 'fc-first fc-last',
20874 cls : 'fc-content',
20875 style : "position: relative;",
20878 cls : 'fc-view fc-view-month fc-grid',
20879 style : 'position: relative',
20880 unselectable : 'on',
20883 cls : 'fc-event-container',
20884 style : 'position:absolute;z-index:8;top:0;left:0;'
20902 initEvents : function()
20905 throw "can not find store for calendar";
20911 style: "text-align:center",
20915 style: "background-color:white;width:50%;margin:250 auto",
20919 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20930 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20932 var size = this.el.select('.fc-content', true).first().getSize();
20933 this.maskEl.setSize(size.width, size.height);
20934 this.maskEl.enableDisplayMode("block");
20935 if(!this.loadMask){
20936 this.maskEl.hide();
20939 this.store = Roo.factory(this.store, Roo.data);
20940 this.store.on('load', this.onLoad, this);
20941 this.store.on('beforeload', this.onBeforeLoad, this);
20945 this.cells = this.el.select('.fc-day',true);
20946 //Roo.log(this.cells);
20947 this.textNodes = this.el.query('.fc-day-number');
20948 this.cells.addClassOnOver('fc-state-hover');
20950 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20951 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20952 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20953 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20955 this.on('monthchange', this.onMonthChange, this);
20957 this.update(new Date().clearTime());
20960 resize : function() {
20961 var sz = this.el.getSize();
20963 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20964 this.el.select('.fc-day-content div',true).setHeight(34);
20969 showPrevMonth : function(e){
20970 this.update(this.activeDate.add("mo", -1));
20972 showToday : function(e){
20973 this.update(new Date().clearTime());
20976 showNextMonth : function(e){
20977 this.update(this.activeDate.add("mo", 1));
20981 showPrevYear : function(){
20982 this.update(this.activeDate.add("y", -1));
20986 showNextYear : function(){
20987 this.update(this.activeDate.add("y", 1));
20992 update : function(date)
20994 var vd = this.activeDate;
20995 this.activeDate = date;
20996 // if(vd && this.el){
20997 // var t = date.getTime();
20998 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20999 // Roo.log('using add remove');
21001 // this.fireEvent('monthchange', this, date);
21003 // this.cells.removeClass("fc-state-highlight");
21004 // this.cells.each(function(c){
21005 // if(c.dateValue == t){
21006 // c.addClass("fc-state-highlight");
21007 // setTimeout(function(){
21008 // try{c.dom.firstChild.focus();}catch(e){}
21018 var days = date.getDaysInMonth();
21020 var firstOfMonth = date.getFirstDateOfMonth();
21021 var startingPos = firstOfMonth.getDay()-this.startDay;
21023 if(startingPos < this.startDay){
21027 var pm = date.add(Date.MONTH, -1);
21028 var prevStart = pm.getDaysInMonth()-startingPos;
21030 this.cells = this.el.select('.fc-day',true);
21031 this.textNodes = this.el.query('.fc-day-number');
21032 this.cells.addClassOnOver('fc-state-hover');
21034 var cells = this.cells.elements;
21035 var textEls = this.textNodes;
21037 Roo.each(cells, function(cell){
21038 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21041 days += startingPos;
21043 // convert everything to numbers so it's fast
21044 var day = 86400000;
21045 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21048 //Roo.log(prevStart);
21050 var today = new Date().clearTime().getTime();
21051 var sel = date.clearTime().getTime();
21052 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21053 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21054 var ddMatch = this.disabledDatesRE;
21055 var ddText = this.disabledDatesText;
21056 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21057 var ddaysText = this.disabledDaysText;
21058 var format = this.format;
21060 var setCellClass = function(cal, cell){
21064 //Roo.log('set Cell Class');
21066 var t = d.getTime();
21070 cell.dateValue = t;
21072 cell.className += " fc-today";
21073 cell.className += " fc-state-highlight";
21074 cell.title = cal.todayText;
21077 // disable highlight in other month..
21078 //cell.className += " fc-state-highlight";
21083 cell.className = " fc-state-disabled";
21084 cell.title = cal.minText;
21088 cell.className = " fc-state-disabled";
21089 cell.title = cal.maxText;
21093 if(ddays.indexOf(d.getDay()) != -1){
21094 cell.title = ddaysText;
21095 cell.className = " fc-state-disabled";
21098 if(ddMatch && format){
21099 var fvalue = d.dateFormat(format);
21100 if(ddMatch.test(fvalue)){
21101 cell.title = ddText.replace("%0", fvalue);
21102 cell.className = " fc-state-disabled";
21106 if (!cell.initialClassName) {
21107 cell.initialClassName = cell.dom.className;
21110 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21115 for(; i < startingPos; i++) {
21116 textEls[i].innerHTML = (++prevStart);
21117 d.setDate(d.getDate()+1);
21119 cells[i].className = "fc-past fc-other-month";
21120 setCellClass(this, cells[i]);
21125 for(; i < days; i++){
21126 intDay = i - startingPos + 1;
21127 textEls[i].innerHTML = (intDay);
21128 d.setDate(d.getDate()+1);
21130 cells[i].className = ''; // "x-date-active";
21131 setCellClass(this, cells[i]);
21135 for(; i < 42; i++) {
21136 textEls[i].innerHTML = (++extraDays);
21137 d.setDate(d.getDate()+1);
21139 cells[i].className = "fc-future fc-other-month";
21140 setCellClass(this, cells[i]);
21143 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21145 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21147 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21148 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21150 if(totalRows != 6){
21151 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21152 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21155 this.fireEvent('monthchange', this, date);
21159 if(!this.internalRender){
21160 var main = this.el.dom.firstChild;
21161 var w = main.offsetWidth;
21162 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21163 Roo.fly(main).setWidth(w);
21164 this.internalRender = true;
21165 // opera does not respect the auto grow header center column
21166 // then, after it gets a width opera refuses to recalculate
21167 // without a second pass
21168 if(Roo.isOpera && !this.secondPass){
21169 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21170 this.secondPass = true;
21171 this.update.defer(10, this, [date]);
21178 findCell : function(dt) {
21179 dt = dt.clearTime().getTime();
21181 this.cells.each(function(c){
21182 //Roo.log("check " +c.dateValue + '?=' + dt);
21183 if(c.dateValue == dt){
21193 findCells : function(ev) {
21194 var s = ev.start.clone().clearTime().getTime();
21196 var e= ev.end.clone().clearTime().getTime();
21199 this.cells.each(function(c){
21200 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21202 if(c.dateValue > e){
21205 if(c.dateValue < s){
21214 // findBestRow: function(cells)
21218 // for (var i =0 ; i < cells.length;i++) {
21219 // ret = Math.max(cells[i].rows || 0,ret);
21226 addItem : function(ev)
21228 // look for vertical location slot in
21229 var cells = this.findCells(ev);
21231 // ev.row = this.findBestRow(cells);
21233 // work out the location.
21237 for(var i =0; i < cells.length; i++) {
21239 cells[i].row = cells[0].row;
21242 cells[i].row = cells[i].row + 1;
21252 if (crow.start.getY() == cells[i].getY()) {
21254 crow.end = cells[i];
21271 cells[0].events.push(ev);
21273 this.calevents.push(ev);
21276 clearEvents: function() {
21278 if(!this.calevents){
21282 Roo.each(this.cells.elements, function(c){
21288 Roo.each(this.calevents, function(e) {
21289 Roo.each(e.els, function(el) {
21290 el.un('mouseenter' ,this.onEventEnter, this);
21291 el.un('mouseleave' ,this.onEventLeave, this);
21296 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21302 renderEvents: function()
21306 this.cells.each(function(c) {
21315 if(c.row != c.events.length){
21316 r = 4 - (4 - (c.row - c.events.length));
21319 c.events = ev.slice(0, r);
21320 c.more = ev.slice(r);
21322 if(c.more.length && c.more.length == 1){
21323 c.events.push(c.more.pop());
21326 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21330 this.cells.each(function(c) {
21332 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21335 for (var e = 0; e < c.events.length; e++){
21336 var ev = c.events[e];
21337 var rows = ev.rows;
21339 for(var i = 0; i < rows.length; i++) {
21341 // how many rows should it span..
21344 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21345 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21347 unselectable : "on",
21350 cls: 'fc-event-inner',
21354 // cls: 'fc-event-time',
21355 // html : cells.length > 1 ? '' : ev.time
21359 cls: 'fc-event-title',
21360 html : String.format('{0}', ev.title)
21367 cls: 'ui-resizable-handle ui-resizable-e',
21368 html : '  '
21375 cfg.cls += ' fc-event-start';
21377 if ((i+1) == rows.length) {
21378 cfg.cls += ' fc-event-end';
21381 var ctr = _this.el.select('.fc-event-container',true).first();
21382 var cg = ctr.createChild(cfg);
21384 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21385 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21387 var r = (c.more.length) ? 1 : 0;
21388 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21389 cg.setWidth(ebox.right - sbox.x -2);
21391 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21392 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21393 cg.on('click', _this.onEventClick, _this, ev);
21404 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21405 style : 'position: absolute',
21406 unselectable : "on",
21409 cls: 'fc-event-inner',
21413 cls: 'fc-event-title',
21421 cls: 'ui-resizable-handle ui-resizable-e',
21422 html : '  '
21428 var ctr = _this.el.select('.fc-event-container',true).first();
21429 var cg = ctr.createChild(cfg);
21431 var sbox = c.select('.fc-day-content',true).first().getBox();
21432 var ebox = c.select('.fc-day-content',true).first().getBox();
21434 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21435 cg.setWidth(ebox.right - sbox.x -2);
21437 cg.on('click', _this.onMoreEventClick, _this, c.more);
21447 onEventEnter: function (e, el,event,d) {
21448 this.fireEvent('evententer', this, el, event);
21451 onEventLeave: function (e, el,event,d) {
21452 this.fireEvent('eventleave', this, el, event);
21455 onEventClick: function (e, el,event,d) {
21456 this.fireEvent('eventclick', this, el, event);
21459 onMonthChange: function () {
21463 onMoreEventClick: function(e, el, more)
21467 this.calpopover.placement = 'right';
21468 this.calpopover.setTitle('More');
21470 this.calpopover.setContent('');
21472 var ctr = this.calpopover.el.select('.popover-content', true).first();
21474 Roo.each(more, function(m){
21476 cls : 'fc-event-hori fc-event-draggable',
21479 var cg = ctr.createChild(cfg);
21481 cg.on('click', _this.onEventClick, _this, m);
21484 this.calpopover.show(el);
21489 onLoad: function ()
21491 this.calevents = [];
21494 if(this.store.getCount() > 0){
21495 this.store.data.each(function(d){
21498 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21499 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21500 time : d.data.start_time,
21501 title : d.data.title,
21502 description : d.data.description,
21503 venue : d.data.venue
21508 this.renderEvents();
21510 if(this.calevents.length && this.loadMask){
21511 this.maskEl.hide();
21515 onBeforeLoad: function()
21517 this.clearEvents();
21519 this.maskEl.show();
21533 * @class Roo.bootstrap.Popover
21534 * @extends Roo.bootstrap.Component
21535 * @parent none builder
21536 * @children Roo.bootstrap.Component
21537 * Bootstrap Popover class
21538 * @cfg {String} html contents of the popover (or false to use children..)
21539 * @cfg {String} title of popover (or false to hide)
21540 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21541 * @cfg {String} trigger click || hover (or false to trigger manually)
21542 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21543 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21544 * - if false and it has a 'parent' then it will be automatically added to that element
21545 * - if string - Roo.get will be called
21546 * @cfg {Number} delay - delay before showing
21549 * Create a new Popover
21550 * @param {Object} config The config object
21553 Roo.bootstrap.Popover = function(config){
21554 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21560 * After the popover show
21562 * @param {Roo.bootstrap.Popover} this
21567 * After the popover hide
21569 * @param {Roo.bootstrap.Popover} this
21575 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21580 placement : 'right',
21581 trigger : 'hover', // hover
21587 can_build_overlaid : false,
21589 maskEl : false, // the mask element
21592 alignEl : false, // when show is called with an element - this get's stored.
21594 getChildContainer : function()
21596 return this.contentEl;
21599 getPopoverHeader : function()
21601 this.title = true; // flag not to hide it..
21602 this.headerEl.addClass('p-0');
21603 return this.headerEl
21607 getAutoCreate : function(){
21610 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21611 style: 'display:block',
21617 cls : 'popover-inner ',
21621 cls: 'popover-title popover-header',
21622 html : this.title === false ? '' : this.title
21625 cls : 'popover-content popover-body ' + (this.cls || ''),
21626 html : this.html || ''
21637 * @param {string} the title
21639 setTitle: function(str)
21643 this.headerEl.dom.innerHTML = str;
21648 * @param {string} the body content
21650 setContent: function(str)
21653 if (this.contentEl) {
21654 this.contentEl.dom.innerHTML = str;
21658 // as it get's added to the bottom of the page.
21659 onRender : function(ct, position)
21661 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21666 var cfg = Roo.apply({}, this.getAutoCreate());
21670 cfg.cls += ' ' + this.cls;
21673 cfg.style = this.style;
21675 //Roo.log("adding to ");
21676 this.el = Roo.get(document.body).createChild(cfg, position);
21677 // Roo.log(this.el);
21680 this.contentEl = this.el.select('.popover-content',true).first();
21681 this.headerEl = this.el.select('.popover-title',true).first();
21684 if(typeof(this.items) != 'undefined'){
21685 var items = this.items;
21688 for(var i =0;i < items.length;i++) {
21689 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21693 this.items = nitems;
21695 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21696 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21703 resizeMask : function()
21705 this.maskEl.setSize(
21706 Roo.lib.Dom.getViewWidth(true),
21707 Roo.lib.Dom.getViewHeight(true)
21711 initEvents : function()
21715 Roo.bootstrap.Popover.register(this);
21718 this.arrowEl = this.el.select('.arrow',true).first();
21719 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21720 this.el.enableDisplayMode('block');
21724 if (this.over === false && !this.parent()) {
21727 if (this.triggers === false) {
21732 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21733 var triggers = this.trigger ? this.trigger.split(' ') : [];
21734 Roo.each(triggers, function(trigger) {
21736 if (trigger == 'click') {
21737 on_el.on('click', this.toggle, this);
21738 } else if (trigger != 'manual') {
21739 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21740 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21742 on_el.on(eventIn ,this.enter, this);
21743 on_el.on(eventOut, this.leave, this);
21753 toggle : function () {
21754 this.hoverState == 'in' ? this.leave() : this.enter();
21757 enter : function () {
21759 clearTimeout(this.timeout);
21761 this.hoverState = 'in';
21763 if (!this.delay || !this.delay.show) {
21768 this.timeout = setTimeout(function () {
21769 if (_t.hoverState == 'in') {
21772 }, this.delay.show)
21775 leave : function() {
21776 clearTimeout(this.timeout);
21778 this.hoverState = 'out';
21780 if (!this.delay || !this.delay.hide) {
21785 this.timeout = setTimeout(function () {
21786 if (_t.hoverState == 'out') {
21789 }, this.delay.hide)
21793 * update the position of the dialog
21794 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21799 doAlign : function()
21802 if (this.alignEl) {
21803 this.updatePosition(this.placement, true);
21806 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21807 var es = this.el.getSize();
21808 var x = Roo.lib.Dom.getViewWidth()/2;
21809 var y = Roo.lib.Dom.getViewHeight()/2;
21810 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21822 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21823 * @param {string} (left|right|top|bottom) position
21825 show : function (on_el, placement)
21827 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21828 on_el = on_el || false; // default to false
21831 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21832 on_el = this.parent().el;
21833 } else if (this.over) {
21834 on_el = Roo.get(this.over);
21839 this.alignEl = Roo.get( on_el );
21842 this.render(document.body);
21848 if (this.title === false) {
21849 this.headerEl.hide();
21854 this.el.dom.style.display = 'block';
21858 //var arrow = this.el.select('.arrow',true).first();
21859 //arrow.set(align[2],
21861 this.el.addClass('in');
21865 this.hoverState = 'in';
21868 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21869 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21870 this.maskEl.dom.style.display = 'block';
21871 this.maskEl.addClass('show');
21873 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21875 this.fireEvent('show', this);
21879 * fire this manually after loading a grid in the table for example
21880 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21881 * @param {Boolean} try and move it if we cant get right position.
21883 updatePosition : function(placement, try_move)
21885 // allow for calling with no parameters
21886 placement = placement ? placement : this.placement;
21887 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21889 this.el.removeClass([
21890 'fade','top','bottom', 'left', 'right','in',
21891 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21893 this.el.addClass(placement + ' bs-popover-' + placement);
21895 if (!this.alignEl ) {
21899 switch (placement) {
21901 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21902 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21903 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21904 //normal display... or moved up/down.
21905 this.el.setXY(offset);
21906 var xy = this.alignEl.getAnchorXY('tr', false);
21908 this.arrowEl.setXY(xy);
21911 // continue through...
21912 return this.updatePosition('left', false);
21916 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21917 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21918 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21919 //normal display... or moved up/down.
21920 this.el.setXY(offset);
21921 var xy = this.alignEl.getAnchorXY('tl', false);
21922 xy[0]-=10;xy[1]+=5; // << fix me
21923 this.arrowEl.setXY(xy);
21927 return this.updatePosition('right', false);
21930 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21931 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21932 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21933 //normal display... or moved up/down.
21934 this.el.setXY(offset);
21935 var xy = this.alignEl.getAnchorXY('t', false);
21936 xy[1]-=10; // << fix me
21937 this.arrowEl.setXY(xy);
21941 return this.updatePosition('bottom', false);
21944 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21945 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21946 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21947 //normal display... or moved up/down.
21948 this.el.setXY(offset);
21949 var xy = this.alignEl.getAnchorXY('b', false);
21950 xy[1]+=2; // << fix me
21951 this.arrowEl.setXY(xy);
21955 return this.updatePosition('top', false);
21966 this.el.setXY([0,0]);
21967 this.el.removeClass('in');
21969 this.hoverState = null;
21970 this.maskEl.hide(); // always..
21971 this.fireEvent('hide', this);
21977 Roo.apply(Roo.bootstrap.Popover, {
21980 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21981 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21982 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21983 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21988 clickHander : false,
21992 onMouseDown : function(e)
21994 if (this.popups.length && !e.getTarget(".roo-popover")) {
21995 /// what is nothing is showing..
22004 register : function(popup)
22006 if (!Roo.bootstrap.Popover.clickHandler) {
22007 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22009 // hide other popups.
22010 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22011 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22012 this.hideAll(); //<< why?
22013 //this.popups.push(popup);
22015 hideAll : function()
22017 this.popups.forEach(function(p) {
22021 onShow : function() {
22022 Roo.bootstrap.Popover.popups.push(this);
22024 onHide : function() {
22025 Roo.bootstrap.Popover.popups.remove(this);
22030 * @class Roo.bootstrap.PopoverNav
22031 * @extends Roo.bootstrap.nav.Simplebar
22032 * @parent Roo.bootstrap.Popover
22033 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22035 * Bootstrap Popover header navigation class
22036 * FIXME? should this go under nav?
22040 * Create a new Popover Header Navigation
22041 * @param {Object} config The config object
22044 Roo.bootstrap.PopoverNav = function(config){
22045 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22048 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22051 container_method : 'getPopoverHeader'
22069 * @class Roo.bootstrap.Progress
22070 * @extends Roo.bootstrap.Component
22071 * @children Roo.bootstrap.ProgressBar
22072 * Bootstrap Progress class
22073 * @cfg {Boolean} striped striped of the progress bar
22074 * @cfg {Boolean} active animated of the progress bar
22078 * Create a new Progress
22079 * @param {Object} config The config object
22082 Roo.bootstrap.Progress = function(config){
22083 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22086 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22091 getAutoCreate : function(){
22099 cfg.cls += ' progress-striped';
22103 cfg.cls += ' active';
22122 * @class Roo.bootstrap.ProgressBar
22123 * @extends Roo.bootstrap.Component
22124 * Bootstrap ProgressBar class
22125 * @cfg {Number} aria_valuenow aria-value now
22126 * @cfg {Number} aria_valuemin aria-value min
22127 * @cfg {Number} aria_valuemax aria-value max
22128 * @cfg {String} label label for the progress bar
22129 * @cfg {String} panel (success | info | warning | danger )
22130 * @cfg {String} role role of the progress bar
22131 * @cfg {String} sr_only text
22135 * Create a new ProgressBar
22136 * @param {Object} config The config object
22139 Roo.bootstrap.ProgressBar = function(config){
22140 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22143 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22147 aria_valuemax : 100,
22153 getAutoCreate : function()
22158 cls: 'progress-bar',
22159 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22171 cfg.role = this.role;
22174 if(this.aria_valuenow){
22175 cfg['aria-valuenow'] = this.aria_valuenow;
22178 if(this.aria_valuemin){
22179 cfg['aria-valuemin'] = this.aria_valuemin;
22182 if(this.aria_valuemax){
22183 cfg['aria-valuemax'] = this.aria_valuemax;
22186 if(this.label && !this.sr_only){
22187 cfg.html = this.label;
22191 cfg.cls += ' progress-bar-' + this.panel;
22197 update : function(aria_valuenow)
22199 this.aria_valuenow = aria_valuenow;
22201 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22209 * @class Roo.bootstrap.TabGroup
22210 * @extends Roo.bootstrap.Column
22211 * @children Roo.bootstrap.TabPanel
22212 * Bootstrap Column class
22213 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22214 * @cfg {Boolean} carousel true to make the group behave like a carousel
22215 * @cfg {Boolean} bullets show bullets for the panels
22216 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22217 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22218 * @cfg {Boolean} showarrow (true|false) show arrow default true
22221 * Create a new TabGroup
22222 * @param {Object} config The config object
22225 Roo.bootstrap.TabGroup = function(config){
22226 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22228 this.navId = Roo.id();
22231 Roo.bootstrap.TabGroup.register(this);
22235 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22238 transition : false,
22243 slideOnTouch : false,
22246 getAutoCreate : function()
22248 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22250 cfg.cls += ' tab-content';
22252 if (this.carousel) {
22253 cfg.cls += ' carousel slide';
22256 cls : 'carousel-inner',
22260 if(this.bullets && !Roo.isTouch){
22263 cls : 'carousel-bullets',
22267 if(this.bullets_cls){
22268 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22275 cfg.cn[0].cn.push(bullets);
22278 if(this.showarrow){
22279 cfg.cn[0].cn.push({
22281 class : 'carousel-arrow',
22285 class : 'carousel-prev',
22289 class : 'fa fa-chevron-left'
22295 class : 'carousel-next',
22299 class : 'fa fa-chevron-right'
22312 initEvents: function()
22314 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22315 // this.el.on("touchstart", this.onTouchStart, this);
22318 if(this.autoslide){
22321 this.slideFn = window.setInterval(function() {
22322 _this.showPanelNext();
22326 if(this.showarrow){
22327 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22328 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22334 // onTouchStart : function(e, el, o)
22336 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22340 // this.showPanelNext();
22344 getChildContainer : function()
22346 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22350 * register a Navigation item
22351 * @param {Roo.bootstrap.nav.Item} the navitem to add
22353 register : function(item)
22355 this.tabs.push( item);
22356 item.navId = this.navId; // not really needed..
22361 getActivePanel : function()
22364 Roo.each(this.tabs, function(t) {
22374 getPanelByName : function(n)
22377 Roo.each(this.tabs, function(t) {
22378 if (t.tabId == n) {
22386 indexOfPanel : function(p)
22389 Roo.each(this.tabs, function(t,i) {
22390 if (t.tabId == p.tabId) {
22399 * show a specific panel
22400 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22401 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22403 showPanel : function (pan)
22405 if(this.transition || typeof(pan) == 'undefined'){
22406 Roo.log("waiting for the transitionend");
22410 if (typeof(pan) == 'number') {
22411 pan = this.tabs[pan];
22414 if (typeof(pan) == 'string') {
22415 pan = this.getPanelByName(pan);
22418 var cur = this.getActivePanel();
22421 Roo.log('pan or acitve pan is undefined');
22425 if (pan.tabId == this.getActivePanel().tabId) {
22429 if (false === cur.fireEvent('beforedeactivate')) {
22433 if(this.bullets > 0 && !Roo.isTouch){
22434 this.setActiveBullet(this.indexOfPanel(pan));
22437 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22439 //class="carousel-item carousel-item-next carousel-item-left"
22441 this.transition = true;
22442 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22443 var lr = dir == 'next' ? 'left' : 'right';
22444 pan.el.addClass(dir); // or prev
22445 pan.el.addClass('carousel-item-' + dir); // or prev
22446 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22447 cur.el.addClass(lr); // or right
22448 pan.el.addClass(lr);
22449 cur.el.addClass('carousel-item-' +lr); // or right
22450 pan.el.addClass('carousel-item-' +lr);
22454 cur.el.on('transitionend', function() {
22455 Roo.log("trans end?");
22457 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22458 pan.setActive(true);
22460 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22461 cur.setActive(false);
22463 _this.transition = false;
22465 }, this, { single: true } );
22470 cur.setActive(false);
22471 pan.setActive(true);
22476 showPanelNext : function()
22478 var i = this.indexOfPanel(this.getActivePanel());
22480 if (i >= this.tabs.length - 1 && !this.autoslide) {
22484 if (i >= this.tabs.length - 1 && this.autoslide) {
22488 this.showPanel(this.tabs[i+1]);
22491 showPanelPrev : function()
22493 var i = this.indexOfPanel(this.getActivePanel());
22495 if (i < 1 && !this.autoslide) {
22499 if (i < 1 && this.autoslide) {
22500 i = this.tabs.length;
22503 this.showPanel(this.tabs[i-1]);
22507 addBullet: function()
22509 if(!this.bullets || Roo.isTouch){
22512 var ctr = this.el.select('.carousel-bullets',true).first();
22513 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22514 var bullet = ctr.createChild({
22515 cls : 'bullet bullet-' + i
22516 },ctr.dom.lastChild);
22521 bullet.on('click', (function(e, el, o, ii, t){
22523 e.preventDefault();
22525 this.showPanel(ii);
22527 if(this.autoslide && this.slideFn){
22528 clearInterval(this.slideFn);
22529 this.slideFn = window.setInterval(function() {
22530 _this.showPanelNext();
22534 }).createDelegate(this, [i, bullet], true));
22539 setActiveBullet : function(i)
22545 Roo.each(this.el.select('.bullet', true).elements, function(el){
22546 el.removeClass('selected');
22549 var bullet = this.el.select('.bullet-' + i, true).first();
22555 bullet.addClass('selected');
22566 Roo.apply(Roo.bootstrap.TabGroup, {
22570 * register a Navigation Group
22571 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22573 register : function(navgrp)
22575 this.groups[navgrp.navId] = navgrp;
22579 * fetch a Navigation Group based on the navigation ID
22580 * if one does not exist , it will get created.
22581 * @param {string} the navgroup to add
22582 * @returns {Roo.bootstrap.nav.Group} the navgroup
22584 get: function(navId) {
22585 if (typeof(this.groups[navId]) == 'undefined') {
22586 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22588 return this.groups[navId] ;
22603 * @class Roo.bootstrap.TabPanel
22604 * @extends Roo.bootstrap.Component
22605 * @children Roo.bootstrap.Component
22606 * Bootstrap TabPanel class
22607 * @cfg {Boolean} active panel active
22608 * @cfg {String} html panel content
22609 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22610 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22611 * @cfg {String} href click to link..
22612 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22616 * Create a new TabPanel
22617 * @param {Object} config The config object
22620 Roo.bootstrap.TabPanel = function(config){
22621 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22625 * Fires when the active status changes
22626 * @param {Roo.bootstrap.TabPanel} this
22627 * @param {Boolean} state the new state
22632 * @event beforedeactivate
22633 * Fires before a tab is de-activated - can be used to do validation on a form.
22634 * @param {Roo.bootstrap.TabPanel} this
22635 * @return {Boolean} false if there is an error
22638 'beforedeactivate': true
22641 this.tabId = this.tabId || Roo.id();
22645 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22652 touchSlide : false,
22653 getAutoCreate : function(){
22658 // item is needed for carousel - not sure if it has any effect otherwise
22659 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22660 html: this.html || ''
22664 cfg.cls += ' active';
22668 cfg.tabId = this.tabId;
22676 initEvents: function()
22678 var p = this.parent();
22680 this.navId = this.navId || p.navId;
22682 if (typeof(this.navId) != 'undefined') {
22683 // not really needed.. but just in case.. parent should be a NavGroup.
22684 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22688 var i = tg.tabs.length - 1;
22690 if(this.active && tg.bullets > 0 && i < tg.bullets){
22691 tg.setActiveBullet(i);
22695 this.el.on('click', this.onClick, this);
22697 if(Roo.isTouch && this.touchSlide){
22698 this.el.on("touchstart", this.onTouchStart, this);
22699 this.el.on("touchmove", this.onTouchMove, this);
22700 this.el.on("touchend", this.onTouchEnd, this);
22705 onRender : function(ct, position)
22707 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22710 setActive : function(state)
22712 Roo.log("panel - set active " + this.tabId + "=" + state);
22714 this.active = state;
22716 this.el.removeClass('active');
22718 } else if (!this.el.hasClass('active')) {
22719 this.el.addClass('active');
22722 this.fireEvent('changed', this, state);
22725 onClick : function(e)
22727 e.preventDefault();
22729 if(!this.href.length){
22733 window.location.href = this.href;
22742 onTouchStart : function(e)
22744 this.swiping = false;
22746 this.startX = e.browserEvent.touches[0].clientX;
22747 this.startY = e.browserEvent.touches[0].clientY;
22750 onTouchMove : function(e)
22752 this.swiping = true;
22754 this.endX = e.browserEvent.touches[0].clientX;
22755 this.endY = e.browserEvent.touches[0].clientY;
22758 onTouchEnd : function(e)
22765 var tabGroup = this.parent();
22767 if(this.endX > this.startX){ // swiping right
22768 tabGroup.showPanelPrev();
22772 if(this.startX > this.endX){ // swiping left
22773 tabGroup.showPanelNext();
22792 * @class Roo.bootstrap.form.DateField
22793 * @extends Roo.bootstrap.form.Input
22794 * Bootstrap DateField class
22795 * @cfg {Number} weekStart default 0
22796 * @cfg {String} viewMode default empty, (months|years)
22797 * @cfg {String} minViewMode default empty, (months|years)
22798 * @cfg {Number} startDate default -Infinity
22799 * @cfg {Number} endDate default Infinity
22800 * @cfg {Boolean} todayHighlight default false
22801 * @cfg {Boolean} todayBtn default false
22802 * @cfg {Boolean} calendarWeeks default false
22803 * @cfg {Object} daysOfWeekDisabled default empty
22804 * @cfg {Boolean} singleMode default false (true | false)
22806 * @cfg {Boolean} keyboardNavigation default true
22807 * @cfg {String} language default en
22810 * Create a new DateField
22811 * @param {Object} config The config object
22814 Roo.bootstrap.form.DateField = function(config){
22815 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22819 * Fires when this field show.
22820 * @param {Roo.bootstrap.form.DateField} this
22821 * @param {Mixed} date The date value
22826 * Fires when this field hide.
22827 * @param {Roo.bootstrap.form.DateField} this
22828 * @param {Mixed} date The date value
22833 * Fires when select a date.
22834 * @param {Roo.bootstrap.form.DateField} this
22835 * @param {Mixed} date The date value
22839 * @event beforeselect
22840 * Fires when before select a date.
22841 * @param {Roo.bootstrap.form.DateField} this
22842 * @param {Mixed} date The date value
22844 beforeselect : true
22848 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22851 * @cfg {String} format
22852 * The default date format string which can be overriden for localization support. The format must be
22853 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22857 * @cfg {String} altFormats
22858 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22859 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22861 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22869 todayHighlight : false,
22875 keyboardNavigation: true,
22877 calendarWeeks: false,
22879 startDate: -Infinity,
22883 daysOfWeekDisabled: [],
22887 singleMode : false,
22889 UTCDate: function()
22891 return new Date(Date.UTC.apply(Date, arguments));
22894 UTCToday: function()
22896 var today = new Date();
22897 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22900 getDate: function() {
22901 var d = this.getUTCDate();
22902 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22905 getUTCDate: function() {
22909 setDate: function(d) {
22910 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22913 setUTCDate: function(d) {
22915 this.setValue(this.formatDate(this.date));
22918 onRender: function(ct, position)
22921 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22923 this.language = this.language || 'en';
22924 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22925 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22927 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22928 this.format = this.format || 'm/d/y';
22929 this.isInline = false;
22930 this.isInput = true;
22931 this.component = this.el.select('.add-on', true).first() || false;
22932 this.component = (this.component && this.component.length === 0) ? false : this.component;
22933 this.hasInput = this.component && this.inputEl().length;
22935 if (typeof(this.minViewMode === 'string')) {
22936 switch (this.minViewMode) {
22938 this.minViewMode = 1;
22941 this.minViewMode = 2;
22944 this.minViewMode = 0;
22949 if (typeof(this.viewMode === 'string')) {
22950 switch (this.viewMode) {
22963 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22965 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22967 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22969 this.picker().on('mousedown', this.onMousedown, this);
22970 this.picker().on('click', this.onClick, this);
22972 this.picker().addClass('datepicker-dropdown');
22974 this.startViewMode = this.viewMode;
22976 if(this.singleMode){
22977 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22978 v.setVisibilityMode(Roo.Element.DISPLAY);
22982 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22983 v.setStyle('width', '189px');
22987 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22988 if(!this.calendarWeeks){
22993 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22994 v.attr('colspan', function(i, val){
22995 return parseInt(val) + 1;
23000 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23002 this.setStartDate(this.startDate);
23003 this.setEndDate(this.endDate);
23005 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23012 if(this.isInline) {
23017 picker : function()
23019 return this.pickerEl;
23020 // return this.el.select('.datepicker', true).first();
23023 fillDow: function()
23025 var dowCnt = this.weekStart;
23034 if(this.calendarWeeks){
23042 while (dowCnt < this.weekStart + 7) {
23046 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23050 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23053 fillMonths: function()
23056 var months = this.picker().select('>.datepicker-months td', true).first();
23058 months.dom.innerHTML = '';
23064 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23067 months.createChild(month);
23074 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;
23076 if (this.date < this.startDate) {
23077 this.viewDate = new Date(this.startDate);
23078 } else if (this.date > this.endDate) {
23079 this.viewDate = new Date(this.endDate);
23081 this.viewDate = new Date(this.date);
23089 var d = new Date(this.viewDate),
23090 year = d.getUTCFullYear(),
23091 month = d.getUTCMonth(),
23092 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23093 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23094 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23095 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23096 currentDate = this.date && this.date.valueOf(),
23097 today = this.UTCToday();
23099 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23101 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23103 // this.picker.select('>tfoot th.today').
23104 // .text(dates[this.language].today)
23105 // .toggle(this.todayBtn !== false);
23107 this.updateNavArrows();
23110 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23112 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23114 prevMonth.setUTCDate(day);
23116 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23118 var nextMonth = new Date(prevMonth);
23120 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23122 nextMonth = nextMonth.valueOf();
23124 var fillMonths = false;
23126 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23128 while(prevMonth.valueOf() <= nextMonth) {
23131 if (prevMonth.getUTCDay() === this.weekStart) {
23133 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23141 if(this.calendarWeeks){
23142 // ISO 8601: First week contains first thursday.
23143 // ISO also states week starts on Monday, but we can be more abstract here.
23145 // Start of current week: based on weekstart/current date
23146 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23147 // Thursday of this week
23148 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23149 // First Thursday of year, year from thursday
23150 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23151 // Calendar week: ms between thursdays, div ms per day, div 7 days
23152 calWeek = (th - yth) / 864e5 / 7 + 1;
23154 fillMonths.cn.push({
23162 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23164 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23167 if (this.todayHighlight &&
23168 prevMonth.getUTCFullYear() == today.getFullYear() &&
23169 prevMonth.getUTCMonth() == today.getMonth() &&
23170 prevMonth.getUTCDate() == today.getDate()) {
23171 clsName += ' today';
23174 if (currentDate && prevMonth.valueOf() === currentDate) {
23175 clsName += ' active';
23178 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23179 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23180 clsName += ' disabled';
23183 fillMonths.cn.push({
23185 cls: 'day ' + clsName,
23186 html: prevMonth.getDate()
23189 prevMonth.setDate(prevMonth.getDate()+1);
23192 var currentYear = this.date && this.date.getUTCFullYear();
23193 var currentMonth = this.date && this.date.getUTCMonth();
23195 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23197 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23198 v.removeClass('active');
23200 if(currentYear === year && k === currentMonth){
23201 v.addClass('active');
23204 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23205 v.addClass('disabled');
23211 year = parseInt(year/10, 10) * 10;
23213 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23215 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23218 for (var i = -1; i < 11; i++) {
23219 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23221 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23229 showMode: function(dir)
23232 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23235 Roo.each(this.picker().select('>div',true).elements, function(v){
23236 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23239 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23244 if(this.isInline) {
23248 this.picker().removeClass(['bottom', 'top']);
23250 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23252 * place to the top of element!
23256 this.picker().addClass('top');
23257 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23262 this.picker().addClass('bottom');
23264 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23267 parseDate : function(value)
23269 if(!value || value instanceof Date){
23272 var v = Date.parseDate(value, this.format);
23273 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23274 v = Date.parseDate(value, 'Y-m-d');
23276 if(!v && this.altFormats){
23277 if(!this.altFormatsArray){
23278 this.altFormatsArray = this.altFormats.split("|");
23280 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23281 v = Date.parseDate(value, this.altFormatsArray[i]);
23287 formatDate : function(date, fmt)
23289 return (!date || !(date instanceof Date)) ?
23290 date : date.dateFormat(fmt || this.format);
23293 onFocus : function()
23295 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23299 onBlur : function()
23301 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23303 var d = this.inputEl().getValue();
23310 showPopup : function()
23312 this.picker().show();
23316 this.fireEvent('showpopup', this, this.date);
23319 hidePopup : function()
23321 if(this.isInline) {
23324 this.picker().hide();
23325 this.viewMode = this.startViewMode;
23328 this.fireEvent('hidepopup', this, this.date);
23332 onMousedown: function(e)
23334 e.stopPropagation();
23335 e.preventDefault();
23340 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23344 setValue: function(v)
23346 if(this.fireEvent('beforeselect', this, v) !== false){
23347 var d = new Date(this.parseDate(v) ).clearTime();
23349 if(isNaN(d.getTime())){
23350 this.date = this.viewDate = '';
23351 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23355 v = this.formatDate(d);
23357 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23359 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23363 this.fireEvent('select', this, this.date);
23367 getValue: function()
23369 return this.formatDate(this.date);
23372 fireKey: function(e)
23374 if (!this.picker().isVisible()){
23375 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23381 var dateChanged = false,
23383 newDate, newViewDate;
23388 e.preventDefault();
23392 if (!this.keyboardNavigation) {
23395 dir = e.keyCode == 37 ? -1 : 1;
23398 newDate = this.moveYear(this.date, dir);
23399 newViewDate = this.moveYear(this.viewDate, dir);
23400 } else if (e.shiftKey){
23401 newDate = this.moveMonth(this.date, dir);
23402 newViewDate = this.moveMonth(this.viewDate, dir);
23404 newDate = new Date(this.date);
23405 newDate.setUTCDate(this.date.getUTCDate() + dir);
23406 newViewDate = new Date(this.viewDate);
23407 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23409 if (this.dateWithinRange(newDate)){
23410 this.date = newDate;
23411 this.viewDate = newViewDate;
23412 this.setValue(this.formatDate(this.date));
23414 e.preventDefault();
23415 dateChanged = true;
23420 if (!this.keyboardNavigation) {
23423 dir = e.keyCode == 38 ? -1 : 1;
23425 newDate = this.moveYear(this.date, dir);
23426 newViewDate = this.moveYear(this.viewDate, dir);
23427 } else if (e.shiftKey){
23428 newDate = this.moveMonth(this.date, dir);
23429 newViewDate = this.moveMonth(this.viewDate, dir);
23431 newDate = new Date(this.date);
23432 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23433 newViewDate = new Date(this.viewDate);
23434 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23436 if (this.dateWithinRange(newDate)){
23437 this.date = newDate;
23438 this.viewDate = newViewDate;
23439 this.setValue(this.formatDate(this.date));
23441 e.preventDefault();
23442 dateChanged = true;
23446 this.setValue(this.formatDate(this.date));
23448 e.preventDefault();
23451 this.setValue(this.formatDate(this.date));
23465 onClick: function(e)
23467 e.stopPropagation();
23468 e.preventDefault();
23470 var target = e.getTarget();
23472 if(target.nodeName.toLowerCase() === 'i'){
23473 target = Roo.get(target).dom.parentNode;
23476 var nodeName = target.nodeName;
23477 var className = target.className;
23478 var html = target.innerHTML;
23479 //Roo.log(nodeName);
23481 switch(nodeName.toLowerCase()) {
23483 switch(className) {
23489 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23490 switch(this.viewMode){
23492 this.viewDate = this.moveMonth(this.viewDate, dir);
23496 this.viewDate = this.moveYear(this.viewDate, dir);
23502 var date = new Date();
23503 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23505 this.setValue(this.formatDate(this.date));
23512 if (className.indexOf('disabled') < 0) {
23513 if (!this.viewDate) {
23514 this.viewDate = new Date();
23516 this.viewDate.setUTCDate(1);
23517 if (className.indexOf('month') > -1) {
23518 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23520 var year = parseInt(html, 10) || 0;
23521 this.viewDate.setUTCFullYear(year);
23525 if(this.singleMode){
23526 this.setValue(this.formatDate(this.viewDate));
23537 //Roo.log(className);
23538 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23539 var day = parseInt(html, 10) || 1;
23540 var year = (this.viewDate || new Date()).getUTCFullYear(),
23541 month = (this.viewDate || new Date()).getUTCMonth();
23543 if (className.indexOf('old') > -1) {
23550 } else if (className.indexOf('new') > -1) {
23558 //Roo.log([year,month,day]);
23559 this.date = this.UTCDate(year, month, day,0,0,0,0);
23560 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23562 //Roo.log(this.formatDate(this.date));
23563 this.setValue(this.formatDate(this.date));
23570 setStartDate: function(startDate)
23572 this.startDate = startDate || -Infinity;
23573 if (this.startDate !== -Infinity) {
23574 this.startDate = this.parseDate(this.startDate);
23577 this.updateNavArrows();
23580 setEndDate: function(endDate)
23582 this.endDate = endDate || Infinity;
23583 if (this.endDate !== Infinity) {
23584 this.endDate = this.parseDate(this.endDate);
23587 this.updateNavArrows();
23590 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23592 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23593 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23594 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23596 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23597 return parseInt(d, 10);
23600 this.updateNavArrows();
23603 updateNavArrows: function()
23605 if(this.singleMode){
23609 var d = new Date(this.viewDate),
23610 year = d.getUTCFullYear(),
23611 month = d.getUTCMonth();
23613 Roo.each(this.picker().select('.prev', true).elements, function(v){
23615 switch (this.viewMode) {
23618 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23624 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23631 Roo.each(this.picker().select('.next', true).elements, function(v){
23633 switch (this.viewMode) {
23636 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23642 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23650 moveMonth: function(date, dir)
23655 var new_date = new Date(date.valueOf()),
23656 day = new_date.getUTCDate(),
23657 month = new_date.getUTCMonth(),
23658 mag = Math.abs(dir),
23660 dir = dir > 0 ? 1 : -1;
23663 // If going back one month, make sure month is not current month
23664 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23666 return new_date.getUTCMonth() == month;
23668 // If going forward one month, make sure month is as expected
23669 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23671 return new_date.getUTCMonth() != new_month;
23673 new_month = month + dir;
23674 new_date.setUTCMonth(new_month);
23675 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23676 if (new_month < 0 || new_month > 11) {
23677 new_month = (new_month + 12) % 12;
23680 // For magnitudes >1, move one month at a time...
23681 for (var i=0; i<mag; i++) {
23682 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23683 new_date = this.moveMonth(new_date, dir);
23685 // ...then reset the day, keeping it in the new month
23686 new_month = new_date.getUTCMonth();
23687 new_date.setUTCDate(day);
23689 return new_month != new_date.getUTCMonth();
23692 // Common date-resetting loop -- if date is beyond end of month, make it
23695 new_date.setUTCDate(--day);
23696 new_date.setUTCMonth(new_month);
23701 moveYear: function(date, dir)
23703 return this.moveMonth(date, dir*12);
23706 dateWithinRange: function(date)
23708 return date >= this.startDate && date <= this.endDate;
23714 this.picker().remove();
23717 validateValue : function(value)
23719 if(this.getVisibilityEl().hasClass('hidden')){
23723 if(value.length < 1) {
23724 if(this.allowBlank){
23730 if(value.length < this.minLength){
23733 if(value.length > this.maxLength){
23737 var vt = Roo.form.VTypes;
23738 if(!vt[this.vtype](value, this)){
23742 if(typeof this.validator == "function"){
23743 var msg = this.validator(value);
23749 if(this.regex && !this.regex.test(value)){
23753 if(typeof(this.parseDate(value)) == 'undefined'){
23757 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23761 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23771 this.date = this.viewDate = '';
23773 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23778 Roo.apply(Roo.bootstrap.form.DateField, {
23789 html: '<i class="fa fa-arrow-left"/>'
23799 html: '<i class="fa fa-arrow-right"/>'
23841 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23842 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23843 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23844 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23845 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23858 navFnc: 'FullYear',
23863 navFnc: 'FullYear',
23868 Roo.apply(Roo.bootstrap.form.DateField, {
23872 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23876 cls: 'datepicker-days',
23880 cls: 'table-condensed',
23882 Roo.bootstrap.form.DateField.head,
23886 Roo.bootstrap.form.DateField.footer
23893 cls: 'datepicker-months',
23897 cls: 'table-condensed',
23899 Roo.bootstrap.form.DateField.head,
23900 Roo.bootstrap.form.DateField.content,
23901 Roo.bootstrap.form.DateField.footer
23908 cls: 'datepicker-years',
23912 cls: 'table-condensed',
23914 Roo.bootstrap.form.DateField.head,
23915 Roo.bootstrap.form.DateField.content,
23916 Roo.bootstrap.form.DateField.footer
23935 * @class Roo.bootstrap.form.TimeField
23936 * @extends Roo.bootstrap.form.Input
23937 * Bootstrap DateField class
23938 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23942 * Create a new TimeField
23943 * @param {Object} config The config object
23946 Roo.bootstrap.form.TimeField = function(config){
23947 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23951 * Fires when this field show.
23952 * @param {Roo.bootstrap.form.DateField} thisthis
23953 * @param {Mixed} date The date value
23958 * Fires when this field hide.
23959 * @param {Roo.bootstrap.form.DateField} this
23960 * @param {Mixed} date The date value
23965 * Fires when select a date.
23966 * @param {Roo.bootstrap.form.DateField} this
23967 * @param {Mixed} date The date value
23973 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23976 * @cfg {String} format
23977 * The default time format string which can be overriden for localization support. The format must be
23978 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23983 getAutoCreate : function()
23985 this.after = '<i class="fa far fa-clock"></i>';
23986 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23990 onRender: function(ct, position)
23993 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23995 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23997 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23999 this.pop = this.picker().select('>.datepicker-time',true).first();
24000 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24002 this.picker().on('mousedown', this.onMousedown, this);
24003 this.picker().on('click', this.onClick, this);
24005 this.picker().addClass('datepicker-dropdown');
24010 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24011 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24012 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24013 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24014 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24015 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24019 fireKey: function(e){
24020 if (!this.picker().isVisible()){
24021 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24027 e.preventDefault();
24035 this.onTogglePeriod();
24038 this.onIncrementMinutes();
24041 this.onDecrementMinutes();
24050 onClick: function(e) {
24051 e.stopPropagation();
24052 e.preventDefault();
24055 picker : function()
24057 return this.pickerEl;
24060 fillTime: function()
24062 var time = this.pop.select('tbody', true).first();
24064 time.dom.innerHTML = '';
24079 cls: 'hours-up fa fas fa-chevron-up'
24099 cls: 'minutes-up fa fas fa-chevron-up'
24120 cls: 'timepicker-hour',
24135 cls: 'timepicker-minute',
24150 cls: 'btn btn-primary period',
24172 cls: 'hours-down fa fas fa-chevron-down'
24192 cls: 'minutes-down fa fas fa-chevron-down'
24210 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24217 var hours = this.time.getHours();
24218 var minutes = this.time.getMinutes();
24231 hours = hours - 12;
24235 hours = '0' + hours;
24239 minutes = '0' + minutes;
24242 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24243 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24244 this.pop.select('button', true).first().dom.innerHTML = period;
24250 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24252 var cls = ['bottom'];
24254 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24261 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24265 //this.picker().setXY(20000,20000);
24266 this.picker().addClass(cls.join('-'));
24270 Roo.each(cls, function(c){
24275 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24276 //_this.picker().setTop(_this.inputEl().getHeight());
24280 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24282 //_this.picker().setTop(0 - _this.picker().getHeight());
24287 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24291 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24299 onFocus : function()
24301 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24305 onBlur : function()
24307 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24313 this.picker().show();
24318 this.fireEvent('show', this, this.date);
24323 this.picker().hide();
24326 this.fireEvent('hide', this, this.date);
24329 setTime : function()
24332 this.setValue(this.time.format(this.format));
24334 this.fireEvent('select', this, this.date);
24339 onMousedown: function(e){
24340 e.stopPropagation();
24341 e.preventDefault();
24344 onIncrementHours: function()
24346 Roo.log('onIncrementHours');
24347 this.time = this.time.add(Date.HOUR, 1);
24352 onDecrementHours: function()
24354 Roo.log('onDecrementHours');
24355 this.time = this.time.add(Date.HOUR, -1);
24359 onIncrementMinutes: function()
24361 Roo.log('onIncrementMinutes');
24362 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24363 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24367 onDecrementMinutes: function()
24369 Roo.log('onDecrementMinutes');
24370 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24371 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24375 onTogglePeriod: function()
24377 Roo.log('onTogglePeriod');
24378 this.time = this.time.add(Date.HOUR, 12);
24386 Roo.apply(Roo.bootstrap.form.TimeField, {
24390 cls: 'datepicker dropdown-menu',
24394 cls: 'datepicker-time',
24398 cls: 'table-condensed',
24427 cls: 'btn btn-info ok',
24455 * @class Roo.bootstrap.form.MonthField
24456 * @extends Roo.bootstrap.form.Input
24457 * Bootstrap MonthField class
24459 * @cfg {String} language default en
24462 * Create a new MonthField
24463 * @param {Object} config The config object
24466 Roo.bootstrap.form.MonthField = function(config){
24467 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24472 * Fires when this field show.
24473 * @param {Roo.bootstrap.form.MonthField} this
24474 * @param {Mixed} date The date value
24479 * Fires when this field hide.
24480 * @param {Roo.bootstrap.form.MonthField} this
24481 * @param {Mixed} date The date value
24486 * Fires when select a date.
24487 * @param {Roo.bootstrap.form.MonthField} this
24488 * @param {String} oldvalue The old value
24489 * @param {String} newvalue The new value
24495 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24497 onRender: function(ct, position)
24500 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24502 this.language = this.language || 'en';
24503 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24504 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24506 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24507 this.isInline = false;
24508 this.isInput = true;
24509 this.component = this.el.select('.add-on', true).first() || false;
24510 this.component = (this.component && this.component.length === 0) ? false : this.component;
24511 this.hasInput = this.component && this.inputEL().length;
24513 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24515 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24517 this.picker().on('mousedown', this.onMousedown, this);
24518 this.picker().on('click', this.onClick, this);
24520 this.picker().addClass('datepicker-dropdown');
24522 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24523 v.setStyle('width', '189px');
24530 if(this.isInline) {
24536 setValue: function(v, suppressEvent)
24538 var o = this.getValue();
24540 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24544 if(suppressEvent !== true){
24545 this.fireEvent('select', this, o, v);
24550 getValue: function()
24555 onClick: function(e)
24557 e.stopPropagation();
24558 e.preventDefault();
24560 var target = e.getTarget();
24562 if(target.nodeName.toLowerCase() === 'i'){
24563 target = Roo.get(target).dom.parentNode;
24566 var nodeName = target.nodeName;
24567 var className = target.className;
24568 var html = target.innerHTML;
24570 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24574 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24576 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24582 picker : function()
24584 return this.pickerEl;
24587 fillMonths: function()
24590 var months = this.picker().select('>.datepicker-months td', true).first();
24592 months.dom.innerHTML = '';
24598 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24601 months.createChild(month);
24610 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24611 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24614 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24615 e.removeClass('active');
24617 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24618 e.addClass('active');
24625 if(this.isInline) {
24629 this.picker().removeClass(['bottom', 'top']);
24631 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24633 * place to the top of element!
24637 this.picker().addClass('top');
24638 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24643 this.picker().addClass('bottom');
24645 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24648 onFocus : function()
24650 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24654 onBlur : function()
24656 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24658 var d = this.inputEl().getValue();
24667 this.picker().show();
24668 this.picker().select('>.datepicker-months', true).first().show();
24672 this.fireEvent('show', this, this.date);
24677 if(this.isInline) {
24680 this.picker().hide();
24681 this.fireEvent('hide', this, this.date);
24685 onMousedown: function(e)
24687 e.stopPropagation();
24688 e.preventDefault();
24693 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24697 fireKey: function(e)
24699 if (!this.picker().isVisible()){
24700 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24711 e.preventDefault();
24715 dir = e.keyCode == 37 ? -1 : 1;
24717 this.vIndex = this.vIndex + dir;
24719 if(this.vIndex < 0){
24723 if(this.vIndex > 11){
24727 if(isNaN(this.vIndex)){
24731 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24737 dir = e.keyCode == 38 ? -1 : 1;
24739 this.vIndex = this.vIndex + dir * 4;
24741 if(this.vIndex < 0){
24745 if(this.vIndex > 11){
24749 if(isNaN(this.vIndex)){
24753 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24758 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24759 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24763 e.preventDefault();
24766 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24767 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24783 this.picker().remove();
24788 Roo.apply(Roo.bootstrap.form.MonthField, {
24807 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24808 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24813 Roo.apply(Roo.bootstrap.form.MonthField, {
24817 cls: 'datepicker dropdown-menu roo-dynamic',
24821 cls: 'datepicker-months',
24825 cls: 'table-condensed',
24827 Roo.bootstrap.form.DateField.content
24847 * @class Roo.bootstrap.form.CheckBox
24848 * @extends Roo.bootstrap.form.Input
24849 * Bootstrap CheckBox class
24851 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24852 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24853 * @cfg {String} boxLabel The text that appears beside the checkbox
24854 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24855 * @cfg {Boolean} checked initnal the element
24856 * @cfg {Boolean} inline inline the element (default false)
24857 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24858 * @cfg {String} tooltip label tooltip
24861 * Create a new CheckBox
24862 * @param {Object} config The config object
24865 Roo.bootstrap.form.CheckBox = function(config){
24866 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24871 * Fires when the element is checked or unchecked.
24872 * @param {Roo.bootstrap.form.CheckBox} this This input
24873 * @param {Boolean} checked The new checked value
24878 * Fires when the element is click.
24879 * @param {Roo.bootstrap.form.CheckBox} this This input
24886 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24888 inputType: 'checkbox',
24897 // checkbox success does not make any sense really..
24902 getAutoCreate : function()
24904 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24910 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24913 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24919 type : this.inputType,
24920 value : this.inputValue,
24921 cls : 'roo-' + this.inputType, //'form-box',
24922 placeholder : this.placeholder || ''
24926 if(this.inputType != 'radio'){
24930 cls : 'roo-hidden-value',
24931 value : this.checked ? this.inputValue : this.valueOff
24936 if (this.weight) { // Validity check?
24937 cfg.cls += " " + this.inputType + "-" + this.weight;
24940 if (this.disabled) {
24941 input.disabled=true;
24945 input.checked = this.checked;
24950 input.name = this.name;
24952 if(this.inputType != 'radio'){
24953 hidden.name = this.name;
24954 input.name = '_hidden_' + this.name;
24959 input.cls += ' input-' + this.size;
24964 ['xs','sm','md','lg'].map(function(size){
24965 if (settings[size]) {
24966 cfg.cls += ' col-' + size + '-' + settings[size];
24970 var inputblock = input;
24972 if (this.before || this.after) {
24975 cls : 'input-group',
24980 inputblock.cn.push({
24982 cls : 'input-group-addon',
24987 inputblock.cn.push(input);
24989 if(this.inputType != 'radio'){
24990 inputblock.cn.push(hidden);
24994 inputblock.cn.push({
24996 cls : 'input-group-addon',
25002 var boxLabelCfg = false;
25008 //'for': id, // box label is handled by onclick - so no for...
25010 html: this.boxLabel
25013 boxLabelCfg.tooltip = this.tooltip;
25019 if (align ==='left' && this.fieldLabel.length) {
25020 // Roo.log("left and has label");
25025 cls : 'control-label',
25026 html : this.fieldLabel
25037 cfg.cn[1].cn.push(boxLabelCfg);
25040 if(this.labelWidth > 12){
25041 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25044 if(this.labelWidth < 13 && this.labelmd == 0){
25045 this.labelmd = this.labelWidth;
25048 if(this.labellg > 0){
25049 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25050 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25053 if(this.labelmd > 0){
25054 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25055 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25058 if(this.labelsm > 0){
25059 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25060 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25063 if(this.labelxs > 0){
25064 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25065 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25068 } else if ( this.fieldLabel.length) {
25069 // Roo.log(" label");
25073 tag: this.boxLabel ? 'span' : 'label',
25075 cls: 'control-label box-input-label',
25076 //cls : 'input-group-addon',
25077 html : this.fieldLabel
25084 cfg.cn.push(boxLabelCfg);
25089 // Roo.log(" no label && no align");
25090 cfg.cn = [ inputblock ] ;
25092 cfg.cn.push(boxLabelCfg);
25100 if(this.inputType != 'radio'){
25101 cfg.cn.push(hidden);
25109 * return the real input element.
25111 inputEl: function ()
25113 return this.el.select('input.roo-' + this.inputType,true).first();
25115 hiddenEl: function ()
25117 return this.el.select('input.roo-hidden-value',true).first();
25120 labelEl: function()
25122 return this.el.select('label.control-label',true).first();
25124 /* depricated... */
25128 return this.labelEl();
25131 boxLabelEl: function()
25133 return this.el.select('label.box-label',true).first();
25136 initEvents : function()
25138 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25140 this.inputEl().on('click', this.onClick, this);
25142 if (this.boxLabel) {
25143 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25146 this.startValue = this.getValue();
25149 Roo.bootstrap.form.CheckBox.register(this);
25153 onClick : function(e)
25155 if(this.fireEvent('click', this, e) !== false){
25156 this.setChecked(!this.checked);
25161 setChecked : function(state,suppressEvent)
25163 this.startValue = this.getValue();
25165 if(this.inputType == 'radio'){
25167 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25168 e.dom.checked = false;
25171 this.inputEl().dom.checked = true;
25173 this.inputEl().dom.value = this.inputValue;
25175 if(suppressEvent !== true){
25176 this.fireEvent('check', this, true);
25184 this.checked = state;
25186 this.inputEl().dom.checked = state;
25189 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25191 if(suppressEvent !== true){
25192 this.fireEvent('check', this, state);
25198 getValue : function()
25200 if(this.inputType == 'radio'){
25201 return this.getGroupValue();
25204 return this.hiddenEl().dom.value;
25208 getGroupValue : function()
25210 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25214 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25217 setValue : function(v,suppressEvent)
25219 if(this.inputType == 'radio'){
25220 this.setGroupValue(v, suppressEvent);
25224 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25229 setGroupValue : function(v, suppressEvent)
25231 this.startValue = this.getValue();
25233 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25234 e.dom.checked = false;
25236 if(e.dom.value == v){
25237 e.dom.checked = true;
25241 if(suppressEvent !== true){
25242 this.fireEvent('check', this, true);
25250 validate : function()
25252 if(this.getVisibilityEl().hasClass('hidden')){
25258 (this.inputType == 'radio' && this.validateRadio()) ||
25259 (this.inputType == 'checkbox' && this.validateCheckbox())
25265 this.markInvalid();
25269 validateRadio : function()
25271 if(this.getVisibilityEl().hasClass('hidden')){
25275 if(this.allowBlank){
25281 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25282 if(!e.dom.checked){
25294 validateCheckbox : function()
25297 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25298 //return (this.getValue() == this.inputValue) ? true : false;
25301 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25309 for(var i in group){
25310 if(group[i].el.isVisible(true)){
25318 for(var i in group){
25323 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25330 * Mark this field as valid
25332 markValid : function()
25336 this.fireEvent('valid', this);
25338 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25341 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25348 if(this.inputType == 'radio'){
25349 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25350 var fg = e.findParent('.form-group', false, true);
25351 if (Roo.bootstrap.version == 3) {
25352 fg.removeClass([_this.invalidClass, _this.validClass]);
25353 fg.addClass(_this.validClass);
25355 fg.removeClass(['is-valid', 'is-invalid']);
25356 fg.addClass('is-valid');
25364 var fg = this.el.findParent('.form-group', false, true);
25365 if (Roo.bootstrap.version == 3) {
25366 fg.removeClass([this.invalidClass, this.validClass]);
25367 fg.addClass(this.validClass);
25369 fg.removeClass(['is-valid', 'is-invalid']);
25370 fg.addClass('is-valid');
25375 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25381 for(var i in group){
25382 var fg = group[i].el.findParent('.form-group', false, true);
25383 if (Roo.bootstrap.version == 3) {
25384 fg.removeClass([this.invalidClass, this.validClass]);
25385 fg.addClass(this.validClass);
25387 fg.removeClass(['is-valid', 'is-invalid']);
25388 fg.addClass('is-valid');
25394 * Mark this field as invalid
25395 * @param {String} msg The validation message
25397 markInvalid : function(msg)
25399 if(this.allowBlank){
25405 this.fireEvent('invalid', this, msg);
25407 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25410 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25414 label.markInvalid();
25417 if(this.inputType == 'radio'){
25419 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25420 var fg = e.findParent('.form-group', false, true);
25421 if (Roo.bootstrap.version == 3) {
25422 fg.removeClass([_this.invalidClass, _this.validClass]);
25423 fg.addClass(_this.invalidClass);
25425 fg.removeClass(['is-invalid', 'is-valid']);
25426 fg.addClass('is-invalid');
25434 var fg = this.el.findParent('.form-group', false, true);
25435 if (Roo.bootstrap.version == 3) {
25436 fg.removeClass([_this.invalidClass, _this.validClass]);
25437 fg.addClass(_this.invalidClass);
25439 fg.removeClass(['is-invalid', 'is-valid']);
25440 fg.addClass('is-invalid');
25445 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25451 for(var i in group){
25452 var fg = group[i].el.findParent('.form-group', false, true);
25453 if (Roo.bootstrap.version == 3) {
25454 fg.removeClass([_this.invalidClass, _this.validClass]);
25455 fg.addClass(_this.invalidClass);
25457 fg.removeClass(['is-invalid', 'is-valid']);
25458 fg.addClass('is-invalid');
25464 clearInvalid : function()
25466 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25468 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25470 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25472 if (label && label.iconEl) {
25473 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25474 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25478 disable : function()
25480 if(this.inputType != 'radio'){
25481 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25488 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25489 _this.getActionEl().addClass(this.disabledClass);
25490 e.dom.disabled = true;
25494 this.disabled = true;
25495 this.fireEvent("disable", this);
25499 enable : function()
25501 if(this.inputType != 'radio'){
25502 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25509 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25510 _this.getActionEl().removeClass(this.disabledClass);
25511 e.dom.disabled = false;
25515 this.disabled = false;
25516 this.fireEvent("enable", this);
25520 setBoxLabel : function(v)
25525 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25531 Roo.apply(Roo.bootstrap.form.CheckBox, {
25536 * register a CheckBox Group
25537 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25539 register : function(checkbox)
25541 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25542 this.groups[checkbox.groupId] = {};
25545 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25549 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25553 * fetch a CheckBox Group based on the group ID
25554 * @param {string} the group ID
25555 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25557 get: function(groupId) {
25558 if (typeof(this.groups[groupId]) == 'undefined') {
25562 return this.groups[groupId] ;
25575 * @class Roo.bootstrap.form.Radio
25576 * @extends Roo.bootstrap.Component
25577 * Bootstrap Radio class
25578 * @cfg {String} boxLabel - the label associated
25579 * @cfg {String} value - the value of radio
25582 * Create a new Radio
25583 * @param {Object} config The config object
25585 Roo.bootstrap.form.Radio = function(config){
25586 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25590 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25596 getAutoCreate : function()
25600 cls : 'form-group radio',
25605 html : this.boxLabel
25613 initEvents : function()
25615 this.parent().register(this);
25617 this.el.on('click', this.onClick, this);
25621 onClick : function(e)
25623 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25624 this.setChecked(true);
25628 setChecked : function(state, suppressEvent)
25630 this.parent().setValue(this.value, suppressEvent);
25634 setBoxLabel : function(v)
25639 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25654 * @class Roo.bootstrap.form.SecurePass
25655 * @extends Roo.bootstrap.form.Input
25656 * Bootstrap SecurePass class
25660 * Create a new SecurePass
25661 * @param {Object} config The config object
25664 Roo.bootstrap.form.SecurePass = function (config) {
25665 // these go here, so the translation tool can replace them..
25667 PwdEmpty: "Please type a password, and then retype it to confirm.",
25668 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25669 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25670 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25671 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25672 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25673 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25674 TooWeak: "Your password is Too Weak."
25676 this.meterLabel = "Password strength:";
25677 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25678 this.meterClass = [
25679 "roo-password-meter-tooweak",
25680 "roo-password-meter-weak",
25681 "roo-password-meter-medium",
25682 "roo-password-meter-strong",
25683 "roo-password-meter-grey"
25688 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25691 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25693 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25695 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25696 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25697 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25698 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25699 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25700 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25701 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25711 * @cfg {String/Object} Label for the strength meter (defaults to
25712 * 'Password strength:')
25717 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25718 * ['Weak', 'Medium', 'Strong'])
25721 pwdStrengths: false,
25734 initEvents: function ()
25736 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25738 if (this.el.is('input[type=password]') && Roo.isSafari) {
25739 this.el.on('keydown', this.SafariOnKeyDown, this);
25742 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25745 onRender: function (ct, position)
25747 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25748 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25749 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25751 this.trigger.createChild({
25756 cls: 'roo-password-meter-grey col-xs-12',
25759 //width: this.meterWidth + 'px'
25763 cls: 'roo-password-meter-text'
25769 if (this.hideTrigger) {
25770 this.trigger.setDisplayed(false);
25772 this.setSize(this.width || '', this.height || '');
25775 onDestroy: function ()
25777 if (this.trigger) {
25778 this.trigger.removeAllListeners();
25779 this.trigger.remove();
25782 this.wrap.remove();
25784 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25787 checkStrength: function ()
25789 var pwd = this.inputEl().getValue();
25790 if (pwd == this._lastPwd) {
25795 if (this.ClientSideStrongPassword(pwd)) {
25797 } else if (this.ClientSideMediumPassword(pwd)) {
25799 } else if (this.ClientSideWeakPassword(pwd)) {
25805 Roo.log('strength1: ' + strength);
25807 //var pm = this.trigger.child('div/div/div').dom;
25808 var pm = this.trigger.child('div/div');
25809 pm.removeClass(this.meterClass);
25810 pm.addClass(this.meterClass[strength]);
25813 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25815 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25817 this._lastPwd = pwd;
25821 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25823 this._lastPwd = '';
25825 var pm = this.trigger.child('div/div');
25826 pm.removeClass(this.meterClass);
25827 pm.addClass('roo-password-meter-grey');
25830 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25833 this.inputEl().dom.type='password';
25836 validateValue: function (value)
25838 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25841 if (value.length == 0) {
25842 if (this.allowBlank) {
25843 this.clearInvalid();
25847 this.markInvalid(this.errors.PwdEmpty);
25848 this.errorMsg = this.errors.PwdEmpty;
25856 if (!value.match(/[\x21-\x7e]+/)) {
25857 this.markInvalid(this.errors.PwdBadChar);
25858 this.errorMsg = this.errors.PwdBadChar;
25861 if (value.length < 6) {
25862 this.markInvalid(this.errors.PwdShort);
25863 this.errorMsg = this.errors.PwdShort;
25866 if (value.length > 16) {
25867 this.markInvalid(this.errors.PwdLong);
25868 this.errorMsg = this.errors.PwdLong;
25872 if (this.ClientSideStrongPassword(value)) {
25874 } else if (this.ClientSideMediumPassword(value)) {
25876 } else if (this.ClientSideWeakPassword(value)) {
25883 if (strength < 2) {
25884 //this.markInvalid(this.errors.TooWeak);
25885 this.errorMsg = this.errors.TooWeak;
25890 console.log('strength2: ' + strength);
25892 //var pm = this.trigger.child('div/div/div').dom;
25894 var pm = this.trigger.child('div/div');
25895 pm.removeClass(this.meterClass);
25896 pm.addClass(this.meterClass[strength]);
25898 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25900 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25902 this.errorMsg = '';
25906 CharacterSetChecks: function (type)
25909 this.fResult = false;
25912 isctype: function (character, type)
25915 case this.kCapitalLetter:
25916 if (character >= 'A' && character <= 'Z') {
25921 case this.kSmallLetter:
25922 if (character >= 'a' && character <= 'z') {
25928 if (character >= '0' && character <= '9') {
25933 case this.kPunctuation:
25934 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25945 IsLongEnough: function (pwd, size)
25947 return !(pwd == null || isNaN(size) || pwd.length < size);
25950 SpansEnoughCharacterSets: function (word, nb)
25952 if (!this.IsLongEnough(word, nb))
25957 var characterSetChecks = new Array(
25958 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25959 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25962 for (var index = 0; index < word.length; ++index) {
25963 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25964 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25965 characterSetChecks[nCharSet].fResult = true;
25972 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25973 if (characterSetChecks[nCharSet].fResult) {
25978 if (nCharSets < nb) {
25984 ClientSideStrongPassword: function (pwd)
25986 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25989 ClientSideMediumPassword: function (pwd)
25991 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25994 ClientSideWeakPassword: function (pwd)
25996 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25999 });Roo.rtf = {}; // namespace
26000 Roo.rtf.Hex = function(hex)
26004 Roo.rtf.Paragraph = function(opts)
26006 this.content = []; ///??? is that used?
26007 };Roo.rtf.Span = function(opts)
26009 this.value = opts.value;
26012 Roo.rtf.Group = function(parent)
26014 // we dont want to acutally store parent - it will make debug a nightmare..
26022 Roo.rtf.Group.prototype = {
26026 addContent : function(node) {
26027 // could set styles...
26028 this.content.push(node);
26030 addChild : function(cn)
26034 // only for images really...
26035 toDataURL : function()
26037 var mimetype = false;
26039 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26040 mimetype = "image/png";
26042 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26043 mimetype = "image/jpeg";
26046 return 'about:blank'; // ?? error?
26050 var hexstring = this.content[this.content.length-1].value;
26052 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26053 return String.fromCharCode(parseInt(a, 16));
26058 // this looks like it's normally the {rtf{ .... }}
26059 Roo.rtf.Document = function()
26061 // we dont want to acutally store parent - it will make debug a nightmare..
26067 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26068 addChild : function(cn)
26072 case 'rtlch': // most content seems to be inside this??
26075 this.rtlch.push(cn);
26078 this[cn.type] = cn;
26083 getElementsByType : function(type)
26086 this._getElementsByType(type, ret, this.cn, 'rtf');
26089 _getElementsByType : function (type, ret, search_array, path)
26091 search_array.forEach(function(n,i) {
26092 if (n.type == type) {
26093 n.path = path + '/' + n.type + ':' + i;
26096 if (n.cn.length > 0) {
26097 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26104 Roo.rtf.Ctrl = function(opts)
26106 this.value = opts.value;
26107 this.param = opts.param;
26112 * based on this https://github.com/iarna/rtf-parser
26113 * it's really only designed to extract pict from pasted RTF
26117 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26126 Roo.rtf.Parser = function(text) {
26127 //super({objectMode: true})
26129 this.parserState = this.parseText;
26131 // these are for interpeter...
26133 ///this.parserState = this.parseTop
26134 this.groupStack = [];
26135 this.hexStore = [];
26138 this.groups = []; // where we put the return.
26140 for (var ii = 0; ii < text.length; ++ii) {
26143 if (text[ii] === '\n') {
26149 this.parserState(text[ii]);
26155 Roo.rtf.Parser.prototype = {
26156 text : '', // string being parsed..
26158 controlWordParam : '',
26162 groupStack : false,
26167 row : 1, // reportin?
26171 push : function (el)
26173 var m = 'cmd'+ el.type;
26174 if (typeof(this[m]) == 'undefined') {
26175 Roo.log('invalid cmd:' + el.type);
26181 flushHexStore : function()
26183 if (this.hexStore.length < 1) {
26186 var hexstr = this.hexStore.map(
26191 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26194 this.hexStore.splice(0)
26198 cmdgroupstart : function()
26200 this.flushHexStore();
26202 this.groupStack.push(this.group);
26205 if (this.doc === false) {
26206 this.group = this.doc = new Roo.rtf.Document();
26210 this.group = new Roo.rtf.Group(this.group);
26212 cmdignorable : function()
26214 this.flushHexStore();
26215 this.group.ignorable = true;
26217 cmdendparagraph : function()
26219 this.flushHexStore();
26220 this.group.addContent(new Roo.rtf.Paragraph());
26222 cmdgroupend : function ()
26224 this.flushHexStore();
26225 var endingGroup = this.group;
26228 this.group = this.groupStack.pop();
26230 this.group.addChild(endingGroup);
26235 var doc = this.group || this.doc;
26236 //if (endingGroup instanceof FontTable) {
26237 // doc.fonts = endingGroup.table
26238 //} else if (endingGroup instanceof ColorTable) {
26239 // doc.colors = endingGroup.table
26240 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26241 if (endingGroup.ignorable === false) {
26243 this.groups.push(endingGroup);
26244 // Roo.log( endingGroup );
26246 //Roo.each(endingGroup.content, function(item)) {
26247 // doc.addContent(item);
26249 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26252 cmdtext : function (cmd)
26254 this.flushHexStore();
26255 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26256 //this.group = this.doc
26257 return; // we really don't care about stray text...
26259 this.group.addContent(new Roo.rtf.Span(cmd));
26261 cmdcontrolword : function (cmd)
26263 this.flushHexStore();
26264 if (!this.group.type) {
26265 this.group.type = cmd.value;
26268 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26269 // we actually don't care about ctrl words...
26272 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26273 if (this[method]) {
26274 this[method](cmd.param)
26276 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26280 cmdhexchar : function(cmd) {
26281 this.hexStore.push(cmd);
26283 cmderror : function(cmd) {
26289 if (this.text !== '\u0000') this.emitText()
26295 parseText : function(c)
26298 this.parserState = this.parseEscapes;
26299 } else if (c === '{') {
26300 this.emitStartGroup();
26301 } else if (c === '}') {
26302 this.emitEndGroup();
26303 } else if (c === '\x0A' || c === '\x0D') {
26304 // cr/lf are noise chars
26310 parseEscapes: function (c)
26312 if (c === '\\' || c === '{' || c === '}') {
26314 this.parserState = this.parseText;
26316 this.parserState = this.parseControlSymbol;
26317 this.parseControlSymbol(c);
26320 parseControlSymbol: function(c)
26323 this.text += '\u00a0'; // nbsp
26324 this.parserState = this.parseText
26325 } else if (c === '-') {
26326 this.text += '\u00ad'; // soft hyphen
26327 } else if (c === '_') {
26328 this.text += '\u2011'; // non-breaking hyphen
26329 } else if (c === '*') {
26330 this.emitIgnorable();
26331 this.parserState = this.parseText;
26332 } else if (c === "'") {
26333 this.parserState = this.parseHexChar;
26334 } else if (c === '|') { // formula cacter
26335 this.emitFormula();
26336 this.parserState = this.parseText;
26337 } else if (c === ':') { // subentry in an index entry
26338 this.emitIndexSubEntry();
26339 this.parserState = this.parseText;
26340 } else if (c === '\x0a') {
26341 this.emitEndParagraph();
26342 this.parserState = this.parseText;
26343 } else if (c === '\x0d') {
26344 this.emitEndParagraph();
26345 this.parserState = this.parseText;
26347 this.parserState = this.parseControlWord;
26348 this.parseControlWord(c);
26351 parseHexChar: function (c)
26353 if (/^[A-Fa-f0-9]$/.test(c)) {
26355 if (this.hexChar.length >= 2) {
26356 this.emitHexChar();
26357 this.parserState = this.parseText;
26361 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26362 this.parserState = this.parseText;
26365 parseControlWord : function(c)
26368 this.emitControlWord();
26369 this.parserState = this.parseText;
26370 } else if (/^[-\d]$/.test(c)) {
26371 this.parserState = this.parseControlWordParam;
26372 this.controlWordParam += c;
26373 } else if (/^[A-Za-z]$/.test(c)) {
26374 this.controlWord += c;
26376 this.emitControlWord();
26377 this.parserState = this.parseText;
26381 parseControlWordParam : function (c) {
26382 if (/^\d$/.test(c)) {
26383 this.controlWordParam += c;
26384 } else if (c === ' ') {
26385 this.emitControlWord();
26386 this.parserState = this.parseText;
26388 this.emitControlWord();
26389 this.parserState = this.parseText;
26397 emitText : function () {
26398 if (this.text === '') {
26410 emitControlWord : function ()
26413 if (this.controlWord === '') {
26414 // do we want to track this - it seems just to cause problems.
26415 //this.emitError('empty control word');
26418 type: 'controlword',
26419 value: this.controlWord,
26420 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26426 this.controlWord = '';
26427 this.controlWordParam = '';
26429 emitStartGroup : function ()
26433 type: 'groupstart',
26439 emitEndGroup : function ()
26449 emitIgnorable : function ()
26459 emitHexChar : function ()
26464 value: this.hexChar,
26471 emitError : function (message)
26479 char: this.cpos //,
26480 //stack: new Error().stack
26483 emitEndParagraph : function () {
26486 type: 'endparagraph',
26494 Roo.htmleditor = {};
26497 * @class Roo.htmleditor.Filter
26498 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26499 * @cfg {DomElement} node The node to iterate and filter
26500 * @cfg {boolean|String|Array} tag Tags to replace
26502 * Create a new Filter.
26503 * @param {Object} config Configuration options
26508 Roo.htmleditor.Filter = function(cfg) {
26509 Roo.apply(this.cfg);
26510 // this does not actually call walk as it's really just a abstract class
26514 Roo.htmleditor.Filter.prototype = {
26520 // overrride to do replace comments.
26521 replaceComment : false,
26523 // overrride to do replace or do stuff with tags..
26524 replaceTag : false,
26526 walk : function(dom)
26528 Roo.each( Array.from(dom.childNodes), function( e ) {
26531 case e.nodeType == 8 && this.replaceComment !== false: // comment
26532 this.replaceComment(e);
26535 case e.nodeType != 1: //not a node.
26538 case this.tag === true: // everything
26539 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26540 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26541 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26542 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26543 if (this.replaceTag && false === this.replaceTag(e)) {
26546 if (e.hasChildNodes()) {
26551 default: // tags .. that do not match.
26552 if (e.hasChildNodes()) {
26562 removeNodeKeepChildren : function( node)
26565 ar = Array.from(node.childNodes);
26566 for (var i = 0; i < ar.length; i++) {
26568 node.removeChild(ar[i]);
26569 // what if we need to walk these???
26570 node.parentNode.insertBefore(ar[i], node);
26573 node.parentNode.removeChild(node);
26578 * @class Roo.htmleditor.FilterAttributes
26579 * clean attributes and styles including http:// etc.. in attribute
26581 * Run a new Attribute Filter
26582 * @param {Object} config Configuration options
26584 Roo.htmleditor.FilterAttributes = function(cfg)
26586 Roo.apply(this, cfg);
26587 this.attrib_black = this.attrib_black || [];
26588 this.attrib_white = this.attrib_white || [];
26590 this.attrib_clean = this.attrib_clean || [];
26591 this.style_white = this.style_white || [];
26592 this.style_black = this.style_black || [];
26593 this.walk(cfg.node);
26596 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26598 tag: true, // all tags
26600 attrib_black : false, // array
26601 attrib_clean : false,
26602 attrib_white : false,
26604 style_white : false,
26605 style_black : false,
26608 replaceTag : function(node)
26610 if (!node.attributes || !node.attributes.length) {
26614 for (var i = node.attributes.length-1; i > -1 ; i--) {
26615 var a = node.attributes[i];
26617 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26618 node.removeAttribute(a.name);
26624 if (a.name.toLowerCase().substr(0,2)=='on') {
26625 node.removeAttribute(a.name);
26630 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26631 node.removeAttribute(a.name);
26634 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26635 this.cleanAttr(node,a.name,a.value); // fixme..
26638 if (a.name == 'style') {
26639 this.cleanStyle(node,a.name,a.value);
26642 /// clean up MS crap..
26643 // tecnically this should be a list of valid class'es..
26646 if (a.name == 'class') {
26647 if (a.value.match(/^Mso/)) {
26648 node.removeAttribute('class');
26651 if (a.value.match(/^body$/)) {
26652 node.removeAttribute('class');
26662 return true; // clean children
26665 cleanAttr: function(node, n,v)
26668 if (v.match(/^\./) || v.match(/^\//)) {
26671 if (v.match(/^(http|https):\/\//)
26672 || v.match(/^mailto:/)
26673 || v.match(/^ftp:/)
26674 || v.match(/^data:/)
26678 if (v.match(/^#/)) {
26681 if (v.match(/^\{/)) { // allow template editing.
26684 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26685 node.removeAttribute(n);
26688 cleanStyle : function(node, n,v)
26690 if (v.match(/expression/)) { //XSS?? should we even bother..
26691 node.removeAttribute(n);
26695 var parts = v.split(/;/);
26698 Roo.each(parts, function(p) {
26699 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26703 var l = p.split(':').shift().replace(/\s+/g,'');
26704 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26706 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26710 // only allow 'c whitelisted system attributes'
26711 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26719 if (clean.length) {
26720 node.setAttribute(n, clean.join(';'));
26722 node.removeAttribute(n);
26731 * @class Roo.htmleditor.FilterBlack
26732 * remove blacklisted elements.
26734 * Run a new Blacklisted Filter
26735 * @param {Object} config Configuration options
26738 Roo.htmleditor.FilterBlack = function(cfg)
26740 Roo.apply(this, cfg);
26741 this.walk(cfg.node);
26744 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26746 tag : true, // all elements.
26748 replaceTag : function(n)
26750 n.parentNode.removeChild(n);
26754 * @class Roo.htmleditor.FilterComment
26757 * Run a new Comments Filter
26758 * @param {Object} config Configuration options
26760 Roo.htmleditor.FilterComment = function(cfg)
26762 this.walk(cfg.node);
26765 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26768 replaceComment : function(n)
26770 n.parentNode.removeChild(n);
26773 * @class Roo.htmleditor.FilterKeepChildren
26774 * remove tags but keep children
26776 * Run a new Keep Children Filter
26777 * @param {Object} config Configuration options
26780 Roo.htmleditor.FilterKeepChildren = function(cfg)
26782 Roo.apply(this, cfg);
26783 if (this.tag === false) {
26784 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26787 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26788 this.cleanNamespace = true;
26791 this.walk(cfg.node);
26794 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26796 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26798 replaceTag : function(node)
26800 // walk children...
26801 //Roo.log(node.tagName);
26802 var ar = Array.from(node.childNodes);
26805 for (var i = 0; i < ar.length; i++) {
26807 if (e.nodeType == 1) {
26809 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26810 || // array and it matches
26811 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26813 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26815 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26817 this.replaceTag(ar[i]); // child is blacklisted as well...
26822 ar = Array.from(node.childNodes);
26823 for (var i = 0; i < ar.length; i++) {
26825 node.removeChild(ar[i]);
26826 // what if we need to walk these???
26827 node.parentNode.insertBefore(ar[i], node);
26828 if (this.tag !== false) {
26833 //Roo.log("REMOVE:" + node.tagName);
26834 node.parentNode.removeChild(node);
26835 return false; // don't walk children
26840 * @class Roo.htmleditor.FilterParagraph
26841 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26842 * like on 'push' to remove the <p> tags and replace them with line breaks.
26844 * Run a new Paragraph Filter
26845 * @param {Object} config Configuration options
26848 Roo.htmleditor.FilterParagraph = function(cfg)
26850 // no need to apply config.
26851 this.walk(cfg.node);
26854 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26861 replaceTag : function(node)
26864 if (node.childNodes.length == 1 &&
26865 node.childNodes[0].nodeType == 3 &&
26866 node.childNodes[0].textContent.trim().length < 1
26868 // remove and replace with '<BR>';
26869 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26870 return false; // no need to walk..
26872 var ar = Array.from(node.childNodes);
26873 for (var i = 0; i < ar.length; i++) {
26874 node.removeChild(ar[i]);
26875 // what if we need to walk these???
26876 node.parentNode.insertBefore(ar[i], node);
26878 // now what about this?
26882 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26883 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26884 node.parentNode.removeChild(node);
26891 * @class Roo.htmleditor.FilterSpan
26892 * filter span's with no attributes out..
26894 * Run a new Span Filter
26895 * @param {Object} config Configuration options
26898 Roo.htmleditor.FilterSpan = function(cfg)
26900 // no need to apply config.
26901 this.walk(cfg.node);
26904 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26910 replaceTag : function(node)
26912 if (node.attributes && node.attributes.length > 0) {
26913 return true; // walk if there are any.
26915 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26921 * @class Roo.htmleditor.FilterTableWidth
26922 try and remove table width data - as that frequently messes up other stuff.
26924 * was cleanTableWidths.
26926 * Quite often pasting from word etc.. results in tables with column and widths.
26927 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26930 * Run a new Table Filter
26931 * @param {Object} config Configuration options
26934 Roo.htmleditor.FilterTableWidth = function(cfg)
26936 // no need to apply config.
26937 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26938 this.walk(cfg.node);
26941 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26946 replaceTag: function(node) {
26950 if (node.hasAttribute('width')) {
26951 node.removeAttribute('width');
26955 if (node.hasAttribute("style")) {
26958 var styles = node.getAttribute("style").split(";");
26960 Roo.each(styles, function(s) {
26961 if (!s.match(/:/)) {
26964 var kv = s.split(":");
26965 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26968 // what ever is left... we allow.
26971 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26972 if (!nstyle.length) {
26973 node.removeAttribute('style');
26977 return true; // continue doing children..
26980 * @class Roo.htmleditor.FilterWord
26981 * try and clean up all the mess that Word generates.
26983 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26986 * Run a new Span Filter
26987 * @param {Object} config Configuration options
26990 Roo.htmleditor.FilterWord = function(cfg)
26992 // no need to apply config.
26993 this.replaceDocBullets(cfg.node);
26995 this.replaceAname(cfg.node);
26996 // this is disabled as the removal is done by other filters;
26997 // this.walk(cfg.node);
27002 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27008 * Clean up MS wordisms...
27010 replaceTag : function(node)
27013 // no idea what this does - span with text, replaceds with just text.
27015 node.nodeName == 'SPAN' &&
27016 !node.hasAttributes() &&
27017 node.childNodes.length == 1 &&
27018 node.firstChild.nodeName == "#text"
27020 var textNode = node.firstChild;
27021 node.removeChild(textNode);
27022 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27023 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27025 node.parentNode.insertBefore(textNode, node);
27026 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27027 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27030 node.parentNode.removeChild(node);
27031 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27036 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27037 node.parentNode.removeChild(node);
27038 return false; // dont do chidlren
27040 //Roo.log(node.tagName);
27041 // remove - but keep children..
27042 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27043 //Roo.log('-- removed');
27044 while (node.childNodes.length) {
27045 var cn = node.childNodes[0];
27046 node.removeChild(cn);
27047 node.parentNode.insertBefore(cn, node);
27048 // move node to parent - and clean it..
27049 if (cn.nodeType == 1) {
27050 this.replaceTag(cn);
27054 node.parentNode.removeChild(node);
27055 /// no need to iterate chidlren = it's got none..
27056 //this.iterateChildren(node, this.cleanWord);
27057 return false; // no need to iterate children.
27060 if (node.className.length) {
27062 var cn = node.className.split(/\W+/);
27064 Roo.each(cn, function(cls) {
27065 if (cls.match(/Mso[a-zA-Z]+/)) {
27070 node.className = cna.length ? cna.join(' ') : '';
27072 node.removeAttribute("class");
27076 if (node.hasAttribute("lang")) {
27077 node.removeAttribute("lang");
27080 if (node.hasAttribute("style")) {
27082 var styles = node.getAttribute("style").split(";");
27084 Roo.each(styles, function(s) {
27085 if (!s.match(/:/)) {
27088 var kv = s.split(":");
27089 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27092 // what ever is left... we allow.
27095 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27096 if (!nstyle.length) {
27097 node.removeAttribute('style');
27100 return true; // do children
27106 styleToObject: function(node)
27108 var styles = (node.getAttribute("style") || '').split(";");
27110 Roo.each(styles, function(s) {
27111 if (!s.match(/:/)) {
27114 var kv = s.split(":");
27116 // what ever is left... we allow.
27117 ret[kv[0].trim()] = kv[1];
27123 replaceAname : function (doc)
27125 // replace all the a/name without..
27126 var aa = Array.from(doc.getElementsByTagName('a'));
27127 for (var i = 0; i < aa.length; i++) {
27129 if (a.hasAttribute("name")) {
27130 a.removeAttribute("name");
27132 if (a.hasAttribute("href")) {
27135 // reparent children.
27136 this.removeNodeKeepChildren(a);
27146 replaceDocBullets : function(doc)
27148 // this is a bit odd - but it appears some indents use ql-indent-1
27149 //Roo.log(doc.innerHTML);
27151 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27152 for( var i = 0; i < listpara.length; i ++) {
27153 listpara[i].className = "MsoListParagraph";
27156 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27157 for( var i = 0; i < listpara.length; i ++) {
27158 listpara[i].className = "MsoListParagraph";
27160 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27161 for( var i = 0; i < listpara.length; i ++) {
27162 listpara[i].className = "MsoListParagraph";
27164 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27165 for( var i = 0; i < listpara.length; i ++) {
27166 listpara[i].className = "MsoListParagraph";
27169 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27170 var htwo = Array.from(doc.getElementsByTagName('h2'));
27171 for( var i = 0; i < htwo.length; i ++) {
27172 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27173 htwo[i].className = "MsoListParagraph";
27176 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27177 for( var i = 0; i < listpara.length; i ++) {
27178 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27179 listpara[i].className = "MsoListParagraph";
27181 listpara[i].className = "MsoNormalx";
27185 listpara = doc.getElementsByClassName('MsoListParagraph');
27186 // Roo.log(doc.innerHTML);
27190 while(listpara.length) {
27192 this.replaceDocBullet(listpara.item(0));
27199 replaceDocBullet : function(p)
27201 // gather all the siblings.
27203 parent = p.parentNode,
27204 doc = parent.ownerDocument,
27207 var listtype = 'ul';
27209 if (ns.nodeType != 1) {
27210 ns = ns.nextSibling;
27213 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27216 var spans = ns.getElementsByTagName('span');
27217 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27219 ns = ns.nextSibling;
27221 if (spans.length && spans[0].hasAttribute('style')) {
27222 var style = this.styleToObject(spans[0]);
27223 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
27230 var spans = ns.getElementsByTagName('span');
27231 if (!spans.length) {
27234 var has_list = false;
27235 for(var i = 0; i < spans.length; i++) {
27236 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27245 ns = ns.nextSibling;
27249 if (!items.length) {
27254 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27255 parent.insertBefore(ul, p);
27257 var stack = [ ul ];
27258 var last_li = false;
27260 var margin_to_depth = {};
27263 items.forEach(function(n, ipos) {
27264 //Roo.log("got innertHMLT=" + n.innerHTML);
27266 var spans = n.getElementsByTagName('span');
27267 if (!spans.length) {
27268 //Roo.log("No spans found");
27270 parent.removeChild(n);
27273 return; // skip it...
27279 for(var i = 0; i < spans.length; i++) {
27281 style = this.styleToObject(spans[i]);
27282 if (typeof(style['mso-list']) == 'undefined') {
27285 if (listtype == 'ol') {
27286 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27288 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27291 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27292 style = this.styleToObject(n); // mo-list is from the parent node.
27293 if (typeof(style['mso-list']) == 'undefined') {
27294 //Roo.log("parent is missing level");
27296 parent.removeChild(n);
27301 var margin = style['margin-left'];
27302 if (typeof(margin_to_depth[margin]) == 'undefined') {
27304 margin_to_depth[margin] = max_margins;
27306 nlvl = margin_to_depth[margin] ;
27310 var nul = doc.createElement(listtype); // what about number lists...
27312 last_li = doc.createElement('li');
27313 stack[lvl].appendChild(last_li);
27315 last_li.appendChild(nul);
27321 // not starting at 1..
27322 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27323 stack[nlvl].setAttribute("start", num);
27326 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27328 nli.innerHTML = n.innerHTML;
27329 //Roo.log("innerHTML = " + n.innerHTML);
27330 parent.removeChild(n);
27346 * @class Roo.htmleditor.FilterStyleToTag
27347 * part of the word stuff... - certain 'styles' should be converted to tags.
27349 * font-weight: bold -> bold
27350 * ?? super / subscrit etc..
27353 * Run a new style to tag filter.
27354 * @param {Object} config Configuration options
27356 Roo.htmleditor.FilterStyleToTag = function(cfg)
27360 B : [ 'fontWeight' , 'bold'],
27361 I : [ 'fontStyle' , 'italic'],
27362 //pre : [ 'font-style' , 'italic'],
27363 // h1.. h6 ?? font-size?
27364 SUP : [ 'verticalAlign' , 'super' ],
27365 SUB : [ 'verticalAlign' , 'sub' ]
27370 Roo.apply(this, cfg);
27373 this.walk(cfg.node);
27380 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27382 tag: true, // all tags
27387 replaceTag : function(node)
27391 if (node.getAttribute("style") === null) {
27395 for (var k in this.tags) {
27396 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27398 node.style.removeProperty(this.tags[k][0]);
27401 if (!inject.length) {
27404 var cn = Array.from(node.childNodes);
27406 Roo.each(inject, function(t) {
27407 var nc = node.ownerDocument.createElement(t);
27408 nn.appendChild(nc);
27411 for(var i = 0;i < cn.length;cn++) {
27412 node.removeChild(cn[i]);
27413 nn.appendChild(cn[i]);
27415 return true /// iterate thru
27419 * @class Roo.htmleditor.FilterLongBr
27420 * BR/BR/BR - keep a maximum of 2...
27422 * Run a new Long BR Filter
27423 * @param {Object} config Configuration options
27426 Roo.htmleditor.FilterLongBr = function(cfg)
27428 // no need to apply config.
27429 this.walk(cfg.node);
27432 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27439 replaceTag : function(node)
27442 var ps = node.nextSibling;
27443 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27444 ps = ps.nextSibling;
27447 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27448 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27452 if (!ps || ps.nodeType != 1) {
27456 if (!ps || ps.tagName != 'BR') {
27465 if (!node.previousSibling) {
27468 var ps = node.previousSibling;
27470 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27471 ps = ps.previousSibling;
27473 if (!ps || ps.nodeType != 1) {
27476 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27477 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27481 node.parentNode.removeChild(node); // remove me...
27483 return false; // no need to do children
27490 * @class Roo.htmleditor.FilterBlock
27491 * removes id / data-block and contenteditable that are associated with blocks
27492 * usage should be done on a cloned copy of the dom
27494 * Run a new Attribute Filter { node : xxxx }}
27495 * @param {Object} config Configuration options
27497 Roo.htmleditor.FilterBlock = function(cfg)
27499 Roo.apply(this, cfg);
27500 var qa = cfg.node.querySelectorAll;
27501 this.removeAttributes('data-block');
27502 this.removeAttributes('contenteditable');
27503 this.removeAttributes('id');
27507 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27509 node: true, // all tags
27512 removeAttributes : function(attr)
27514 var ar = this.node.querySelectorAll('*[' + attr + ']');
27515 for (var i =0;i<ar.length;i++) {
27516 ar[i].removeAttribute(attr);
27525 * This is based loosely on tinymce
27526 * @class Roo.htmleditor.TidySerializer
27527 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27529 * @method Serializer
27530 * @param {Object} settings Name/value settings object.
27534 Roo.htmleditor.TidySerializer = function(settings)
27536 Roo.apply(this, settings);
27538 this.writer = new Roo.htmleditor.TidyWriter(settings);
27543 Roo.htmleditor.TidySerializer.prototype = {
27546 * @param {boolean} inner do the inner of the node.
27553 * Serializes the specified node into a string.
27556 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27557 * @method serialize
27558 * @param {DomElement} node Node instance to serialize.
27559 * @return {String} String with HTML based on DOM tree.
27561 serialize : function(node) {
27563 // = settings.validate;
27564 var writer = this.writer;
27568 3: function(node) {
27570 writer.text(node.nodeValue, node);
27573 8: function(node) {
27574 writer.comment(node.nodeValue);
27576 // Processing instruction
27577 7: function(node) {
27578 writer.pi(node.name, node.nodeValue);
27581 10: function(node) {
27582 writer.doctype(node.nodeValue);
27585 4: function(node) {
27586 writer.cdata(node.nodeValue);
27588 // Document fragment
27589 11: function(node) {
27590 node = node.firstChild;
27596 node = node.nextSibling
27601 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27602 return writer.getContent();
27605 walk: function(node)
27607 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27608 handler = this.handlers[node.nodeType];
27615 var name = node.nodeName;
27616 var isEmpty = node.childNodes.length < 1;
27618 var writer = this.writer;
27619 var attrs = node.attributes;
27622 writer.start(node.nodeName, attrs, isEmpty, node);
27626 node = node.firstChild;
27633 node = node.nextSibling;
27639 // Serialize element and treat all non elements as fragments
27644 * This is based loosely on tinymce
27645 * @class Roo.htmleditor.TidyWriter
27646 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27649 * - not tested much with 'PRE' formated elements.
27655 Roo.htmleditor.TidyWriter = function(settings)
27658 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27659 Roo.apply(this, settings);
27663 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27666 Roo.htmleditor.TidyWriter.prototype = {
27673 // part of state...
27677 last_inline : false,
27682 * Writes the a start element such as <p id="a">.
27685 * @param {String} name Name of the element.
27686 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27687 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27689 start: function(name, attrs, empty, node)
27691 var i, l, attr, value;
27693 // there are some situations where adding line break && indentation will not work. will not work.
27694 // <span / b / i ... formating?
27696 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27697 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27699 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27701 var add_lb = name == 'BR' ? false : in_inline;
27703 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27707 var indentstr = this.indentstr;
27709 // e_inline = elements that can be inline, but still allow \n before and after?
27710 // only 'BR' ??? any others?
27712 // ADD LINE BEFORE tage
27713 if (!this.in_pre) {
27716 if (name == 'BR') {
27718 } else if (this.lastElementEndsWS()) {
27721 // otherwise - no new line. (and dont indent.)
27732 this.html.push(indentstr + '<', name.toLowerCase());
27735 for (i = 0, l = attrs.length; i < l; i++) {
27737 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27743 this.html[this.html.length] = '/>';
27745 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27747 var e_inline = name == 'BR' ? false : this.in_inline;
27749 if (!e_inline && !this.in_pre) {
27756 this.html[this.html.length] = '>';
27758 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27760 if (!in_inline && !in_pre) {
27761 var cn = node.firstChild;
27763 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27767 cn = cn.nextSibling;
27775 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27777 in_inline : in_inline
27779 // add a line after if we are not in a
27781 if (!in_inline && !in_pre) {
27790 lastElementEndsWS : function()
27792 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27793 if (value === false) {
27796 return value.match(/\s+$/);
27801 * Writes the a end element such as </p>.
27804 * @param {String} name Name of the element.
27806 end: function(name) {
27809 var indentstr = '';
27810 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27812 if (!this.in_pre && !in_inline) {
27814 indentstr = this.indentstr;
27816 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27817 this.last_inline = in_inline;
27819 // pop the indent state..
27822 * Writes a text node.
27824 * In pre - we should not mess with the contents.
27828 * @param {String} text String to write out.
27829 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27831 text: function(in_text, node)
27833 // if not in whitespace critical
27834 if (in_text.length < 1) {
27837 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27840 this.html[this.html.length] = text;
27844 if (this.in_inline) {
27845 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27847 text = text.replace(/\s+/,' '); // all white space to single white space
27850 // if next tag is '<BR>', then we can trim right..
27851 if (node.nextSibling &&
27852 node.nextSibling.nodeType == 1 &&
27853 node.nextSibling.nodeName == 'BR' )
27855 text = text.replace(/\s+$/g,'');
27857 // if previous tag was a BR, we can also trim..
27858 if (node.previousSibling &&
27859 node.previousSibling.nodeType == 1 &&
27860 node.previousSibling.nodeName == 'BR' )
27862 text = this.indentstr + text.replace(/^\s+/g,'');
27864 if (text.match(/\n/)) {
27865 text = text.replace(
27866 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27868 // remoeve the last whitespace / line break.
27869 text = text.replace(/\n\s+$/,'');
27871 // repace long lines
27875 this.html[this.html.length] = text;
27878 // see if previous element was a inline element.
27879 var indentstr = this.indentstr;
27881 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27883 // should trim left?
27884 if (node.previousSibling &&
27885 node.previousSibling.nodeType == 1 &&
27886 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27892 text = text.replace(/^\s+/,''); // trim left
27895 // should trim right?
27896 if (node.nextSibling &&
27897 node.nextSibling.nodeType == 1 &&
27898 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27903 text = text.replace(/\s+$/,''); // trim right
27910 if (text.length < 1) {
27913 if (!text.match(/\n/)) {
27914 this.html.push(indentstr + text);
27918 text = this.indentstr + text.replace(
27919 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27921 // remoeve the last whitespace / line break.
27922 text = text.replace(/\s+$/,'');
27924 this.html.push(text);
27926 // split and indent..
27931 * Writes a cdata node such as <![CDATA[data]]>.
27934 * @param {String} text String to write out inside the cdata.
27936 cdata: function(text) {
27937 this.html.push('<![CDATA[', text, ']]>');
27940 * Writes a comment node such as <!-- Comment -->.
27943 * @param {String} text String to write out inside the comment.
27945 comment: function(text) {
27946 this.html.push('<!--', text, '-->');
27949 * Writes a PI node such as <?xml attr="value" ?>.
27952 * @param {String} name Name of the pi.
27953 * @param {String} text String to write out inside the pi.
27955 pi: function(name, text) {
27956 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27957 this.indent != '' && this.html.push('\n');
27960 * Writes a doctype node such as <!DOCTYPE data>.
27963 * @param {String} text String to write out inside the doctype.
27965 doctype: function(text) {
27966 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27969 * Resets the internal buffer if one wants to reuse the writer.
27973 reset: function() {
27974 this.html.length = 0;
27983 * Returns the contents that got serialized.
27985 * @method getContent
27986 * @return {String} HTML contents that got written down.
27988 getContent: function() {
27989 return this.html.join('').replace(/\n$/, '');
27992 pushState : function(cfg)
27994 this.state.push(cfg);
27995 Roo.apply(this, cfg);
27998 popState : function()
28000 if (this.state.length < 1) {
28001 return; // nothing to push
28008 if (this.state.length > 0) {
28009 cfg = this.state[this.state.length-1];
28011 Roo.apply(this, cfg);
28014 addLine: function()
28016 if (this.html.length < 1) {
28021 var value = this.html[this.html.length - 1];
28022 if (value.length > 0 && '\n' !== value) {
28023 this.html.push('\n');
28028 //'pre script noscript style textarea video audio iframe object code'
28029 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28033 Roo.htmleditor.TidyWriter.inline_elements = [
28034 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28035 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28037 Roo.htmleditor.TidyWriter.shortend_elements = [
28038 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28039 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28042 Roo.htmleditor.TidyWriter.whitespace_elements = [
28043 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28045 * This is based loosely on tinymce
28046 * @class Roo.htmleditor.TidyEntities
28048 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28050 * Not 100% sure this is actually used or needed.
28053 Roo.htmleditor.TidyEntities = {
28056 * initialize data..
28058 init : function (){
28060 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28065 buildEntitiesLookup: function(items, radix) {
28066 var i, chr, entity, lookup = {};
28070 items = typeof(items) == 'string' ? items.split(',') : items;
28071 radix = radix || 10;
28072 // Build entities lookup table
28073 for (i = 0; i < items.length; i += 2) {
28074 chr = String.fromCharCode(parseInt(items[i], radix));
28075 // Only add non base entities
28076 if (!this.baseEntities[chr]) {
28077 entity = '&' + items[i + 1] + ';';
28078 lookup[chr] = entity;
28079 lookup[entity] = chr;
28118 // Needs to be escaped since the YUI compressor would otherwise break the code
28125 // Reverse lookup table for raw entities
28126 reverseEntities : {
28134 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28135 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28136 rawCharsRegExp : /[<>&\"\']/g,
28137 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28138 namedEntities : false,
28139 namedEntitiesData : [
28640 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28642 * @method encodeRaw
28643 * @param {String} text Text to encode.
28644 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28645 * @return {String} Entity encoded text.
28647 encodeRaw: function(text, attr)
28650 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28651 return t.baseEntities[chr] || chr;
28655 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28656 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28657 * and is exposed as the DOMUtils.encode function.
28659 * @method encodeAllRaw
28660 * @param {String} text Text to encode.
28661 * @return {String} Entity encoded text.
28663 encodeAllRaw: function(text) {
28665 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28666 return t.baseEntities[chr] || chr;
28670 * Encodes the specified string using numeric entities. The core entities will be
28671 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28673 * @method encodeNumeric
28674 * @param {String} text Text to encode.
28675 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28676 * @return {String} Entity encoded text.
28678 encodeNumeric: function(text, attr) {
28680 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28681 // Multi byte sequence convert it to a single entity
28682 if (chr.length > 1) {
28683 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28685 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28689 * Encodes the specified string using named entities. The core entities will be encoded
28690 * as named ones but all non lower ascii characters will be encoded into named entities.
28692 * @method encodeNamed
28693 * @param {String} text Text to encode.
28694 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28695 * @param {Object} entities Optional parameter with entities to use.
28696 * @return {String} Entity encoded text.
28698 encodeNamed: function(text, attr, entities) {
28700 entities = entities || this.namedEntities;
28701 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28702 return t.baseEntities[chr] || entities[chr] || chr;
28706 * Returns an encode function based on the name(s) and it's optional entities.
28708 * @method getEncodeFunc
28709 * @param {String} name Comma separated list of encoders for example named,numeric.
28710 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28711 * @return {function} Encode function to be used.
28713 getEncodeFunc: function(name, entities) {
28714 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28716 function encodeNamedAndNumeric(text, attr) {
28717 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28718 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28722 function encodeCustomNamed(text, attr) {
28723 return t.encodeNamed(text, attr, entities);
28725 // Replace + with , to be compatible with previous TinyMCE versions
28726 name = this.makeMap(name.replace(/\+/g, ','));
28727 // Named and numeric encoder
28728 if (name.named && name.numeric) {
28729 return this.encodeNamedAndNumeric;
28735 return encodeCustomNamed;
28737 return this.encodeNamed;
28740 if (name.numeric) {
28741 return this.encodeNumeric;
28744 return this.encodeRaw;
28747 * Decodes the specified string, this will replace entities with raw UTF characters.
28750 * @param {String} text Text to entity decode.
28751 * @return {String} Entity decoded string.
28753 decode: function(text)
28756 return text.replace(this.entityRegExp, function(all, numeric) {
28758 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28759 // Support upper UTF
28760 if (numeric > 65535) {
28762 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28764 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28766 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28769 nativeDecode : function (text) {
28772 makeMap : function (items, delim, map) {
28774 items = items || [];
28775 delim = delim || ',';
28776 if (typeof items == "string") {
28777 items = items.split(delim);
28782 map[items[i]] = {};
28790 Roo.htmleditor.TidyEntities.init();
28792 * @class Roo.htmleditor.KeyEnter
28793 * Handle Enter press..
28794 * @cfg {Roo.HtmlEditorCore} core the editor.
28796 * Create a new Filter.
28797 * @param {Object} config Configuration options
28804 Roo.htmleditor.KeyEnter = function(cfg) {
28805 Roo.apply(this, cfg);
28806 // this does not actually call walk as it's really just a abstract class
28808 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28811 //Roo.htmleditor.KeyEnter.i = 0;
28814 Roo.htmleditor.KeyEnter.prototype = {
28818 keypress : function(e)
28820 if (e.charCode != 13 && e.charCode != 10) {
28821 Roo.log([e.charCode,e]);
28824 e.preventDefault();
28825 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28826 var doc = this.core.doc;
28830 var sel = this.core.getSelection();
28831 var range = sel.getRangeAt(0);
28832 var n = range.commonAncestorContainer;
28833 var pc = range.closest([ 'ol', 'ul']);
28834 var pli = range.closest('li');
28835 if (!pc || e.ctrlKey) {
28836 // on it list, or ctrl pressed.
28838 sel.insertNode('br', 'after');
28840 // only do this if we have ctrl key..
28841 var br = doc.createElement('br');
28842 br.className = 'clear';
28843 br.setAttribute('style', 'clear: both');
28844 sel.insertNode(br, 'after');
28848 this.core.undoManager.addEvent();
28849 this.core.fireEditorEvent(e);
28853 // deal with <li> insetion
28854 if (pli.innerText.trim() == '' &&
28855 pli.previousSibling &&
28856 pli.previousSibling.nodeName == 'LI' &&
28857 pli.previousSibling.innerText.trim() == '') {
28858 pli.parentNode.removeChild(pli.previousSibling);
28859 sel.cursorAfter(pc);
28860 this.core.undoManager.addEvent();
28861 this.core.fireEditorEvent(e);
28865 var li = doc.createElement('LI');
28866 li.innerHTML = ' ';
28867 if (!pli || !pli.firstSibling) {
28868 pc.appendChild(li);
28870 pli.parentNode.insertBefore(li, pli.firstSibling);
28872 sel.cursorText (li.firstChild);
28874 this.core.undoManager.addEvent();
28875 this.core.fireEditorEvent(e);
28887 * @class Roo.htmleditor.Block
28888 * Base class for html editor blocks - do not use it directly .. extend it..
28889 * @cfg {DomElement} node The node to apply stuff to.
28890 * @cfg {String} friendly_name the name that appears in the context bar about this block
28891 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28894 * Create a new Filter.
28895 * @param {Object} config Configuration options
28898 Roo.htmleditor.Block = function(cfg)
28900 // do nothing .. should not be called really.
28903 * factory method to get the block from an element (using cache if necessary)
28905 * @param {HtmlElement} the dom element
28907 Roo.htmleditor.Block.factory = function(node)
28909 var cc = Roo.htmleditor.Block.cache;
28910 var id = Roo.get(node).id;
28911 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28912 Roo.htmleditor.Block.cache[id].readElement(node);
28913 return Roo.htmleditor.Block.cache[id];
28915 var db = node.getAttribute('data-block');
28917 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28919 var cls = Roo.htmleditor['Block' + db];
28920 if (typeof(cls) == 'undefined') {
28921 //Roo.log(node.getAttribute('data-block'));
28922 Roo.log("OOps missing block : " + 'Block' + db);
28925 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28926 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
28930 * initalize all Elements from content that are 'blockable'
28932 * @param the body element
28934 Roo.htmleditor.Block.initAll = function(body, type)
28936 if (typeof(type) == 'undefined') {
28937 var ia = Roo.htmleditor.Block.initAll;
28943 Roo.each(Roo.get(body).query(type), function(e) {
28944 Roo.htmleditor.Block.factory(e);
28947 // question goes here... do we need to clear out this cache sometimes?
28948 // or show we make it relivant to the htmleditor.
28949 Roo.htmleditor.Block.cache = {};
28951 Roo.htmleditor.Block.prototype = {
28955 // used by context menu
28956 friendly_name : 'Based Block',
28958 // text for button to delete this element
28959 deleteTitle : false,
28963 * Update a node with values from this object
28964 * @param {DomElement} node
28966 updateElement : function(node)
28968 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28971 * convert to plain HTML for calling insertAtCursor..
28973 toHTML : function()
28975 return Roo.DomHelper.markup(this.toObject());
28978 * used by readEleemnt to extract data from a node
28979 * may need improving as it's pretty basic
28981 * @param {DomElement} node
28982 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28983 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28984 * @param {String} style the style property - eg. text-align
28986 getVal : function(node, tag, attr, style)
28989 if (tag !== true && n.tagName != tag.toUpperCase()) {
28990 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28991 // but kiss for now.
28992 n = node.getElementsByTagName(tag).item(0);
28997 if (attr === false) {
29000 if (attr == 'html') {
29001 return n.innerHTML;
29003 if (attr == 'style') {
29004 return n.style[style];
29007 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29011 * create a DomHelper friendly object - for use with
29012 * Roo.DomHelper.markup / overwrite / etc..
29015 toObject : function()
29020 * Read a node that has a 'data-block' property - and extract the values from it.
29021 * @param {DomElement} node - the node
29023 readElement : function(node)
29034 * @class Roo.htmleditor.BlockFigure
29035 * Block that has an image and a figcaption
29036 * @cfg {String} image_src the url for the image
29037 * @cfg {String} align (left|right) alignment for the block default left
29038 * @cfg {String} caption the text to appear below (and in the alt tag)
29039 * @cfg {String} caption_display (block|none) display or not the caption
29040 * @cfg {String|number} image_width the width of the image number or %?
29041 * @cfg {String|number} image_height the height of the image number or %?
29044 * Create a new Filter.
29045 * @param {Object} config Configuration options
29048 Roo.htmleditor.BlockFigure = function(cfg)
29051 this.readElement(cfg.node);
29052 this.updateElement(cfg.node);
29054 Roo.apply(this, cfg);
29056 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29063 caption_display : 'block',
29069 // margin: '2%', not used
29071 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29074 // used by context menu
29075 friendly_name : 'Image with caption',
29076 deleteTitle : "Delete Image and Caption",
29078 contextMenu : function(toolbar)
29081 var block = function() {
29082 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29086 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29088 var syncValue = toolbar.editorcore.syncValue;
29094 xtype : 'TextItem',
29096 xns : rooui.Toolbar //Boostrap?
29100 text: 'Change Image URL',
29103 click: function (btn, state)
29107 Roo.MessageBox.show({
29108 title : "Image Source URL",
29109 msg : "Enter the url for the image",
29110 buttons: Roo.MessageBox.OKCANCEL,
29111 fn: function(btn, val){
29118 toolbar.editorcore.onEditorEvent();
29122 //multiline: multiline,
29124 value : b.image_src
29128 xns : rooui.Toolbar
29133 text: 'Change Link URL',
29136 click: function (btn, state)
29140 Roo.MessageBox.show({
29141 title : "Link URL",
29142 msg : "Enter the url for the link - leave blank to have no link",
29143 buttons: Roo.MessageBox.OKCANCEL,
29144 fn: function(btn, val){
29151 toolbar.editorcore.onEditorEvent();
29155 //multiline: multiline,
29161 xns : rooui.Toolbar
29165 text: 'Show Video URL',
29168 click: function (btn, state)
29170 Roo.MessageBox.alert("Video URL",
29171 block().video_url == '' ? 'This image is not linked ot a video' :
29172 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29175 xns : rooui.Toolbar
29180 xtype : 'TextItem',
29182 xns : rooui.Toolbar //Boostrap?
29185 xtype : 'ComboBox',
29186 allowBlank : false,
29187 displayField : 'val',
29190 triggerAction : 'all',
29192 valueField : 'val',
29196 select : function (combo, r, index)
29198 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29200 b.width = r.get('val');
29203 toolbar.editorcore.onEditorEvent();
29208 xtype : 'SimpleStore',
29221 xtype : 'TextItem',
29223 xns : rooui.Toolbar //Boostrap?
29226 xtype : 'ComboBox',
29227 allowBlank : false,
29228 displayField : 'val',
29231 triggerAction : 'all',
29233 valueField : 'val',
29237 select : function (combo, r, index)
29239 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29241 b.align = r.get('val');
29244 toolbar.editorcore.onEditorEvent();
29249 xtype : 'SimpleStore',
29263 text: 'Hide Caption',
29264 name : 'caption_display',
29266 enableToggle : true,
29267 setValue : function(v) {
29268 // this trigger toggle.
29270 this.setText(v ? "Hide Caption" : "Show Caption");
29271 this.setPressed(v != 'block');
29274 toggle: function (btn, state)
29277 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29278 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29281 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29282 toolbar.editorcore.onEditorEvent();
29285 xns : rooui.Toolbar
29291 * create a DomHelper friendly object - for use with
29292 * Roo.DomHelper.markup / overwrite / etc..
29294 toObject : function()
29296 var d = document.createElement('div');
29297 d.innerHTML = this.caption;
29299 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29301 var iw = this.align == 'center' ? this.width : '100%';
29304 contenteditable : 'false',
29305 src : this.image_src,
29306 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29309 maxWidth : iw + ' !important', // this is not getting rendered?
29315 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29317 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29322 if (this.href.length > 0) {
29326 contenteditable : 'true',
29334 if (this.video_url.length > 0) {
29339 allowfullscreen : true,
29340 width : 420, // these are for video tricks - that we replace the outer
29342 src : this.video_url,
29348 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29349 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29354 'data-block' : 'Figure',
29355 'data-width' : this.width,
29356 contenteditable : 'false',
29360 float : this.align ,
29361 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29362 width : this.align == 'center' ? '100%' : this.width,
29364 padding: this.align == 'center' ? '0' : '0 10px' ,
29365 textAlign : this.align // seems to work for email..
29370 align : this.align,
29376 'data-display' : this.caption_display,
29378 textAlign : 'left',
29380 lineHeight : '24px',
29381 display : this.caption_display,
29382 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29384 width: this.align == 'center' ? this.width : '100%'
29388 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29393 marginTop : '16px',
29399 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29401 contenteditable : true,
29417 readElement : function(node)
29419 // this should not really come from the link...
29420 this.video_url = this.getVal(node, 'div', 'src');
29421 this.cls = this.getVal(node, 'div', 'class');
29422 this.href = this.getVal(node, 'a', 'href');
29425 this.image_src = this.getVal(node, 'img', 'src');
29427 this.align = this.getVal(node, 'figure', 'align');
29428 var figcaption = this.getVal(node, 'figcaption', false);
29429 if (figcaption !== '') {
29430 this.caption = this.getVal(figcaption, 'i', 'html');
29434 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29435 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29436 this.width = this.getVal(node, true, 'data-width');
29437 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29440 removeNode : function()
29457 * @class Roo.htmleditor.BlockTable
29458 * Block that manages a table
29461 * Create a new Filter.
29462 * @param {Object} config Configuration options
29465 Roo.htmleditor.BlockTable = function(cfg)
29468 this.readElement(cfg.node);
29469 this.updateElement(cfg.node);
29471 Roo.apply(this, cfg);
29474 for(var r = 0; r < this.no_row; r++) {
29476 for(var c = 0; c < this.no_col; c++) {
29477 this.rows[r][c] = this.emptyCell();
29484 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29493 // used by context menu
29494 friendly_name : 'Table',
29495 deleteTitle : 'Delete Table',
29496 // context menu is drawn once..
29498 contextMenu : function(toolbar)
29501 var block = function() {
29502 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29506 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29508 var syncValue = toolbar.editorcore.syncValue;
29514 xtype : 'TextItem',
29516 xns : rooui.Toolbar //Boostrap?
29519 xtype : 'ComboBox',
29520 allowBlank : false,
29521 displayField : 'val',
29524 triggerAction : 'all',
29526 valueField : 'val',
29530 select : function (combo, r, index)
29532 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29534 b.width = r.get('val');
29537 toolbar.editorcore.onEditorEvent();
29542 xtype : 'SimpleStore',
29554 xtype : 'TextItem',
29555 text : "Columns: ",
29556 xns : rooui.Toolbar //Boostrap?
29563 click : function (_self, e)
29565 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29566 block().removeColumn();
29568 toolbar.editorcore.onEditorEvent();
29571 xns : rooui.Toolbar
29577 click : function (_self, e)
29579 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29580 block().addColumn();
29582 toolbar.editorcore.onEditorEvent();
29585 xns : rooui.Toolbar
29589 xtype : 'TextItem',
29591 xns : rooui.Toolbar //Boostrap?
29598 click : function (_self, e)
29600 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29601 block().removeRow();
29603 toolbar.editorcore.onEditorEvent();
29606 xns : rooui.Toolbar
29612 click : function (_self, e)
29616 toolbar.editorcore.onEditorEvent();
29619 xns : rooui.Toolbar
29624 text: 'Reset Column Widths',
29627 click : function (_self, e)
29629 block().resetWidths();
29631 toolbar.editorcore.onEditorEvent();
29634 xns : rooui.Toolbar
29645 * create a DomHelper friendly object - for use with
29646 * Roo.DomHelper.markup / overwrite / etc..
29647 * ?? should it be called with option to hide all editing features?
29649 toObject : function()
29654 contenteditable : 'false', // this stops cell selection from picking the table.
29655 'data-block' : 'Table',
29658 border : 'solid 1px #000', // ??? hard coded?
29659 'border-collapse' : 'collapse'
29662 { tag : 'tbody' , cn : [] }
29666 // do we have a head = not really
29668 Roo.each(this.rows, function( row ) {
29673 border : 'solid 1px #000',
29679 ret.cn[0].cn.push(tr);
29680 // does the row have any properties? ?? height?
29682 Roo.each(row, function( cell ) {
29686 contenteditable : 'true',
29687 'data-block' : 'Td',
29691 if (cell.colspan > 1) {
29692 td.colspan = cell.colspan ;
29693 nc += cell.colspan;
29697 if (cell.rowspan > 1) {
29698 td.rowspan = cell.rowspan ;
29707 ncols = Math.max(nc, ncols);
29711 // add the header row..
29720 readElement : function(node)
29722 node = node ? node : this.node ;
29723 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29727 var trs = Array.from(node.rows);
29728 trs.forEach(function(tr) {
29730 this.rows.push(row);
29734 Array.from(tr.cells).forEach(function(td) {
29737 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29738 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29739 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29740 html : td.innerHTML
29742 no_column += add.colspan;
29749 this.no_col = Math.max(this.no_col, no_column);
29756 normalizeRows: function()
29760 this.rows.forEach(function(row) {
29763 row = this.normalizeRow(row);
29765 row.forEach(function(c) {
29766 while (typeof(ret[rid][cid]) != 'undefined') {
29769 if (typeof(ret[rid]) == 'undefined') {
29775 if (c.rowspan < 2) {
29779 for(var i = 1 ;i < c.rowspan; i++) {
29780 if (typeof(ret[rid+i]) == 'undefined') {
29783 ret[rid+i][cid] = c;
29791 normalizeRow: function(row)
29794 row.forEach(function(c) {
29795 if (c.colspan < 2) {
29799 for(var i =0 ;i < c.colspan; i++) {
29807 deleteColumn : function(sel)
29809 if (!sel || sel.type != 'col') {
29812 if (this.no_col < 2) {
29816 this.rows.forEach(function(row) {
29817 var cols = this.normalizeRow(row);
29818 var col = cols[sel.col];
29819 if (col.colspan > 1) {
29829 removeColumn : function()
29831 this.deleteColumn({
29833 col : this.no_col-1
29835 this.updateElement();
29839 addColumn : function()
29842 this.rows.forEach(function(row) {
29843 row.push(this.emptyCell());
29846 this.updateElement();
29849 deleteRow : function(sel)
29851 if (!sel || sel.type != 'row') {
29855 if (this.no_row < 2) {
29859 var rows = this.normalizeRows();
29862 rows[sel.row].forEach(function(col) {
29863 if (col.rowspan > 1) {
29866 col.remove = 1; // flage it as removed.
29871 this.rows.forEach(function(row) {
29873 row.forEach(function(c) {
29874 if (typeof(c.remove) == 'undefined') {
29879 if (newrow.length > 0) {
29883 this.rows = newrows;
29888 this.updateElement();
29891 removeRow : function()
29895 row : this.no_row-1
29901 addRow : function()
29905 for (var i = 0; i < this.no_col; i++ ) {
29907 row.push(this.emptyCell());
29910 this.rows.push(row);
29911 this.updateElement();
29915 // the default cell object... at present...
29916 emptyCell : function() {
29917 return (new Roo.htmleditor.BlockTd({})).toObject();
29922 removeNode : function()
29929 resetWidths : function()
29931 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29932 var nn = Roo.htmleditor.Block.factory(n);
29934 nn.updateElement(n);
29947 * since selections really work on the table cell, then editing really should work from there
29949 * The original plan was to support merging etc... - but that may not be needed yet..
29951 * So this simple version will support:
29953 * adjust the width +/-
29954 * reset the width...
29963 * @class Roo.htmleditor.BlockTable
29964 * Block that manages a table
29967 * Create a new Filter.
29968 * @param {Object} config Configuration options
29971 Roo.htmleditor.BlockTd = function(cfg)
29974 this.readElement(cfg.node);
29975 this.updateElement(cfg.node);
29977 Roo.apply(this, cfg);
29982 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29987 textAlign : 'left',
29994 // used by context menu
29995 friendly_name : 'Table Cell',
29996 deleteTitle : false, // use our customer delete
29998 // context menu is drawn once..
30000 contextMenu : function(toolbar)
30003 var cell = function() {
30004 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30007 var table = function() {
30008 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30012 var saveSel = function()
30014 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30016 var restoreSel = function()
30020 toolbar.editorcore.focus();
30021 var cr = toolbar.editorcore.getSelection();
30022 cr.removeAllRanges();
30024 toolbar.editorcore.onEditorEvent();
30025 }).defer(10, this);
30031 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30033 var syncValue = toolbar.editorcore.syncValue;
30040 text : 'Edit Table',
30042 click : function() {
30043 var t = toolbar.tb.selectedNode.closest('table');
30044 toolbar.editorcore.selectNode(t);
30045 toolbar.editorcore.onEditorEvent();
30054 xtype : 'TextItem',
30055 text : "Column Width: ",
30056 xns : rooui.Toolbar
30063 click : function (_self, e)
30065 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30066 cell().shrinkColumn();
30068 toolbar.editorcore.onEditorEvent();
30071 xns : rooui.Toolbar
30077 click : function (_self, e)
30079 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30080 cell().growColumn();
30082 toolbar.editorcore.onEditorEvent();
30085 xns : rooui.Toolbar
30089 xtype : 'TextItem',
30090 text : "Vertical Align: ",
30091 xns : rooui.Toolbar //Boostrap?
30094 xtype : 'ComboBox',
30095 allowBlank : false,
30096 displayField : 'val',
30099 triggerAction : 'all',
30101 valueField : 'val',
30105 select : function (combo, r, index)
30107 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30109 b.valign = r.get('val');
30112 toolbar.editorcore.onEditorEvent();
30117 xtype : 'SimpleStore',
30121 ['bottom'] // there are afew more...
30129 xtype : 'TextItem',
30130 text : "Merge Cells: ",
30131 xns : rooui.Toolbar
30140 click : function (_self, e)
30142 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30143 cell().mergeRight();
30144 //block().growColumn();
30146 toolbar.editorcore.onEditorEvent();
30149 xns : rooui.Toolbar
30156 click : function (_self, e)
30158 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30159 cell().mergeBelow();
30160 //block().growColumn();
30162 toolbar.editorcore.onEditorEvent();
30165 xns : rooui.Toolbar
30168 xtype : 'TextItem',
30170 xns : rooui.Toolbar
30178 click : function (_self, e)
30180 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30183 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30184 toolbar.editorcore.onEditorEvent();
30188 xns : rooui.Toolbar
30192 xns : rooui.Toolbar
30201 xns : rooui.Toolbar,
30210 click : function (_self, e)
30214 cell().deleteColumn();
30216 toolbar.editorcore.selectNode(t.node);
30217 toolbar.editorcore.onEditorEvent();
30226 click : function (_self, e)
30229 cell().deleteRow();
30232 toolbar.editorcore.selectNode(t.node);
30233 toolbar.editorcore.onEditorEvent();
30240 xtype : 'Separator',
30247 click : function (_self, e)
30250 var nn = t.node.nextSibling || t.node.previousSibling;
30251 t.node.parentNode.removeChild(t.node);
30253 toolbar.editorcore.selectNode(nn, true);
30255 toolbar.editorcore.onEditorEvent();
30265 // align... << fixme
30273 * create a DomHelper friendly object - for use with
30274 * Roo.DomHelper.markup / overwrite / etc..
30275 * ?? should it be called with option to hide all editing features?
30278 * create a DomHelper friendly object - for use with
30279 * Roo.DomHelper.markup / overwrite / etc..
30280 * ?? should it be called with option to hide all editing features?
30282 toObject : function()
30286 contenteditable : 'true', // this stops cell selection from picking the table.
30287 'data-block' : 'Td',
30288 valign : this.valign,
30290 'text-align' : this.textAlign,
30291 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30292 'border-collapse' : 'collapse',
30293 padding : '6px', // 8 for desktop / 4 for mobile
30294 'vertical-align': this.valign
30298 if (this.width != '') {
30299 ret.width = this.width;
30300 ret.style.width = this.width;
30304 if (this.colspan > 1) {
30305 ret.colspan = this.colspan ;
30307 if (this.rowspan > 1) {
30308 ret.rowspan = this.rowspan ;
30317 readElement : function(node)
30319 node = node ? node : this.node ;
30320 this.width = node.style.width;
30321 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30322 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30323 this.html = node.innerHTML;
30324 if (node.style.textAlign != '') {
30325 this.textAlign = node.style.textAlign;
30331 // the default cell object... at present...
30332 emptyCell : function() {
30336 textAlign : 'left',
30337 html : " " // is this going to be editable now?
30342 removeNode : function()
30344 return this.node.closest('table');
30352 toTableArray : function()
30355 var tab = this.node.closest('tr').closest('table');
30356 Array.from(tab.rows).forEach(function(r, ri){
30360 this.colWidths = [];
30361 var all_auto = true;
30362 Array.from(tab.rows).forEach(function(r, ri){
30365 Array.from(r.cells).forEach(function(ce, ci){
30370 colspan : ce.colSpan,
30371 rowspan : ce.rowSpan
30373 if (ce.isEqualNode(this.node)) {
30376 // if we have been filled up by a row?
30377 if (typeof(ret[rn][cn]) != 'undefined') {
30378 while(typeof(ret[rn][cn]) != 'undefined') {
30384 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30385 this.colWidths[cn] = ce.style.width;
30386 if (this.colWidths[cn] != '') {
30392 if (c.colspan < 2 && c.rowspan < 2 ) {
30397 for(var j = 0; j < c.rowspan; j++) {
30398 if (typeof(ret[rn+j]) == 'undefined') {
30399 continue; // we have a problem..
30402 for(var i = 0; i < c.colspan; i++) {
30403 ret[rn+j][cn+i] = c;
30412 // initalize widths.?
30413 // either all widths or no widths..
30415 this.colWidths[0] = false; // no widths flag.
30426 mergeRight: function()
30429 // get the contents of the next cell along..
30430 var tr = this.node.closest('tr');
30431 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30432 if (i >= tr.childNodes.length - 1) {
30433 return; // no cells on right to merge with.
30435 var table = this.toTableArray();
30437 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30438 return; // nothing right?
30440 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30441 // right cell - must be same rowspan and on the same row.
30442 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30443 return; // right hand side is not same rowspan.
30448 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30449 tr.removeChild(rc.cell);
30450 this.colspan += rc.colspan;
30451 this.node.setAttribute('colspan', this.colspan);
30453 var table = this.toTableArray();
30454 this.normalizeWidths(table);
30455 this.updateWidths(table);
30459 mergeBelow : function()
30461 var table = this.toTableArray();
30462 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30463 return; // no row below
30465 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30466 return; // nothing right?
30468 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30470 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30471 return; // right hand side is not same rowspan.
30473 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30474 rc.cell.parentNode.removeChild(rc.cell);
30475 this.rowspan += rc.rowspan;
30476 this.node.setAttribute('rowspan', this.rowspan);
30481 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30484 var table = this.toTableArray();
30485 var cd = this.cellData;
30489 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30492 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30493 if (r == cd.row && c == cd.col) {
30494 this.node.removeAttribute('rowspan');
30495 this.node.removeAttribute('colspan');
30498 var ntd = this.node.cloneNode(); // which col/row should be 0..
30499 ntd.removeAttribute('id');
30500 ntd.style.width = this.colWidths[c];
30501 ntd.innerHTML = '';
30502 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30506 this.redrawAllCells(table);
30512 redrawAllCells: function(table)
30516 var tab = this.node.closest('tr').closest('table');
30517 var ctr = tab.rows[0].parentNode;
30518 Array.from(tab.rows).forEach(function(r, ri){
30520 Array.from(r.cells).forEach(function(ce, ci){
30521 ce.parentNode.removeChild(ce);
30523 r.parentNode.removeChild(r);
30525 for(var r = 0 ; r < table.length; r++) {
30526 var re = tab.rows[r];
30528 var re = tab.ownerDocument.createElement('tr');
30529 ctr.appendChild(re);
30530 for(var c = 0 ; c < table[r].length; c++) {
30531 if (table[r][c].cell === false) {
30535 re.appendChild(table[r][c].cell);
30537 table[r][c].cell = false;
30542 updateWidths : function(table)
30544 for(var r = 0 ; r < table.length; r++) {
30546 for(var c = 0 ; c < table[r].length; c++) {
30547 if (table[r][c].cell === false) {
30551 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30552 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30553 el.width = Math.floor(this.colWidths[c]) +'%';
30554 el.updateElement(el.node);
30556 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30557 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30559 for(var i = 0; i < table[r][c].colspan; i ++) {
30560 width += Math.floor(this.colWidths[c + i]);
30562 el.width = width +'%';
30563 el.updateElement(el.node);
30565 table[r][c].cell = false; // done
30569 normalizeWidths : function(table)
30571 if (this.colWidths[0] === false) {
30572 var nw = 100.0 / this.colWidths.length;
30573 this.colWidths.forEach(function(w,i) {
30574 this.colWidths[i] = nw;
30579 var t = 0, missing = [];
30581 this.colWidths.forEach(function(w,i) {
30583 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30584 var add = this.colWidths[i];
30593 var nc = this.colWidths.length;
30594 if (missing.length) {
30595 var mult = (nc - missing.length) / (1.0 * nc);
30597 var ew = (100 -t) / (1.0 * missing.length);
30598 this.colWidths.forEach(function(w,i) {
30600 this.colWidths[i] = w * mult;
30604 this.colWidths[i] = ew;
30606 // have to make up numbers..
30609 // now we should have all the widths..
30614 shrinkColumn : function()
30616 var table = this.toTableArray();
30617 this.normalizeWidths(table);
30618 var col = this.cellData.col;
30619 var nw = this.colWidths[col] * 0.8;
30623 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30624 this.colWidths.forEach(function(w,i) {
30626 this.colWidths[i] = nw;
30629 this.colWidths[i] += otherAdd
30631 this.updateWidths(table);
30634 growColumn : function()
30636 var table = this.toTableArray();
30637 this.normalizeWidths(table);
30638 var col = this.cellData.col;
30639 var nw = this.colWidths[col] * 1.2;
30643 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30644 this.colWidths.forEach(function(w,i) {
30646 this.colWidths[i] = nw;
30649 this.colWidths[i] -= otherSub
30651 this.updateWidths(table);
30654 deleteRow : function()
30656 // delete this rows 'tr'
30657 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30658 // then reduce the rowspan.
30659 var table = this.toTableArray();
30660 // this.cellData.row;
30661 for (var i =0;i< table[this.cellData.row].length ; i++) {
30662 var c = table[this.cellData.row][i];
30663 if (c.row != this.cellData.row) {
30666 c.cell.setAttribute('rowspan', c.rowspan);
30669 if (c.rowspan > 1) {
30671 c.cell.setAttribute('rowspan', c.rowspan);
30674 table.splice(this.cellData.row,1);
30675 this.redrawAllCells(table);
30678 deleteColumn : function()
30680 var table = this.toTableArray();
30682 for (var i =0;i< table.length ; i++) {
30683 var c = table[i][this.cellData.col];
30684 if (c.col != this.cellData.col) {
30685 table[i][this.cellData.col].colspan--;
30686 } else if (c.colspan > 1) {
30688 c.cell.setAttribute('colspan', c.colspan);
30690 table[i].splice(this.cellData.col,1);
30693 this.redrawAllCells(table);
30701 //<script type="text/javascript">
30704 * Based Ext JS Library 1.1.1
30705 * Copyright(c) 2006-2007, Ext JS, LLC.
30711 * @class Roo.HtmlEditorCore
30712 * @extends Roo.Component
30713 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30715 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30718 Roo.HtmlEditorCore = function(config){
30721 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30726 * @event initialize
30727 * Fires when the editor is fully initialized (including the iframe)
30728 * @param {Roo.HtmlEditorCore} this
30733 * Fires when the editor is first receives the focus. Any insertion must wait
30734 * until after this event.
30735 * @param {Roo.HtmlEditorCore} this
30739 * @event beforesync
30740 * Fires before the textarea is updated with content from the editor iframe. Return false
30741 * to cancel the sync.
30742 * @param {Roo.HtmlEditorCore} this
30743 * @param {String} html
30747 * @event beforepush
30748 * Fires before the iframe editor is updated with content from the textarea. Return false
30749 * to cancel the push.
30750 * @param {Roo.HtmlEditorCore} this
30751 * @param {String} html
30756 * Fires when the textarea is updated with content from the editor iframe.
30757 * @param {Roo.HtmlEditorCore} this
30758 * @param {String} html
30763 * Fires when the iframe editor is updated with content from the textarea.
30764 * @param {Roo.HtmlEditorCore} this
30765 * @param {String} html
30770 * @event editorevent
30771 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30772 * @param {Roo.HtmlEditorCore} this
30779 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30781 // defaults : white / black...
30782 this.applyBlacklists();
30789 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30793 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30799 * @cfg {String} css styling for resizing. (used on bootstrap only)
30803 * @cfg {Number} height (in pixels)
30807 * @cfg {Number} width (in pixels)
30811 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30812 * if you are doing an email editor, this probably needs disabling, it's designed
30817 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30819 enableBlocks : true,
30821 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30824 stylesheets: false,
30826 * @cfg {String} language default en - language of text (usefull for rtl languages)
30832 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30833 * - by default they are stripped - if you are editing email you may need this.
30835 allowComments: false,
30839 // private properties
30840 validationEvent : false,
30842 initialized : false,
30844 sourceEditMode : false,
30845 onFocus : Roo.emptyFn,
30847 hideMode:'offsets',
30851 // blacklist + whitelisted elements..
30858 undoManager : false,
30860 * Protected method that will not generally be called directly. It
30861 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30862 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30864 getDocMarkup : function(){
30868 // inherit styels from page...??
30869 if (this.stylesheets === false) {
30871 Roo.get(document.head).select('style').each(function(node) {
30872 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30875 Roo.get(document.head).select('link').each(function(node) {
30876 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30879 } else if (!this.stylesheets.length) {
30881 st = '<style type="text/css">' +
30882 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30885 for (var i in this.stylesheets) {
30886 if (typeof(this.stylesheets[i]) != 'string') {
30889 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30894 st += '<style type="text/css">' +
30895 'IMG { cursor: pointer } ' +
30898 st += '<meta name="google" content="notranslate">';
30900 var cls = 'notranslate roo-htmleditor-body';
30902 if(this.bodyCls.length){
30903 cls += ' ' + this.bodyCls;
30906 return '<html class="notranslate" translate="no"><head>' + st +
30907 //<style type="text/css">' +
30908 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30910 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30914 onRender : function(ct, position)
30917 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30918 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30921 this.el.dom.style.border = '0 none';
30922 this.el.dom.setAttribute('tabIndex', -1);
30923 this.el.addClass('x-hidden hide');
30927 if(Roo.isIE){ // fix IE 1px bogus margin
30928 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30932 this.frameId = Roo.id();
30936 cls: 'form-control', // bootstrap..
30938 name: this.frameId,
30939 frameBorder : 'no',
30940 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
30943 ifcfg.style = { resize : this.resize };
30946 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
30949 this.iframe = iframe.dom;
30951 this.assignDocWin();
30953 this.doc.designMode = 'on';
30956 this.doc.write(this.getDocMarkup());
30960 var task = { // must defer to wait for browser to be ready
30962 //console.log("run task?" + this.doc.readyState);
30963 this.assignDocWin();
30964 if(this.doc.body || this.doc.readyState == 'complete'){
30966 this.doc.designMode="on";
30971 Roo.TaskMgr.stop(task);
30972 this.initEditor.defer(10, this);
30979 Roo.TaskMgr.start(task);
30984 onResize : function(w, h)
30986 Roo.log('resize: ' +w + ',' + h );
30987 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30991 if(typeof w == 'number'){
30993 this.iframe.style.width = w + 'px';
30995 if(typeof h == 'number'){
30997 this.iframe.style.height = h + 'px';
30999 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31006 * Toggles the editor between standard and source edit mode.
31007 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31009 toggleSourceEdit : function(sourceEditMode){
31011 this.sourceEditMode = sourceEditMode === true;
31013 if(this.sourceEditMode){
31015 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31018 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31019 //this.iframe.className = '';
31022 //this.setSize(this.owner.wrap.getSize());
31023 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31030 * Protected method that will not generally be called directly. If you need/want
31031 * custom HTML cleanup, this is the method you should override.
31032 * @param {String} html The HTML to be cleaned
31033 * return {String} The cleaned HTML
31035 cleanHtml : function(html)
31037 html = String(html);
31038 if(html.length > 5){
31039 if(Roo.isSafari){ // strip safari nonsense
31040 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31043 if(html == ' '){
31050 * HTML Editor -> Textarea
31051 * Protected method that will not generally be called directly. Syncs the contents
31052 * of the editor iframe with the textarea.
31054 syncValue : function()
31056 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31057 if(this.initialized){
31059 if (this.undoManager) {
31060 this.undoManager.addEvent();
31064 var bd = (this.doc.body || this.doc.documentElement);
31067 var sel = this.win.getSelection();
31069 var div = document.createElement('div');
31070 div.innerHTML = bd.innerHTML;
31071 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31072 if (gtx.length > 0) {
31073 var rm = gtx.item(0).parentNode;
31074 rm.parentNode.removeChild(rm);
31078 if (this.enableBlocks) {
31079 new Roo.htmleditor.FilterBlock({ node : div });
31082 var html = div.innerHTML;
31085 if (this.autoClean) {
31087 new Roo.htmleditor.FilterAttributes({
31108 attrib_clean : ['href', 'src' ]
31111 var tidy = new Roo.htmleditor.TidySerializer({
31114 html = tidy.serialize(div);
31120 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31121 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31123 html = '<div style="'+m[0]+'">' + html + '</div>';
31126 html = this.cleanHtml(html);
31127 // fix up the special chars.. normaly like back quotes in word...
31128 // however we do not want to do this with chinese..
31129 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31131 var cc = match.charCodeAt();
31133 // Get the character value, handling surrogate pairs
31134 if (match.length == 2) {
31135 // It's a surrogate pair, calculate the Unicode code point
31136 var high = match.charCodeAt(0) - 0xD800;
31137 var low = match.charCodeAt(1) - 0xDC00;
31138 cc = (high * 0x400) + low + 0x10000;
31140 (cc >= 0x4E00 && cc < 0xA000 ) ||
31141 (cc >= 0x3400 && cc < 0x4E00 ) ||
31142 (cc >= 0xf900 && cc < 0xfb00 )
31147 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31148 return "&#" + cc + ";";
31155 if(this.owner.fireEvent('beforesync', this, html) !== false){
31156 this.el.dom.value = html;
31157 this.owner.fireEvent('sync', this, html);
31163 * TEXTAREA -> EDITABLE
31164 * Protected method that will not generally be called directly. Pushes the value of the textarea
31165 * into the iframe editor.
31167 pushValue : function()
31169 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31170 if(this.initialized){
31171 var v = this.el.dom.value.trim();
31174 if(this.owner.fireEvent('beforepush', this, v) !== false){
31175 var d = (this.doc.body || this.doc.documentElement);
31178 this.el.dom.value = d.innerHTML;
31179 this.owner.fireEvent('push', this, v);
31181 if (this.autoClean) {
31182 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31183 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31185 if (this.enableBlocks) {
31186 Roo.htmleditor.Block.initAll(this.doc.body);
31189 this.updateLanguage();
31191 var lc = this.doc.body.lastChild;
31192 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31193 // add an extra line at the end.
31194 this.doc.body.appendChild(this.doc.createElement('br'));
31202 deferFocus : function(){
31203 this.focus.defer(10, this);
31207 focus : function(){
31208 if(this.win && !this.sourceEditMode){
31215 assignDocWin: function()
31217 var iframe = this.iframe;
31220 this.doc = iframe.contentWindow.document;
31221 this.win = iframe.contentWindow;
31223 // if (!Roo.get(this.frameId)) {
31226 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31227 // this.win = Roo.get(this.frameId).dom.contentWindow;
31229 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31233 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31234 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31239 initEditor : function(){
31240 //console.log("INIT EDITOR");
31241 this.assignDocWin();
31245 this.doc.designMode="on";
31247 this.doc.write(this.getDocMarkup());
31250 var dbody = (this.doc.body || this.doc.documentElement);
31251 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31252 // this copies styles from the containing element into thsi one..
31253 // not sure why we need all of this..
31254 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31256 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31257 //ss['background-attachment'] = 'fixed'; // w3c
31258 dbody.bgProperties = 'fixed'; // ie
31259 dbody.setAttribute("translate", "no");
31261 //Roo.DomHelper.applyStyles(dbody, ss);
31262 Roo.EventManager.on(this.doc, {
31264 'mouseup': this.onEditorEvent,
31265 'dblclick': this.onEditorEvent,
31266 'click': this.onEditorEvent,
31267 'keyup': this.onEditorEvent,
31272 Roo.EventManager.on(this.doc, {
31273 'paste': this.onPasteEvent,
31277 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31280 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31281 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31283 this.initialized = true;
31286 // initialize special key events - enter
31287 new Roo.htmleditor.KeyEnter({core : this});
31291 this.owner.fireEvent('initialize', this);
31294 // this is to prevent a href clicks resulting in a redirect?
31296 onPasteEvent : function(e,v)
31298 // I think we better assume paste is going to be a dirty load of rubish from word..
31300 // even pasting into a 'email version' of this widget will have to clean up that mess.
31301 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31303 // check what type of paste - if it's an image, then handle it differently.
31304 if (cd.files && cd.files.length > 0) {
31306 var urlAPI = (window.createObjectURL && window) ||
31307 (window.URL && URL.revokeObjectURL && URL) ||
31308 (window.webkitURL && webkitURL);
31310 var url = urlAPI.createObjectURL( cd.files[0]);
31311 this.insertAtCursor('<img src=" + url + ">');
31314 if (cd.types.indexOf('text/html') < 0 ) {
31318 var html = cd.getData('text/html'); // clipboard event
31319 if (cd.types.indexOf('text/rtf') > -1) {
31320 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31321 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31326 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31327 .map(function(g) { return g.toDataURL(); })
31328 .filter(function(g) { return g != 'about:blank'; });
31331 html = this.cleanWordChars(html);
31333 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31336 var sn = this.getParentElement();
31337 // check if d contains a table, and prevent nesting??
31338 //Roo.log(d.getElementsByTagName('table'));
31340 //Roo.log(sn.closest('table'));
31341 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31342 e.preventDefault();
31343 this.insertAtCursor("You can not nest tables");
31344 //Roo.log("prevent?"); // fixme -
31350 if (images.length > 0) {
31351 // replace all v:imagedata - with img.
31352 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31353 Roo.each(ar, function(node) {
31354 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31355 node.parentNode.removeChild(node);
31359 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31360 img.setAttribute('src', images[i]);
31363 if (this.autoClean) {
31364 new Roo.htmleditor.FilterWord({ node : d });
31366 new Roo.htmleditor.FilterStyleToTag({ node : d });
31367 new Roo.htmleditor.FilterAttributes({
31369 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31370 attrib_clean : ['href', 'src' ]
31372 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31373 // should be fonts..
31374 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31375 new Roo.htmleditor.FilterParagraph({ node : d });
31376 new Roo.htmleditor.FilterSpan({ node : d });
31377 new Roo.htmleditor.FilterLongBr({ node : d });
31378 new Roo.htmleditor.FilterComment({ node : d });
31382 if (this.enableBlocks) {
31384 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31385 if (img.closest('figure')) { // assume!! that it's aready
31388 var fig = new Roo.htmleditor.BlockFigure({
31389 image_src : img.src
31391 fig.updateElement(img); // replace it..
31397 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31398 if (this.enableBlocks) {
31399 Roo.htmleditor.Block.initAll(this.doc.body);
31403 e.preventDefault();
31404 this.owner.fireEvent('paste', this);
31406 // default behaveiour should be our local cleanup paste? (optional?)
31407 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31408 //this.owner.fireEvent('paste', e, v);
31411 onDestroy : function(){
31417 //for (var i =0; i < this.toolbars.length;i++) {
31418 // // fixme - ask toolbars for heights?
31419 // this.toolbars[i].onDestroy();
31422 //this.wrap.dom.innerHTML = '';
31423 //this.wrap.remove();
31428 onFirstFocus : function(){
31430 this.assignDocWin();
31431 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31433 this.activated = true;
31436 if(Roo.isGecko){ // prevent silly gecko errors
31438 var s = this.win.getSelection();
31439 if(!s.focusNode || s.focusNode.nodeType != 3){
31440 var r = s.getRangeAt(0);
31441 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31446 this.execCmd('useCSS', true);
31447 this.execCmd('styleWithCSS', false);
31450 this.owner.fireEvent('activate', this);
31454 adjustFont: function(btn){
31455 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31456 //if(Roo.isSafari){ // safari
31459 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31460 if(Roo.isSafari){ // safari
31461 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31462 v = (v < 10) ? 10 : v;
31463 v = (v > 48) ? 48 : v;
31464 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31469 v = Math.max(1, v+adjust);
31471 this.execCmd('FontSize', v );
31474 onEditorEvent : function(e)
31478 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31479 return; // we do not handle this.. (undo manager does..)
31481 // in theory this detects if the last element is not a br, then we try and do that.
31482 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31484 e.target.nodeName == 'BODY' &&
31485 e.type == "mouseup" &&
31486 this.doc.body.lastChild
31488 var lc = this.doc.body.lastChild;
31489 // gtx-trans is google translate plugin adding crap.
31490 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31491 lc = lc.previousSibling;
31493 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31494 // if last element is <BR> - then dont do anything.
31496 var ns = this.doc.createElement('br');
31497 this.doc.body.appendChild(ns);
31498 range = this.doc.createRange();
31499 range.setStartAfter(ns);
31500 range.collapse(true);
31501 var sel = this.win.getSelection();
31502 sel.removeAllRanges();
31503 sel.addRange(range);
31509 this.fireEditorEvent(e);
31510 // this.updateToolbar();
31511 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31514 fireEditorEvent: function(e)
31516 this.owner.fireEvent('editorevent', this, e);
31519 insertTag : function(tg)
31521 // could be a bit smarter... -> wrap the current selected tRoo..
31522 if (tg.toLowerCase() == 'span' ||
31523 tg.toLowerCase() == 'code' ||
31524 tg.toLowerCase() == 'sup' ||
31525 tg.toLowerCase() == 'sub'
31528 range = this.createRange(this.getSelection());
31529 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31530 wrappingNode.appendChild(range.extractContents());
31531 range.insertNode(wrappingNode);
31538 this.execCmd("formatblock", tg);
31539 this.undoManager.addEvent();
31542 insertText : function(txt)
31546 var range = this.createRange();
31547 range.deleteContents();
31548 //alert(Sender.getAttribute('label'));
31550 range.insertNode(this.doc.createTextNode(txt));
31551 this.undoManager.addEvent();
31557 * Executes a Midas editor command on the editor document and performs necessary focus and
31558 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31559 * @param {String} cmd The Midas command
31560 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31562 relayCmd : function(cmd, value)
31566 case 'justifyleft':
31567 case 'justifyright':
31568 case 'justifycenter':
31569 // if we are in a cell, then we will adjust the
31570 var n = this.getParentElement();
31571 var td = n.closest('td');
31573 var bl = Roo.htmleditor.Block.factory(td);
31574 bl.textAlign = cmd.replace('justify','');
31575 bl.updateElement();
31576 this.owner.fireEvent('editorevent', this);
31579 this.execCmd('styleWithCSS', true); //
31583 // if there is no selection, then we insert, and set the curson inside it..
31584 this.execCmd('styleWithCSS', false);
31594 this.execCmd(cmd, value);
31595 this.owner.fireEvent('editorevent', this);
31596 //this.updateToolbar();
31597 this.owner.deferFocus();
31601 * Executes a Midas editor command directly on the editor document.
31602 * For visual commands, you should use {@link #relayCmd} instead.
31603 * <b>This should only be called after the editor is initialized.</b>
31604 * @param {String} cmd The Midas command
31605 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31607 execCmd : function(cmd, value){
31608 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31615 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31617 * @param {String} text | dom node..
31619 insertAtCursor : function(text)
31622 if(!this.activated){
31626 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31630 // from jquery ui (MIT licenced)
31632 var win = this.win;
31634 if (win.getSelection && win.getSelection().getRangeAt) {
31636 // delete the existing?
31638 this.createRange(this.getSelection()).deleteContents();
31639 range = win.getSelection().getRangeAt(0);
31640 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31641 range.insertNode(node);
31642 range = range.cloneRange();
31643 range.collapse(false);
31645 win.getSelection().removeAllRanges();
31646 win.getSelection().addRange(range);
31650 } else if (win.document.selection && win.document.selection.createRange) {
31651 // no firefox support
31652 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31653 win.document.selection.createRange().pasteHTML(txt);
31656 // no firefox support
31657 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31658 this.execCmd('InsertHTML', txt);
31666 mozKeyPress : function(e){
31668 var c = e.getCharCode(), cmd;
31671 c = String.fromCharCode(c).toLowerCase();
31685 // this.cleanUpPaste.defer(100, this);
31691 this.relayCmd(cmd);
31692 //this.win.focus();
31693 //this.execCmd(cmd);
31694 //this.deferFocus();
31695 e.preventDefault();
31703 fixKeys : function(){ // load time branching for fastest keydown performance
31707 return function(e){
31708 var k = e.getKey(), r;
31711 r = this.doc.selection.createRange();
31714 r.pasteHTML('    ');
31719 /// this is handled by Roo.htmleditor.KeyEnter
31722 r = this.doc.selection.createRange();
31724 var target = r.parentElement();
31725 if(!target || target.tagName.toLowerCase() != 'li'){
31727 r.pasteHTML('<br/>');
31734 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31735 // this.cleanUpPaste.defer(100, this);
31741 }else if(Roo.isOpera){
31742 return function(e){
31743 var k = e.getKey();
31747 this.execCmd('InsertHTML','    ');
31751 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31752 // this.cleanUpPaste.defer(100, this);
31757 }else if(Roo.isSafari){
31758 return function(e){
31759 var k = e.getKey();
31763 this.execCmd('InsertText','\t');
31767 this.mozKeyPress(e);
31769 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31770 // this.cleanUpPaste.defer(100, this);
31778 getAllAncestors: function()
31780 var p = this.getSelectedNode();
31783 a.push(p); // push blank onto stack..
31784 p = this.getParentElement();
31788 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31792 a.push(this.doc.body);
31796 lastSelNode : false,
31799 getSelection : function()
31801 this.assignDocWin();
31802 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31805 * Select a dom node
31806 * @param {DomElement} node the node to select
31808 selectNode : function(node, collapse)
31810 var nodeRange = node.ownerDocument.createRange();
31812 nodeRange.selectNode(node);
31814 nodeRange.selectNodeContents(node);
31816 if (collapse === true) {
31817 nodeRange.collapse(true);
31820 var s = this.win.getSelection();
31821 s.removeAllRanges();
31822 s.addRange(nodeRange);
31825 getSelectedNode: function()
31827 // this may only work on Gecko!!!
31829 // should we cache this!!!!
31833 var range = this.createRange(this.getSelection()).cloneRange();
31836 var parent = range.parentElement();
31838 var testRange = range.duplicate();
31839 testRange.moveToElementText(parent);
31840 if (testRange.inRange(range)) {
31843 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31846 parent = parent.parentElement;
31851 // is ancestor a text element.
31852 var ac = range.commonAncestorContainer;
31853 if (ac.nodeType == 3) {
31854 ac = ac.parentNode;
31857 var ar = ac.childNodes;
31860 var other_nodes = [];
31861 var has_other_nodes = false;
31862 for (var i=0;i<ar.length;i++) {
31863 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31866 // fullly contained node.
31868 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31873 // probably selected..
31874 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31875 other_nodes.push(ar[i]);
31879 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31884 has_other_nodes = true;
31886 if (!nodes.length && other_nodes.length) {
31887 nodes= other_nodes;
31889 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31897 createRange: function(sel)
31899 // this has strange effects when using with
31900 // top toolbar - not sure if it's a great idea.
31901 //this.editor.contentWindow.focus();
31902 if (typeof sel != "undefined") {
31904 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31906 return this.doc.createRange();
31909 return this.doc.createRange();
31912 getParentElement: function()
31915 this.assignDocWin();
31916 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31918 var range = this.createRange(sel);
31921 var p = range.commonAncestorContainer;
31922 while (p.nodeType == 3) { // text node
31933 * Range intersection.. the hard stuff...
31937 * [ -- selected range --- ]
31941 * if end is before start or hits it. fail.
31942 * if start is after end or hits it fail.
31944 * if either hits (but other is outside. - then it's not
31950 // @see http://www.thismuchiknow.co.uk/?p=64.
31951 rangeIntersectsNode : function(range, node)
31953 var nodeRange = node.ownerDocument.createRange();
31955 nodeRange.selectNode(node);
31957 nodeRange.selectNodeContents(node);
31960 var rangeStartRange = range.cloneRange();
31961 rangeStartRange.collapse(true);
31963 var rangeEndRange = range.cloneRange();
31964 rangeEndRange.collapse(false);
31966 var nodeStartRange = nodeRange.cloneRange();
31967 nodeStartRange.collapse(true);
31969 var nodeEndRange = nodeRange.cloneRange();
31970 nodeEndRange.collapse(false);
31972 return rangeStartRange.compareBoundaryPoints(
31973 Range.START_TO_START, nodeEndRange) == -1 &&
31974 rangeEndRange.compareBoundaryPoints(
31975 Range.START_TO_START, nodeStartRange) == 1;
31979 rangeCompareNode : function(range, node)
31981 var nodeRange = node.ownerDocument.createRange();
31983 nodeRange.selectNode(node);
31985 nodeRange.selectNodeContents(node);
31989 range.collapse(true);
31991 nodeRange.collapse(true);
31993 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31994 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
31996 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31998 var nodeIsBefore = ss == 1;
31999 var nodeIsAfter = ee == -1;
32001 if (nodeIsBefore && nodeIsAfter) {
32004 if (!nodeIsBefore && nodeIsAfter) {
32005 return 1; //right trailed.
32008 if (nodeIsBefore && !nodeIsAfter) {
32009 return 2; // left trailed.
32015 cleanWordChars : function(input) {// change the chars to hex code
32018 [ 8211, "–" ],
32019 [ 8212, "—" ],
32027 var output = input;
32028 Roo.each(swapCodes, function(sw) {
32029 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32031 output = output.replace(swapper, sw[1]);
32041 cleanUpChild : function (node)
32044 new Roo.htmleditor.FilterComment({node : node});
32045 new Roo.htmleditor.FilterAttributes({
32047 attrib_black : this.ablack,
32048 attrib_clean : this.aclean,
32049 style_white : this.cwhite,
32050 style_black : this.cblack
32052 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32053 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32059 * Clean up MS wordisms...
32060 * @deprecated - use filter directly
32062 cleanWord : function(node)
32064 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32065 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32072 * @deprecated - use filters
32074 cleanTableWidths : function(node)
32076 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32083 applyBlacklists : function()
32085 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32086 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32088 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32089 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32090 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32094 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32095 if (b.indexOf(tag) > -1) {
32098 this.white.push(tag);
32102 Roo.each(w, function(tag) {
32103 if (b.indexOf(tag) > -1) {
32106 if (this.white.indexOf(tag) > -1) {
32109 this.white.push(tag);
32114 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32115 if (w.indexOf(tag) > -1) {
32118 this.black.push(tag);
32122 Roo.each(b, function(tag) {
32123 if (w.indexOf(tag) > -1) {
32126 if (this.black.indexOf(tag) > -1) {
32129 this.black.push(tag);
32134 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32135 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32139 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32140 if (b.indexOf(tag) > -1) {
32143 this.cwhite.push(tag);
32147 Roo.each(w, function(tag) {
32148 if (b.indexOf(tag) > -1) {
32151 if (this.cwhite.indexOf(tag) > -1) {
32154 this.cwhite.push(tag);
32159 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32160 if (w.indexOf(tag) > -1) {
32163 this.cblack.push(tag);
32167 Roo.each(b, function(tag) {
32168 if (w.indexOf(tag) > -1) {
32171 if (this.cblack.indexOf(tag) > -1) {
32174 this.cblack.push(tag);
32179 setStylesheets : function(stylesheets)
32181 if(typeof(stylesheets) == 'string'){
32182 Roo.get(this.iframe.contentDocument.head).createChild({
32184 rel : 'stylesheet',
32193 Roo.each(stylesheets, function(s) {
32198 Roo.get(_this.iframe.contentDocument.head).createChild({
32200 rel : 'stylesheet',
32210 updateLanguage : function()
32212 if (!this.iframe || !this.iframe.contentDocument) {
32215 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32219 removeStylesheets : function()
32223 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32228 setStyle : function(style)
32230 Roo.get(this.iframe.contentDocument.head).createChild({
32239 // hide stuff that is not compatible
32253 * @event specialkey
32257 * @cfg {String} fieldClass @hide
32260 * @cfg {String} focusClass @hide
32263 * @cfg {String} autoCreate @hide
32266 * @cfg {String} inputType @hide
32269 * @cfg {String} invalidClass @hide
32272 * @cfg {String} invalidText @hide
32275 * @cfg {String} msgFx @hide
32278 * @cfg {String} validateOnBlur @hide
32282 Roo.HtmlEditorCore.white = [
32283 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32285 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32286 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32287 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32288 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32289 'TABLE', 'UL', 'XMP',
32291 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32294 'DIR', 'MENU', 'OL', 'UL', 'DL',
32300 Roo.HtmlEditorCore.black = [
32301 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32303 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32304 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32305 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32306 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32307 //'FONT' // CLEAN LATER..
32308 'COLGROUP', 'COL' // messy tables.
32312 Roo.HtmlEditorCore.clean = [ // ?? needed???
32313 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32315 Roo.HtmlEditorCore.tag_remove = [
32320 Roo.HtmlEditorCore.ablack = [
32324 Roo.HtmlEditorCore.aclean = [
32325 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32329 Roo.HtmlEditorCore.pwhite= [
32330 'http', 'https', 'mailto'
32333 // white listed style attributes.
32334 Roo.HtmlEditorCore.cwhite= [
32335 // 'text-align', /// default is to allow most things..
32341 // black listed style attributes.
32342 Roo.HtmlEditorCore.cblack= [
32343 // 'font-size' -- this can be set by the project
32357 * @class Roo.bootstrap.form.HtmlEditor
32358 * @extends Roo.bootstrap.form.TextArea
32359 * Bootstrap HtmlEditor class
32362 * Create a new HtmlEditor
32363 * @param {Object} config The config object
32366 Roo.bootstrap.form.HtmlEditor = function(config){
32367 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32368 if (!this.toolbars) {
32369 this.toolbars = [];
32372 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32375 * @event initialize
32376 * Fires when the editor is fully initialized (including the iframe)
32377 * @param {HtmlEditor} this
32382 * Fires when the editor is first receives the focus. Any insertion must wait
32383 * until after this event.
32384 * @param {HtmlEditor} this
32388 * @event beforesync
32389 * Fires before the textarea is updated with content from the editor iframe. Return false
32390 * to cancel the sync.
32391 * @param {HtmlEditor} this
32392 * @param {String} html
32396 * @event beforepush
32397 * Fires before the iframe editor is updated with content from the textarea. Return false
32398 * to cancel the push.
32399 * @param {HtmlEditor} this
32400 * @param {String} html
32405 * Fires when the textarea is updated with content from the editor iframe.
32406 * @param {HtmlEditor} this
32407 * @param {String} html
32412 * Fires when the iframe editor is updated with content from the textarea.
32413 * @param {HtmlEditor} this
32414 * @param {String} html
32418 * @event editmodechange
32419 * Fires when the editor switches edit modes
32420 * @param {HtmlEditor} this
32421 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32423 editmodechange: true,
32425 * @event editorevent
32426 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32427 * @param {HtmlEditor} this
32431 * @event firstfocus
32432 * Fires when on first focus - needed by toolbars..
32433 * @param {HtmlEditor} this
32438 * Auto save the htmlEditor value as a file into Events
32439 * @param {HtmlEditor} this
32443 * @event savedpreview
32444 * preview the saved version of htmlEditor
32445 * @param {HtmlEditor} this
32452 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32456 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32461 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32466 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32470 * @cfg {Number} height (in pixels)
32474 * @cfg {Number} width (in pixels)
32479 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32482 stylesheets: false,
32487 // private properties
32488 validationEvent : false,
32490 initialized : false,
32493 onFocus : Roo.emptyFn,
32495 hideMode:'offsets',
32497 tbContainer : false,
32501 toolbarContainer :function() {
32502 return this.wrap.select('.x-html-editor-tb',true).first();
32506 * Protected method that will not generally be called directly. It
32507 * is called when the editor creates its toolbar. Override this method if you need to
32508 * add custom toolbar buttons.
32509 * @param {HtmlEditor} editor
32511 createToolbar : function(){
32512 Roo.log('renewing');
32513 Roo.log("create toolbars");
32515 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32516 this.toolbars[0].render(this.toolbarContainer());
32520 // if (!editor.toolbars || !editor.toolbars.length) {
32521 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32524 // for (var i =0 ; i < editor.toolbars.length;i++) {
32525 // editor.toolbars[i] = Roo.factory(
32526 // typeof(editor.toolbars[i]) == 'string' ?
32527 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
32528 // Roo.bootstrap.form.HtmlEditor);
32529 // editor.toolbars[i].init(editor);
32535 onRender : function(ct, position)
32537 // Roo.log("Call onRender: " + this.xtype);
32539 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32541 this.wrap = this.inputEl().wrap({
32542 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32545 this.editorcore.onRender(ct, position);
32548 this.createToolbar(this);
32556 onResize : function(w, h)
32558 Roo.log('resize: ' +w + ',' + h );
32559 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32563 if(this.inputEl() ){
32564 if(typeof w == 'number'){
32565 var aw = w - this.wrap.getFrameWidth('lr');
32566 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32569 if(typeof h == 'number'){
32570 var tbh = -11; // fixme it needs to tool bar size!
32571 for (var i =0; i < this.toolbars.length;i++) {
32572 // fixme - ask toolbars for heights?
32573 tbh += this.toolbars[i].el.getHeight();
32574 //if (this.toolbars[i].footer) {
32575 // tbh += this.toolbars[i].footer.el.getHeight();
32583 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32584 ah -= 5; // knock a few pixes off for look..
32585 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32589 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32590 this.editorcore.onResize(ew,eh);
32595 * Toggles the editor between standard and source edit mode.
32596 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32598 toggleSourceEdit : function(sourceEditMode)
32600 this.editorcore.toggleSourceEdit(sourceEditMode);
32602 if(this.editorcore.sourceEditMode){
32603 Roo.log('editor - showing textarea');
32606 // Roo.log(this.syncValue());
32608 this.inputEl().removeClass(['hide', 'x-hidden']);
32609 this.inputEl().dom.removeAttribute('tabIndex');
32610 this.inputEl().focus();
32612 Roo.log('editor - hiding textarea');
32614 // Roo.log(this.pushValue());
32617 this.inputEl().addClass(['hide', 'x-hidden']);
32618 this.inputEl().dom.setAttribute('tabIndex', -1);
32619 //this.deferFocus();
32622 //if(this.resizable){
32623 // this.setSize(this.wrap.getSize());
32626 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32629 // private (for BoxComponent)
32630 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32632 // private (for BoxComponent)
32633 getResizeEl : function(){
32637 // private (for BoxComponent)
32638 getPositionEl : function(){
32643 initEvents : function(){
32644 this.originalValue = this.getValue();
32648 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32651 // markInvalid : Roo.emptyFn,
32653 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32656 // clearInvalid : Roo.emptyFn,
32658 setValue : function(v){
32659 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32660 this.editorcore.pushValue();
32665 deferFocus : function(){
32666 this.focus.defer(10, this);
32670 focus : function(){
32671 this.editorcore.focus();
32677 onDestroy : function(){
32683 for (var i =0; i < this.toolbars.length;i++) {
32684 // fixme - ask toolbars for heights?
32685 this.toolbars[i].onDestroy();
32688 this.wrap.dom.innerHTML = '';
32689 this.wrap.remove();
32694 onFirstFocus : function(){
32695 //Roo.log("onFirstFocus");
32696 this.editorcore.onFirstFocus();
32697 for (var i =0; i < this.toolbars.length;i++) {
32698 this.toolbars[i].onFirstFocus();
32704 syncValue : function()
32706 this.editorcore.syncValue();
32709 pushValue : function()
32711 this.editorcore.pushValue();
32715 // hide stuff that is not compatible
32729 * @event specialkey
32733 * @cfg {String} fieldClass @hide
32736 * @cfg {String} focusClass @hide
32739 * @cfg {String} autoCreate @hide
32742 * @cfg {String} inputType @hide
32746 * @cfg {String} invalidText @hide
32749 * @cfg {String} msgFx @hide
32752 * @cfg {String} validateOnBlur @hide
32761 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32763 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32764 * @parent Roo.bootstrap.form.HtmlEditor
32765 * @extends Roo.bootstrap.nav.Simplebar
32771 new Roo.bootstrap.form.HtmlEditor({
32774 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32775 disable : { fonts: 1 , format: 1, ..., ... , ...],
32781 * @cfg {Object} disable List of elements to disable..
32782 * @cfg {Array} btns List of additional buttons.
32786 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32789 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32792 Roo.apply(this, config);
32794 // default disabled, based on 'good practice'..
32795 this.disable = this.disable || {};
32796 Roo.applyIf(this.disable, {
32799 specialElements : true
32801 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32803 this.editor = config.editor;
32804 this.editorcore = config.editor.editorcore;
32806 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32808 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32809 // dont call parent... till later.
32811 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
32816 editorcore : false,
32821 "h1","h2","h3","h4","h5","h6",
32823 "abbr", "acronym", "address", "cite", "samp", "var",
32827 onRender : function(ct, position)
32829 // Roo.log("Call onRender: " + this.xtype);
32831 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32833 this.el.dom.style.marginBottom = '0';
32835 var editorcore = this.editorcore;
32836 var editor= this.editor;
32839 var btn = function(id,cmd , toggle, handler, html){
32841 var event = toggle ? 'toggle' : 'click';
32846 xns: Roo.bootstrap,
32850 enableToggle:toggle !== false,
32852 pressed : toggle ? false : null,
32855 a.listeners[toggle ? 'toggle' : 'click'] = function() {
32856 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
32862 // var cb_box = function...
32867 xns: Roo.bootstrap,
32872 xns: Roo.bootstrap,
32876 Roo.each(this.formats, function(f) {
32877 style.menu.items.push({
32879 xns: Roo.bootstrap,
32880 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32885 editorcore.insertTag(this.tagname);
32892 children.push(style);
32894 btn('bold',false,true);
32895 btn('italic',false,true);
32896 btn('align-left', 'justifyleft',true);
32897 btn('align-center', 'justifycenter',true);
32898 btn('align-right' , 'justifyright',true);
32899 btn('link', false, false, function(btn) {
32900 //Roo.log("create link?");
32901 var url = prompt(this.createLinkText, this.defaultLinkValue);
32902 if(url && url != 'http:/'+'/'){
32903 this.editorcore.relayCmd('createlink', url);
32906 btn('list','insertunorderedlist',true);
32907 btn('pencil', false,true, function(btn){
32909 this.toggleSourceEdit(btn.pressed);
32912 if (this.editor.btns.length > 0) {
32913 for (var i = 0; i<this.editor.btns.length; i++) {
32914 children.push(this.editor.btns[i]);
32922 xns: Roo.bootstrap,
32927 xns: Roo.bootstrap,
32932 cog.menu.items.push({
32934 xns: Roo.bootstrap,
32935 html : Clean styles,
32940 editorcore.insertTag(this.tagname);
32949 this.xtype = 'NavSimplebar';
32951 for(var i=0;i< children.length;i++) {
32953 this.buttons.add(this.addxtypeChild(children[i]));
32957 editor.on('editorevent', this.updateToolbar, this);
32959 onBtnClick : function(id)
32961 this.editorcore.relayCmd(id);
32962 this.editorcore.focus();
32966 * Protected method that will not generally be called directly. It triggers
32967 * a toolbar update by reading the markup state of the current selection in the editor.
32969 updateToolbar: function(){
32971 if(!this.editorcore.activated){
32972 this.editor.onFirstFocus(); // is this neeed?
32976 var btns = this.buttons;
32977 var doc = this.editorcore.doc;
32978 btns.get('bold').setActive(doc.queryCommandState('bold'));
32979 btns.get('italic').setActive(doc.queryCommandState('italic'));
32980 //btns.get('underline').setActive(doc.queryCommandState('underline'));
32982 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32983 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32984 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32986 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32987 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32990 var ans = this.editorcore.getAllAncestors();
32991 if (this.formatCombo) {
32994 var store = this.formatCombo.store;
32995 this.formatCombo.setValue("");
32996 for (var i =0; i < ans.length;i++) {
32997 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32999 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33007 // hides menus... - so this cant be on a menu...
33008 Roo.bootstrap.MenuMgr.hideAll();
33010 Roo.bootstrap.menu.Manager.hideAll();
33011 //this.editorsyncValue();
33013 onFirstFocus: function() {
33014 this.buttons.each(function(item){
33018 toggleSourceEdit : function(sourceEditMode){
33021 if(sourceEditMode){
33022 Roo.log("disabling buttons");
33023 this.buttons.each( function(item){
33024 if(item.cmd != 'pencil'){
33030 Roo.log("enabling buttons");
33031 if(this.editorcore.initialized){
33032 this.buttons.each( function(item){
33038 Roo.log("calling toggole on editor");
33039 // tell the editor that it's been pressed..
33040 this.editor.toggleSourceEdit(sourceEditMode);
33054 * @class Roo.bootstrap.form.Markdown
33055 * @extends Roo.bootstrap.form.TextArea
33056 * Bootstrap Showdown editable area
33057 * @cfg {string} content
33060 * Create a new Showdown
33063 Roo.bootstrap.form.Markdown = function(config){
33064 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33068 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33072 initEvents : function()
33075 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33076 this.markdownEl = this.el.createChild({
33077 cls : 'roo-markdown-area'
33079 this.inputEl().addClass('d-none');
33080 if (this.getValue() == '') {
33081 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33084 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33086 this.markdownEl.on('click', this.toggleTextEdit, this);
33087 this.on('blur', this.toggleTextEdit, this);
33088 this.on('specialkey', this.resizeTextArea, this);
33091 toggleTextEdit : function()
33093 var sh = this.markdownEl.getHeight();
33094 this.inputEl().addClass('d-none');
33095 this.markdownEl.addClass('d-none');
33096 if (!this.editing) {
33098 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33099 this.inputEl().removeClass('d-none');
33100 this.inputEl().focus();
33101 this.editing = true;
33104 // show showdown...
33105 this.updateMarkdown();
33106 this.markdownEl.removeClass('d-none');
33107 this.editing = false;
33110 updateMarkdown : function()
33112 if (this.getValue() == '') {
33113 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33117 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33120 resizeTextArea: function () {
33123 Roo.log([sh, this.getValue().split("\n").length * 30]);
33124 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33126 setValue : function(val)
33128 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33129 if (!this.editing) {
33130 this.updateMarkdown();
33136 if (!this.editing) {
33137 this.toggleTextEdit();
33145 * Ext JS Library 1.1.1
33146 * Copyright(c) 2006-2007, Ext JS, LLC.
33148 * Originally Released Under LGPL - original licence link has changed is not relivant.
33151 * <script type="text/javascript">
33155 * @class Roo.bootstrap.PagingToolbar
33156 * @extends Roo.bootstrap.nav.Simplebar
33157 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33159 * Create a new PagingToolbar
33160 * @param {Object} config The config object
33161 * @param {Roo.data.Store} store
33163 Roo.bootstrap.PagingToolbar = function(config)
33165 // old args format still supported... - xtype is prefered..
33166 // created from xtype...
33168 this.ds = config.dataSource;
33170 if (config.store && !this.ds) {
33171 this.store= Roo.factory(config.store, Roo.data);
33172 this.ds = this.store;
33173 this.ds.xmodule = this.xmodule || false;
33176 this.toolbarItems = [];
33177 if (config.items) {
33178 this.toolbarItems = config.items;
33181 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33186 this.bind(this.ds);
33189 if (Roo.bootstrap.version == 4) {
33190 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33192 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33197 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33199 * @cfg {Roo.bootstrap.Button} buttons[]
33200 * Buttons for the toolbar
33203 * @cfg {Roo.data.Store} store
33204 * The underlying data store providing the paged data
33207 * @cfg {String/HTMLElement/Element} container
33208 * container The id or element that will contain the toolbar
33211 * @cfg {Boolean} displayInfo
33212 * True to display the displayMsg (defaults to false)
33215 * @cfg {Number} pageSize
33216 * The number of records to display per page (defaults to 20)
33220 * @cfg {String} displayMsg
33221 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33223 displayMsg : 'Displaying {0} - {1} of {2}',
33225 * @cfg {String} emptyMsg
33226 * The message to display when no records are found (defaults to "No data to display")
33228 emptyMsg : 'No data to display',
33230 * Customizable piece of the default paging text (defaults to "Page")
33233 beforePageText : "Page",
33235 * Customizable piece of the default paging text (defaults to "of %0")
33238 afterPageText : "of {0}",
33240 * Customizable piece of the default paging text (defaults to "First Page")
33243 firstText : "First Page",
33245 * Customizable piece of the default paging text (defaults to "Previous Page")
33248 prevText : "Previous Page",
33250 * Customizable piece of the default paging text (defaults to "Next Page")
33253 nextText : "Next Page",
33255 * Customizable piece of the default paging text (defaults to "Last Page")
33258 lastText : "Last Page",
33260 * Customizable piece of the default paging text (defaults to "Refresh")
33263 refreshText : "Refresh",
33267 onRender : function(ct, position)
33269 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33270 this.navgroup.parentId = this.id;
33271 this.navgroup.onRender(this.el, null);
33272 // add the buttons to the navgroup
33274 if(this.displayInfo){
33275 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33276 this.displayEl = this.el.select('.x-paging-info', true).first();
33277 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33278 // this.displayEl = navel.el.select('span',true).first();
33284 Roo.each(_this.buttons, function(e){ // this might need to use render????
33285 Roo.factory(e).render(_this.el);
33289 Roo.each(_this.toolbarItems, function(e) {
33290 _this.navgroup.addItem(e);
33294 this.first = this.navgroup.addItem({
33295 tooltip: this.firstText,
33296 cls: "prev btn-outline-secondary",
33297 html : ' <i class="fa fa-step-backward"></i>',
33299 preventDefault: true,
33300 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33303 this.prev = this.navgroup.addItem({
33304 tooltip: this.prevText,
33305 cls: "prev btn-outline-secondary",
33306 html : ' <i class="fa fa-backward"></i>',
33308 preventDefault: true,
33309 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33311 //this.addSeparator();
33314 var field = this.navgroup.addItem( {
33316 cls : 'x-paging-position btn-outline-secondary',
33318 html : this.beforePageText +
33319 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33320 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33323 this.field = field.el.select('input', true).first();
33324 this.field.on("keydown", this.onPagingKeydown, this);
33325 this.field.on("focus", function(){this.dom.select();});
33328 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33329 //this.field.setHeight(18);
33330 //this.addSeparator();
33331 this.next = this.navgroup.addItem({
33332 tooltip: this.nextText,
33333 cls: "next btn-outline-secondary",
33334 html : ' <i class="fa fa-forward"></i>',
33336 preventDefault: true,
33337 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33339 this.last = this.navgroup.addItem({
33340 tooltip: this.lastText,
33341 html : ' <i class="fa fa-step-forward"></i>',
33342 cls: "next btn-outline-secondary",
33344 preventDefault: true,
33345 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33347 //this.addSeparator();
33348 this.loading = this.navgroup.addItem({
33349 tooltip: this.refreshText,
33350 cls: "btn-outline-secondary",
33351 html : ' <i class="fa fa-refresh"></i>',
33352 preventDefault: true,
33353 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33359 updateInfo : function(){
33360 if(this.displayEl){
33361 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33362 var msg = count == 0 ?
33366 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33368 this.displayEl.update(msg);
33373 onLoad : function(ds, r, o)
33375 this.cursor = o.params && o.params.start ? o.params.start : 0;
33377 var d = this.getPageData(),
33382 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33383 this.field.dom.value = ap;
33384 this.first.setDisabled(ap == 1);
33385 this.prev.setDisabled(ap == 1);
33386 this.next.setDisabled(ap == ps);
33387 this.last.setDisabled(ap == ps);
33388 this.loading.enable();
33393 getPageData : function(){
33394 var total = this.ds.getTotalCount();
33397 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33398 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33403 onLoadError : function(proxy, o){
33404 this.loading.enable();
33405 if (this.ds.events.loadexception.listeners.length < 2) {
33406 // nothing has been assigned to loadexception except this...
33408 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33414 onPagingKeydown : function(e){
33415 var k = e.getKey();
33416 var d = this.getPageData();
33418 var v = this.field.dom.value, pageNum;
33419 if(!v || isNaN(pageNum = parseInt(v, 10))){
33420 this.field.dom.value = d.activePage;
33423 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33424 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33427 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))
33429 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33430 this.field.dom.value = pageNum;
33431 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33434 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33436 var v = this.field.dom.value, pageNum;
33437 var increment = (e.shiftKey) ? 10 : 1;
33438 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33441 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33442 this.field.dom.value = d.activePage;
33445 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33447 this.field.dom.value = parseInt(v, 10) + increment;
33448 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33449 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33456 beforeLoad : function(){
33458 this.loading.disable();
33463 onClick : function(which){
33472 ds.load({params:{start: 0, limit: this.pageSize}});
33475 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33478 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33481 var total = ds.getTotalCount();
33482 var extra = total % this.pageSize;
33483 var lastStart = extra ? (total - extra) : total-this.pageSize;
33484 ds.load({params:{start: lastStart, limit: this.pageSize}});
33487 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33493 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33494 * @param {Roo.data.Store} store The data store to unbind
33496 unbind : function(ds){
33497 ds.un("beforeload", this.beforeLoad, this);
33498 ds.un("load", this.onLoad, this);
33499 ds.un("loadexception", this.onLoadError, this);
33500 ds.un("remove", this.updateInfo, this);
33501 ds.un("add", this.updateInfo, this);
33502 this.ds = undefined;
33506 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33507 * @param {Roo.data.Store} store The data store to bind
33509 bind : function(ds){
33510 ds.on("beforeload", this.beforeLoad, this);
33511 ds.on("load", this.onLoad, this);
33512 ds.on("loadexception", this.onLoadError, this);
33513 ds.on("remove", this.updateInfo, this);
33514 ds.on("add", this.updateInfo, this);
33525 * @class Roo.bootstrap.MessageBar
33526 * @extends Roo.bootstrap.Component
33527 * Bootstrap MessageBar class
33528 * @cfg {String} html contents of the MessageBar
33529 * @cfg {String} weight (info | success | warning | danger) default info
33530 * @cfg {String} beforeClass insert the bar before the given class
33531 * @cfg {Boolean} closable (true | false) default false
33532 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33535 * Create a new Element
33536 * @param {Object} config The config object
33539 Roo.bootstrap.MessageBar = function(config){
33540 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33543 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33549 beforeClass: 'bootstrap-sticky-wrap',
33551 getAutoCreate : function(){
33555 cls: 'alert alert-dismissable alert-' + this.weight,
33560 html: this.html || ''
33566 cfg.cls += ' alert-messages-fixed';
33580 onRender : function(ct, position)
33582 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33585 var cfg = Roo.apply({}, this.getAutoCreate());
33589 cfg.cls += ' ' + this.cls;
33592 cfg.style = this.style;
33594 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33596 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33599 this.el.select('>button.close').on('click', this.hide, this);
33605 if (!this.rendered) {
33611 this.fireEvent('show', this);
33617 if (!this.rendered) {
33623 this.fireEvent('hide', this);
33626 update : function()
33628 // var e = this.el.dom.firstChild;
33630 // if(this.closable){
33631 // e = e.nextSibling;
33634 // e.data = this.html || '';
33636 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33652 * @class Roo.bootstrap.Graph
33653 * @extends Roo.bootstrap.Component
33654 * Bootstrap Graph class
33658 @cfg {String} graphtype bar | vbar | pie
33659 @cfg {number} g_x coodinator | centre x (pie)
33660 @cfg {number} g_y coodinator | centre y (pie)
33661 @cfg {number} g_r radius (pie)
33662 @cfg {number} g_height height of the chart (respected by all elements in the set)
33663 @cfg {number} g_width width of the chart (respected by all elements in the set)
33664 @cfg {Object} title The title of the chart
33667 -opts (object) options for the chart
33669 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33670 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33672 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.
33673 o stacked (boolean) whether or not to tread values as in a stacked bar chart
33675 o stretch (boolean)
33677 -opts (object) options for the pie
33680 o startAngle (number)
33681 o endAngle (number)
33685 * Create a new Input
33686 * @param {Object} config The config object
33689 Roo.bootstrap.Graph = function(config){
33690 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33696 * The img click event for the img.
33697 * @param {Roo.EventObject} e
33703 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
33714 //g_colors: this.colors,
33721 getAutoCreate : function(){
33732 onRender : function(ct,position){
33735 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33737 if (typeof(Raphael) == 'undefined') {
33738 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33742 this.raphael = Raphael(this.el.dom);
33744 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33745 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33746 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33747 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33749 r.text(160, 10, "Single Series Chart").attr(txtattr);
33750 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33751 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33752 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33754 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33755 r.barchart(330, 10, 300, 220, data1);
33756 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33757 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33760 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33761 // r.barchart(30, 30, 560, 250, xdata, {
33762 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33763 // axis : "0 0 1 1",
33764 // axisxlabels : xdata
33765 // //yvalues : cols,
33768 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33770 // this.load(null,xdata,{
33771 // axis : "0 0 1 1",
33772 // axisxlabels : xdata
33777 load : function(graphtype,xdata,opts)
33779 this.raphael.clear();
33781 graphtype = this.graphtype;
33786 var r = this.raphael,
33787 fin = function () {
33788 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33790 fout = function () {
33791 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33793 pfin = function() {
33794 this.sector.stop();
33795 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33798 this.label[0].stop();
33799 this.label[0].attr({ r: 7.5 });
33800 this.label[1].attr({ "font-weight": 800 });
33803 pfout = function() {
33804 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33807 this.label[0].animate({ r: 5 }, 500, "bounce");
33808 this.label[1].attr({ "font-weight": 400 });
33814 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33817 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33820 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
33821 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33823 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33830 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33835 setTitle: function(o)
33840 initEvents: function() {
33843 this.el.on('click', this.onClick, this);
33847 onClick : function(e)
33849 Roo.log('img onclick');
33850 this.fireEvent('click', this, e);
33856 Roo.bootstrap.dash = {};/*
33862 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33865 * @class Roo.bootstrap.dash.NumberBox
33866 * @extends Roo.bootstrap.Component
33867 * Bootstrap NumberBox class
33868 * @cfg {String} headline Box headline
33869 * @cfg {String} content Box content
33870 * @cfg {String} icon Box icon
33871 * @cfg {String} footer Footer text
33872 * @cfg {String} fhref Footer href
33875 * Create a new NumberBox
33876 * @param {Object} config The config object
33880 Roo.bootstrap.dash.NumberBox = function(config){
33881 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33885 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
33894 getAutoCreate : function(){
33898 cls : 'small-box ',
33906 cls : 'roo-headline',
33907 html : this.headline
33911 cls : 'roo-content',
33912 html : this.content
33926 cls : 'ion ' + this.icon
33935 cls : 'small-box-footer',
33936 href : this.fhref || '#',
33940 cfg.cn.push(footer);
33947 onRender : function(ct,position){
33948 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33955 setHeadline: function (value)
33957 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33960 setFooter: function (value, href)
33962 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33965 this.el.select('a.small-box-footer',true).first().attr('href', href);
33970 setContent: function (value)
33972 this.el.select('.roo-content',true).first().dom.innerHTML = value;
33975 initEvents: function()
33989 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33992 * @class Roo.bootstrap.dash.TabBox
33993 * @extends Roo.bootstrap.Component
33994 * @children Roo.bootstrap.dash.TabPane
33995 * Bootstrap TabBox class
33996 * @cfg {String} title Title of the TabBox
33997 * @cfg {String} icon Icon of the TabBox
33998 * @cfg {Boolean} showtabs (true|false) show the tabs default true
33999 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34002 * Create a new TabBox
34003 * @param {Object} config The config object
34007 Roo.bootstrap.dash.TabBox = function(config){
34008 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34013 * When a pane is added
34014 * @param {Roo.bootstrap.dash.TabPane} pane
34018 * @event activatepane
34019 * When a pane is activated
34020 * @param {Roo.bootstrap.dash.TabPane} pane
34022 "activatepane" : true
34030 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34035 tabScrollable : false,
34037 getChildContainer : function()
34039 return this.el.select('.tab-content', true).first();
34042 getAutoCreate : function(){
34046 cls: 'pull-left header',
34054 cls: 'fa ' + this.icon
34060 cls: 'nav nav-tabs pull-right',
34066 if(this.tabScrollable){
34073 cls: 'nav nav-tabs pull-right',
34084 cls: 'nav-tabs-custom',
34089 cls: 'tab-content no-padding',
34097 initEvents : function()
34099 //Roo.log('add add pane handler');
34100 this.on('addpane', this.onAddPane, this);
34103 * Updates the box title
34104 * @param {String} html to set the title to.
34106 setTitle : function(value)
34108 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34110 onAddPane : function(pane)
34112 this.panes.push(pane);
34113 //Roo.log('addpane');
34115 // tabs are rendere left to right..
34116 if(!this.showtabs){
34120 var ctr = this.el.select('.nav-tabs', true).first();
34123 var existing = ctr.select('.nav-tab',true);
34124 var qty = existing.getCount();;
34127 var tab = ctr.createChild({
34129 cls : 'nav-tab' + (qty ? '' : ' active'),
34137 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34140 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34142 pane.el.addClass('active');
34147 onTabClick : function(ev,un,ob,pane)
34149 //Roo.log('tab - prev default');
34150 ev.preventDefault();
34153 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34154 pane.tab.addClass('active');
34155 //Roo.log(pane.title);
34156 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34157 // technically we should have a deactivate event.. but maybe add later.
34158 // and it should not de-activate the selected tab...
34159 this.fireEvent('activatepane', pane);
34160 pane.el.addClass('active');
34161 pane.fireEvent('activate');
34166 getActivePane : function()
34169 Roo.each(this.panes, function(p) {
34170 if(p.el.hasClass('active')){
34191 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34193 * @class Roo.bootstrap.TabPane
34194 * @extends Roo.bootstrap.Component
34195 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34196 * Bootstrap TabPane class
34197 * @cfg {Boolean} active (false | true) Default false
34198 * @cfg {String} title title of panel
34202 * Create a new TabPane
34203 * @param {Object} config The config object
34206 Roo.bootstrap.dash.TabPane = function(config){
34207 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34213 * When a pane is activated
34214 * @param {Roo.bootstrap.dash.TabPane} pane
34221 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34226 // the tabBox that this is attached to.
34229 getAutoCreate : function()
34237 cfg.cls += ' active';
34242 initEvents : function()
34244 //Roo.log('trigger add pane handler');
34245 this.parent().fireEvent('addpane', this)
34249 * Updates the tab title
34250 * @param {String} html to set the title to.
34252 setTitle: function(str)
34258 this.tab.select('a', true).first().dom.innerHTML = str;
34277 * @class Roo.bootstrap.Tooltip
34278 * Bootstrap Tooltip class
34279 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34280 * to determine which dom element triggers the tooltip.
34282 * It needs to add support for additional attributes like tooltip-position
34285 * Create a new Toolti
34286 * @param {Object} config The config object
34289 Roo.bootstrap.Tooltip = function(config){
34290 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34292 this.alignment = Roo.bootstrap.Tooltip.alignment;
34294 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34295 this.alignment = config.alignment;
34300 Roo.apply(Roo.bootstrap.Tooltip, {
34302 * @function init initialize tooltip monitoring.
34306 currentTip : false,
34307 currentRegion : false,
34313 Roo.get(document).on('mouseover', this.enter ,this);
34314 Roo.get(document).on('mouseout', this.leave, this);
34317 this.currentTip = new Roo.bootstrap.Tooltip();
34320 enter : function(ev)
34322 var dom = ev.getTarget();
34324 //Roo.log(['enter',dom]);
34325 var el = Roo.fly(dom);
34326 if (this.currentEl) {
34328 //Roo.log(this.currentEl);
34329 //Roo.log(this.currentEl.contains(dom));
34330 if (this.currentEl == el) {
34333 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34339 if (this.currentTip.el) {
34340 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34344 if(!el || el.dom == document){
34350 if (!el.attr('tooltip')) {
34351 pel = el.findParent("[tooltip]");
34353 bindEl = Roo.get(pel);
34359 // you can not look for children, as if el is the body.. then everythign is the child..
34360 if (!pel && !el.attr('tooltip')) { //
34361 if (!el.select("[tooltip]").elements.length) {
34364 // is the mouse over this child...?
34365 bindEl = el.select("[tooltip]").first();
34366 var xy = ev.getXY();
34367 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34368 //Roo.log("not in region.");
34371 //Roo.log("child element over..");
34374 this.currentEl = el;
34375 this.currentTip.bind(bindEl);
34376 this.currentRegion = Roo.lib.Region.getRegion(dom);
34377 this.currentTip.enter();
34380 leave : function(ev)
34382 var dom = ev.getTarget();
34383 //Roo.log(['leave',dom]);
34384 if (!this.currentEl) {
34389 if (dom != this.currentEl.dom) {
34392 var xy = ev.getXY();
34393 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34396 // only activate leave if mouse cursor is outside... bounding box..
34401 if (this.currentTip) {
34402 this.currentTip.leave();
34404 //Roo.log('clear currentEl');
34405 this.currentEl = false;
34410 'left' : ['r-l', [-2,0], 'right'],
34411 'right' : ['l-r', [2,0], 'left'],
34412 'bottom' : ['t-b', [0,2], 'top'],
34413 'top' : [ 'b-t', [0,-2], 'bottom']
34419 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34424 delay : null, // can be { show : 300 , hide: 500}
34428 hoverState : null, //???
34430 placement : 'bottom',
34434 getAutoCreate : function(){
34441 cls : 'tooltip-arrow arrow'
34444 cls : 'tooltip-inner'
34451 bind : function(el)
34456 initEvents : function()
34458 this.arrowEl = this.el.select('.arrow', true).first();
34459 this.innerEl = this.el.select('.tooltip-inner', true).first();
34462 enter : function () {
34464 if (this.timeout != null) {
34465 clearTimeout(this.timeout);
34468 this.hoverState = 'in';
34469 //Roo.log("enter - show");
34470 if (!this.delay || !this.delay.show) {
34475 this.timeout = setTimeout(function () {
34476 if (_t.hoverState == 'in') {
34479 }, this.delay.show);
34483 clearTimeout(this.timeout);
34485 this.hoverState = 'out';
34486 if (!this.delay || !this.delay.hide) {
34492 this.timeout = setTimeout(function () {
34493 //Roo.log("leave - timeout");
34495 if (_t.hoverState == 'out') {
34497 Roo.bootstrap.Tooltip.currentEl = false;
34502 show : function (msg)
34505 this.render(document.body);
34508 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34510 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34512 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34514 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34515 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34517 if(this.bindEl.attr('tooltip-class')) {
34518 this.el.addClass(this.bindEl.attr('tooltip-class'));
34521 var placement = typeof this.placement == 'function' ?
34522 this.placement.call(this, this.el, on_el) :
34525 if(this.bindEl.attr('tooltip-placement')) {
34526 placement = this.bindEl.attr('tooltip-placement');
34529 var autoToken = /\s?auto?\s?/i;
34530 var autoPlace = autoToken.test(placement);
34532 placement = placement.replace(autoToken, '') || 'top';
34536 //this.el.setXY([0,0]);
34538 //this.el.dom.style.display='block';
34540 //this.el.appendTo(on_el);
34542 var p = this.getPosition();
34543 var box = this.el.getBox();
34549 var align = this.alignment[placement];
34551 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34553 if(placement == 'top' || placement == 'bottom'){
34555 placement = 'right';
34558 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34559 placement = 'left';
34562 var scroll = Roo.select('body', true).first().getScroll();
34564 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34568 align = this.alignment[placement];
34570 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34574 var elems = document.getElementsByTagName('div');
34575 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34576 for (var i = 0; i < elems.length; i++) {
34577 var zindex = Number.parseInt(
34578 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34581 if (zindex > highest) {
34588 this.el.dom.style.zIndex = highest;
34590 this.el.alignTo(this.bindEl, align[0],align[1]);
34591 //var arrow = this.el.select('.arrow',true).first();
34592 //arrow.set(align[2],
34594 this.el.addClass(placement);
34595 this.el.addClass("bs-tooltip-"+ placement);
34597 this.el.addClass('in fade show');
34599 this.hoverState = null;
34601 if (this.el.hasClass('fade')) {
34616 //this.el.setXY([0,0]);
34617 if(this.bindEl.attr('tooltip-class')) {
34618 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34620 this.el.removeClass(['show', 'in']);
34636 * @class Roo.bootstrap.LocationPicker
34637 * @extends Roo.bootstrap.Component
34638 * Bootstrap LocationPicker class
34639 * @cfg {Number} latitude Position when init default 0
34640 * @cfg {Number} longitude Position when init default 0
34641 * @cfg {Number} zoom default 15
34642 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34643 * @cfg {Boolean} mapTypeControl default false
34644 * @cfg {Boolean} disableDoubleClickZoom default false
34645 * @cfg {Boolean} scrollwheel default true
34646 * @cfg {Boolean} streetViewControl default false
34647 * @cfg {Number} radius default 0
34648 * @cfg {String} locationName
34649 * @cfg {Boolean} draggable default true
34650 * @cfg {Boolean} enableAutocomplete default false
34651 * @cfg {Boolean} enableReverseGeocode default true
34652 * @cfg {String} markerTitle
34655 * Create a new LocationPicker
34656 * @param {Object} config The config object
34660 Roo.bootstrap.LocationPicker = function(config){
34662 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34667 * Fires when the picker initialized.
34668 * @param {Roo.bootstrap.LocationPicker} this
34669 * @param {Google Location} location
34673 * @event positionchanged
34674 * Fires when the picker position changed.
34675 * @param {Roo.bootstrap.LocationPicker} this
34676 * @param {Google Location} location
34678 positionchanged : true,
34681 * Fires when the map resize.
34682 * @param {Roo.bootstrap.LocationPicker} this
34687 * Fires when the map show.
34688 * @param {Roo.bootstrap.LocationPicker} this
34693 * Fires when the map hide.
34694 * @param {Roo.bootstrap.LocationPicker} this
34699 * Fires when click the map.
34700 * @param {Roo.bootstrap.LocationPicker} this
34701 * @param {Map event} e
34705 * @event mapRightClick
34706 * Fires when right click the map.
34707 * @param {Roo.bootstrap.LocationPicker} this
34708 * @param {Map event} e
34710 mapRightClick : true,
34712 * @event markerClick
34713 * Fires when click the marker.
34714 * @param {Roo.bootstrap.LocationPicker} this
34715 * @param {Map event} e
34717 markerClick : true,
34719 * @event markerRightClick
34720 * Fires when right click the marker.
34721 * @param {Roo.bootstrap.LocationPicker} this
34722 * @param {Map event} e
34724 markerRightClick : true,
34726 * @event OverlayViewDraw
34727 * Fires when OverlayView Draw
34728 * @param {Roo.bootstrap.LocationPicker} this
34730 OverlayViewDraw : true,
34732 * @event OverlayViewOnAdd
34733 * Fires when OverlayView Draw
34734 * @param {Roo.bootstrap.LocationPicker} this
34736 OverlayViewOnAdd : true,
34738 * @event OverlayViewOnRemove
34739 * Fires when OverlayView Draw
34740 * @param {Roo.bootstrap.LocationPicker} this
34742 OverlayViewOnRemove : true,
34744 * @event OverlayViewShow
34745 * Fires when OverlayView Draw
34746 * @param {Roo.bootstrap.LocationPicker} this
34747 * @param {Pixel} cpx
34749 OverlayViewShow : true,
34751 * @event OverlayViewHide
34752 * Fires when OverlayView Draw
34753 * @param {Roo.bootstrap.LocationPicker} this
34755 OverlayViewHide : true,
34757 * @event loadexception
34758 * Fires when load google lib failed.
34759 * @param {Roo.bootstrap.LocationPicker} this
34761 loadexception : true
34766 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
34768 gMapContext: false,
34774 mapTypeControl: false,
34775 disableDoubleClickZoom: false,
34777 streetViewControl: false,
34781 enableAutocomplete: false,
34782 enableReverseGeocode: true,
34785 getAutoCreate: function()
34790 cls: 'roo-location-picker'
34796 initEvents: function(ct, position)
34798 if(!this.el.getWidth() || this.isApplied()){
34802 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34807 initial: function()
34809 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34810 this.fireEvent('loadexception', this);
34814 if(!this.mapTypeId){
34815 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34818 this.gMapContext = this.GMapContext();
34820 this.initOverlayView();
34822 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34826 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34827 _this.setPosition(_this.gMapContext.marker.position);
34830 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34831 _this.fireEvent('mapClick', this, event);
34835 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34836 _this.fireEvent('mapRightClick', this, event);
34840 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34841 _this.fireEvent('markerClick', this, event);
34845 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34846 _this.fireEvent('markerRightClick', this, event);
34850 this.setPosition(this.gMapContext.location);
34852 this.fireEvent('initial', this, this.gMapContext.location);
34855 initOverlayView: function()
34859 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34863 _this.fireEvent('OverlayViewDraw', _this);
34868 _this.fireEvent('OverlayViewOnAdd', _this);
34871 onRemove: function()
34873 _this.fireEvent('OverlayViewOnRemove', _this);
34876 show: function(cpx)
34878 _this.fireEvent('OverlayViewShow', _this, cpx);
34883 _this.fireEvent('OverlayViewHide', _this);
34889 fromLatLngToContainerPixel: function(event)
34891 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34894 isApplied: function()
34896 return this.getGmapContext() == false ? false : true;
34899 getGmapContext: function()
34901 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34904 GMapContext: function()
34906 var position = new google.maps.LatLng(this.latitude, this.longitude);
34908 var _map = new google.maps.Map(this.el.dom, {
34911 mapTypeId: this.mapTypeId,
34912 mapTypeControl: this.mapTypeControl,
34913 disableDoubleClickZoom: this.disableDoubleClickZoom,
34914 scrollwheel: this.scrollwheel,
34915 streetViewControl: this.streetViewControl,
34916 locationName: this.locationName,
34917 draggable: this.draggable,
34918 enableAutocomplete: this.enableAutocomplete,
34919 enableReverseGeocode: this.enableReverseGeocode
34922 var _marker = new google.maps.Marker({
34923 position: position,
34925 title: this.markerTitle,
34926 draggable: this.draggable
34933 location: position,
34934 radius: this.radius,
34935 locationName: this.locationName,
34936 addressComponents: {
34937 formatted_address: null,
34938 addressLine1: null,
34939 addressLine2: null,
34941 streetNumber: null,
34945 stateOrProvince: null
34948 domContainer: this.el.dom,
34949 geodecoder: new google.maps.Geocoder()
34953 drawCircle: function(center, radius, options)
34955 if (this.gMapContext.circle != null) {
34956 this.gMapContext.circle.setMap(null);
34960 options = Roo.apply({}, options, {
34961 strokeColor: "#0000FF",
34962 strokeOpacity: .35,
34964 fillColor: "#0000FF",
34968 options.map = this.gMapContext.map;
34969 options.radius = radius;
34970 options.center = center;
34971 this.gMapContext.circle = new google.maps.Circle(options);
34972 return this.gMapContext.circle;
34978 setPosition: function(location)
34980 this.gMapContext.location = location;
34981 this.gMapContext.marker.setPosition(location);
34982 this.gMapContext.map.panTo(location);
34983 this.drawCircle(location, this.gMapContext.radius, {});
34987 if (this.gMapContext.settings.enableReverseGeocode) {
34988 this.gMapContext.geodecoder.geocode({
34989 latLng: this.gMapContext.location
34990 }, function(results, status) {
34992 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34993 _this.gMapContext.locationName = results[0].formatted_address;
34994 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34996 _this.fireEvent('positionchanged', this, location);
35003 this.fireEvent('positionchanged', this, location);
35008 google.maps.event.trigger(this.gMapContext.map, "resize");
35010 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35012 this.fireEvent('resize', this);
35015 setPositionByLatLng: function(latitude, longitude)
35017 this.setPosition(new google.maps.LatLng(latitude, longitude));
35020 getCurrentPosition: function()
35023 latitude: this.gMapContext.location.lat(),
35024 longitude: this.gMapContext.location.lng()
35028 getAddressName: function()
35030 return this.gMapContext.locationName;
35033 getAddressComponents: function()
35035 return this.gMapContext.addressComponents;
35038 address_component_from_google_geocode: function(address_components)
35042 for (var i = 0; i < address_components.length; i++) {
35043 var component = address_components[i];
35044 if (component.types.indexOf("postal_code") >= 0) {
35045 result.postalCode = component.short_name;
35046 } else if (component.types.indexOf("street_number") >= 0) {
35047 result.streetNumber = component.short_name;
35048 } else if (component.types.indexOf("route") >= 0) {
35049 result.streetName = component.short_name;
35050 } else if (component.types.indexOf("neighborhood") >= 0) {
35051 result.city = component.short_name;
35052 } else if (component.types.indexOf("locality") >= 0) {
35053 result.city = component.short_name;
35054 } else if (component.types.indexOf("sublocality") >= 0) {
35055 result.district = component.short_name;
35056 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35057 result.stateOrProvince = component.short_name;
35058 } else if (component.types.indexOf("country") >= 0) {
35059 result.country = component.short_name;
35063 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35064 result.addressLine2 = "";
35068 setZoomLevel: function(zoom)
35070 this.gMapContext.map.setZoom(zoom);
35083 this.fireEvent('show', this);
35094 this.fireEvent('hide', this);
35099 Roo.apply(Roo.bootstrap.LocationPicker, {
35101 OverlayView : function(map, options)
35103 options = options || {};
35110 * @class Roo.bootstrap.Alert
35111 * @extends Roo.bootstrap.Component
35112 * Bootstrap Alert class - shows an alert area box
35114 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35115 Enter a valid email address
35118 * @cfg {String} title The title of alert
35119 * @cfg {String} html The content of alert
35120 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35121 * @cfg {String} fa font-awesomeicon
35122 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35123 * @cfg {Boolean} close true to show a x closer
35127 * Create a new alert
35128 * @param {Object} config The config object
35132 Roo.bootstrap.Alert = function(config){
35133 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35137 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35143 faicon: false, // BC
35147 getAutoCreate : function()
35159 style : this.close ? '' : 'display:none'
35163 cls : 'roo-alert-icon'
35168 cls : 'roo-alert-title',
35173 cls : 'roo-alert-text',
35180 cfg.cn[0].cls += ' fa ' + this.faicon;
35183 cfg.cn[0].cls += ' fa ' + this.fa;
35187 cfg.cls += ' alert-' + this.weight;
35193 initEvents: function()
35195 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35196 this.titleEl = this.el.select('.roo-alert-title',true).first();
35197 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35198 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35199 if (this.seconds > 0) {
35200 this.hide.defer(this.seconds, this);
35204 * Set the Title Message HTML
35205 * @param {String} html
35207 setTitle : function(str)
35209 this.titleEl.dom.innerHTML = str;
35213 * Set the Body Message HTML
35214 * @param {String} html
35216 setHtml : function(str)
35218 this.htmlEl.dom.innerHTML = str;
35221 * Set the Weight of the alert
35222 * @param {String} (success|info|warning|danger) weight
35225 setWeight : function(weight)
35228 this.el.removeClass('alert-' + this.weight);
35231 this.weight = weight;
35233 this.el.addClass('alert-' + this.weight);
35236 * Set the Icon of the alert
35237 * @param {String} see fontawsome names (name without the 'fa-' bit)
35239 setIcon : function(icon)
35242 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35245 this.faicon = icon;
35247 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35272 * @class Roo.bootstrap.UploadCropbox
35273 * @extends Roo.bootstrap.Component
35274 * Bootstrap UploadCropbox class
35275 * @cfg {String} emptyText show when image has been loaded
35276 * @cfg {String} rotateNotify show when image too small to rotate
35277 * @cfg {Number} errorTimeout default 3000
35278 * @cfg {Number} minWidth default 300
35279 * @cfg {Number} minHeight default 300
35280 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35281 * @cfg {Boolean} isDocument (true|false) default false
35282 * @cfg {String} url action url
35283 * @cfg {String} paramName default 'imageUpload'
35284 * @cfg {String} method default POST
35285 * @cfg {Boolean} loadMask (true|false) default true
35286 * @cfg {Boolean} loadingText default 'Loading...'
35289 * Create a new UploadCropbox
35290 * @param {Object} config The config object
35293 Roo.bootstrap.UploadCropbox = function(config){
35294 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35298 * @event beforeselectfile
35299 * Fire before select file
35300 * @param {Roo.bootstrap.UploadCropbox} this
35302 "beforeselectfile" : true,
35305 * Fire after initEvent
35306 * @param {Roo.bootstrap.UploadCropbox} this
35311 * Fire after initEvent
35312 * @param {Roo.bootstrap.UploadCropbox} this
35313 * @param {String} data
35318 * Fire when preparing the file data
35319 * @param {Roo.bootstrap.UploadCropbox} this
35320 * @param {Object} file
35325 * Fire when get exception
35326 * @param {Roo.bootstrap.UploadCropbox} this
35327 * @param {XMLHttpRequest} xhr
35329 "exception" : true,
35331 * @event beforeloadcanvas
35332 * Fire before load the canvas
35333 * @param {Roo.bootstrap.UploadCropbox} this
35334 * @param {String} src
35336 "beforeloadcanvas" : true,
35339 * Fire when trash image
35340 * @param {Roo.bootstrap.UploadCropbox} this
35345 * Fire when download the image
35346 * @param {Roo.bootstrap.UploadCropbox} this
35350 * @event footerbuttonclick
35351 * Fire when footerbuttonclick
35352 * @param {Roo.bootstrap.UploadCropbox} this
35353 * @param {String} type
35355 "footerbuttonclick" : true,
35359 * @param {Roo.bootstrap.UploadCropbox} this
35364 * Fire when rotate the image
35365 * @param {Roo.bootstrap.UploadCropbox} this
35366 * @param {String} pos
35371 * Fire when inspect the file
35372 * @param {Roo.bootstrap.UploadCropbox} this
35373 * @param {Object} file
35378 * Fire when xhr upload the file
35379 * @param {Roo.bootstrap.UploadCropbox} this
35380 * @param {Object} data
35385 * Fire when arrange the file data
35386 * @param {Roo.bootstrap.UploadCropbox} this
35387 * @param {Object} formData
35392 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35395 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35397 emptyText : 'Click to upload image',
35398 rotateNotify : 'Image is too small to rotate',
35399 errorTimeout : 3000,
35413 cropType : 'image/jpeg',
35415 canvasLoaded : false,
35416 isDocument : false,
35418 paramName : 'imageUpload',
35420 loadingText : 'Loading...',
35423 getAutoCreate : function()
35427 cls : 'roo-upload-cropbox',
35431 cls : 'roo-upload-cropbox-selector',
35436 cls : 'roo-upload-cropbox-body',
35437 style : 'cursor:pointer',
35441 cls : 'roo-upload-cropbox-preview'
35445 cls : 'roo-upload-cropbox-thumb'
35449 cls : 'roo-upload-cropbox-empty-notify',
35450 html : this.emptyText
35454 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35455 html : this.rotateNotify
35461 cls : 'roo-upload-cropbox-footer',
35464 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35474 onRender : function(ct, position)
35476 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35478 if (this.buttons.length) {
35480 Roo.each(this.buttons, function(bb) {
35482 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35484 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35490 this.maskEl = this.el;
35494 initEvents : function()
35496 this.urlAPI = (window.createObjectURL && window) ||
35497 (window.URL && URL.revokeObjectURL && URL) ||
35498 (window.webkitURL && webkitURL);
35500 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35501 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35503 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35504 this.selectorEl.hide();
35506 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35507 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35509 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35510 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35511 this.thumbEl.hide();
35513 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35514 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35516 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35517 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35518 this.errorEl.hide();
35520 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35521 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35522 this.footerEl.hide();
35524 this.setThumbBoxSize();
35530 this.fireEvent('initial', this);
35537 window.addEventListener("resize", function() { _this.resize(); } );
35539 this.bodyEl.on('click', this.beforeSelectFile, this);
35542 this.bodyEl.on('touchstart', this.onTouchStart, this);
35543 this.bodyEl.on('touchmove', this.onTouchMove, this);
35544 this.bodyEl.on('touchend', this.onTouchEnd, this);
35548 this.bodyEl.on('mousedown', this.onMouseDown, this);
35549 this.bodyEl.on('mousemove', this.onMouseMove, this);
35550 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35551 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35552 Roo.get(document).on('mouseup', this.onMouseUp, this);
35555 this.selectorEl.on('change', this.onFileSelected, this);
35561 this.baseScale = 1;
35563 this.baseRotate = 1;
35564 this.dragable = false;
35565 this.pinching = false;
35568 this.cropData = false;
35569 this.notifyEl.dom.innerHTML = this.emptyText;
35571 this.selectorEl.dom.value = '';
35575 resize : function()
35577 if(this.fireEvent('resize', this) != false){
35578 this.setThumbBoxPosition();
35579 this.setCanvasPosition();
35583 onFooterButtonClick : function(e, el, o, type)
35586 case 'rotate-left' :
35587 this.onRotateLeft(e);
35589 case 'rotate-right' :
35590 this.onRotateRight(e);
35593 this.beforeSelectFile(e);
35608 this.fireEvent('footerbuttonclick', this, type);
35611 beforeSelectFile : function(e)
35613 e.preventDefault();
35615 if(this.fireEvent('beforeselectfile', this) != false){
35616 this.selectorEl.dom.click();
35620 onFileSelected : function(e)
35622 e.preventDefault();
35624 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35628 var file = this.selectorEl.dom.files[0];
35630 if(this.fireEvent('inspect', this, file) != false){
35631 this.prepare(file);
35636 trash : function(e)
35638 this.fireEvent('trash', this);
35641 download : function(e)
35643 this.fireEvent('download', this);
35646 loadCanvas : function(src)
35648 if(this.fireEvent('beforeloadcanvas', this, src) != false){
35652 this.imageEl = document.createElement('img');
35656 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35658 this.imageEl.src = src;
35662 onLoadCanvas : function()
35664 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35665 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35667 this.bodyEl.un('click', this.beforeSelectFile, this);
35669 this.notifyEl.hide();
35670 this.thumbEl.show();
35671 this.footerEl.show();
35673 this.baseRotateLevel();
35675 if(this.isDocument){
35676 this.setThumbBoxSize();
35679 this.setThumbBoxPosition();
35681 this.baseScaleLevel();
35687 this.canvasLoaded = true;
35690 this.maskEl.unmask();
35695 setCanvasPosition : function()
35697 if(!this.canvasEl){
35701 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35702 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35704 this.previewEl.setLeft(pw);
35705 this.previewEl.setTop(ph);
35709 onMouseDown : function(e)
35713 this.dragable = true;
35714 this.pinching = false;
35716 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35717 this.dragable = false;
35721 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35722 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35726 onMouseMove : function(e)
35730 if(!this.canvasLoaded){
35734 if (!this.dragable){
35738 var minX = Math.ceil(this.thumbEl.getLeft(true));
35739 var minY = Math.ceil(this.thumbEl.getTop(true));
35741 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35742 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35744 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35745 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35747 x = x - this.mouseX;
35748 y = y - this.mouseY;
35750 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35751 var bgY = Math.ceil(y + this.previewEl.getTop(true));
35753 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35754 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35756 this.previewEl.setLeft(bgX);
35757 this.previewEl.setTop(bgY);
35759 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35760 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35763 onMouseUp : function(e)
35767 this.dragable = false;
35770 onMouseWheel : function(e)
35774 this.startScale = this.scale;
35776 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35778 if(!this.zoomable()){
35779 this.scale = this.startScale;
35788 zoomable : function()
35790 var minScale = this.thumbEl.getWidth() / this.minWidth;
35792 if(this.minWidth < this.minHeight){
35793 minScale = this.thumbEl.getHeight() / this.minHeight;
35796 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35797 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35801 (this.rotate == 0 || this.rotate == 180) &&
35803 width > this.imageEl.OriginWidth ||
35804 height > this.imageEl.OriginHeight ||
35805 (width < this.minWidth && height < this.minHeight)
35813 (this.rotate == 90 || this.rotate == 270) &&
35815 width > this.imageEl.OriginWidth ||
35816 height > this.imageEl.OriginHeight ||
35817 (width < this.minHeight && height < this.minWidth)
35824 !this.isDocument &&
35825 (this.rotate == 0 || this.rotate == 180) &&
35827 width < this.minWidth ||
35828 width > this.imageEl.OriginWidth ||
35829 height < this.minHeight ||
35830 height > this.imageEl.OriginHeight
35837 !this.isDocument &&
35838 (this.rotate == 90 || this.rotate == 270) &&
35840 width < this.minHeight ||
35841 width > this.imageEl.OriginWidth ||
35842 height < this.minWidth ||
35843 height > this.imageEl.OriginHeight
35853 onRotateLeft : function(e)
35855 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35857 var minScale = this.thumbEl.getWidth() / this.minWidth;
35859 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35860 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35862 this.startScale = this.scale;
35864 while (this.getScaleLevel() < minScale){
35866 this.scale = this.scale + 1;
35868 if(!this.zoomable()){
35873 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35874 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35879 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35886 this.scale = this.startScale;
35888 this.onRotateFail();
35893 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35895 if(this.isDocument){
35896 this.setThumbBoxSize();
35897 this.setThumbBoxPosition();
35898 this.setCanvasPosition();
35903 this.fireEvent('rotate', this, 'left');
35907 onRotateRight : function(e)
35909 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35911 var minScale = this.thumbEl.getWidth() / this.minWidth;
35913 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35914 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35916 this.startScale = this.scale;
35918 while (this.getScaleLevel() < minScale){
35920 this.scale = this.scale + 1;
35922 if(!this.zoomable()){
35927 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35928 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35933 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35940 this.scale = this.startScale;
35942 this.onRotateFail();
35947 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35949 if(this.isDocument){
35950 this.setThumbBoxSize();
35951 this.setThumbBoxPosition();
35952 this.setCanvasPosition();
35957 this.fireEvent('rotate', this, 'right');
35960 onRotateFail : function()
35962 this.errorEl.show(true);
35966 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35971 this.previewEl.dom.innerHTML = '';
35973 var canvasEl = document.createElement("canvas");
35975 var contextEl = canvasEl.getContext("2d");
35977 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35978 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35979 var center = this.imageEl.OriginWidth / 2;
35981 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35982 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35983 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35984 center = this.imageEl.OriginHeight / 2;
35987 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35989 contextEl.translate(center, center);
35990 contextEl.rotate(this.rotate * Math.PI / 180);
35992 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35994 this.canvasEl = document.createElement("canvas");
35996 this.contextEl = this.canvasEl.getContext("2d");
35998 switch (this.rotate) {
36001 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36002 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36004 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36009 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36010 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36012 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36013 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);
36017 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36022 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36023 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36025 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36026 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);
36030 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);
36035 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36036 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36038 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36039 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36043 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);
36050 this.previewEl.appendChild(this.canvasEl);
36052 this.setCanvasPosition();
36057 if(!this.canvasLoaded){
36061 var imageCanvas = document.createElement("canvas");
36063 var imageContext = imageCanvas.getContext("2d");
36065 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36066 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36068 var center = imageCanvas.width / 2;
36070 imageContext.translate(center, center);
36072 imageContext.rotate(this.rotate * Math.PI / 180);
36074 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36076 var canvas = document.createElement("canvas");
36078 var context = canvas.getContext("2d");
36080 canvas.width = this.minWidth;
36081 canvas.height = this.minHeight;
36083 switch (this.rotate) {
36086 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36087 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36089 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36090 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36092 var targetWidth = this.minWidth - 2 * x;
36093 var targetHeight = this.minHeight - 2 * y;
36097 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36098 scale = targetWidth / width;
36101 if(x > 0 && y == 0){
36102 scale = targetHeight / height;
36105 if(x > 0 && y > 0){
36106 scale = targetWidth / width;
36108 if(width < height){
36109 scale = targetHeight / height;
36113 context.scale(scale, scale);
36115 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36116 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36118 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36119 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36121 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36126 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36127 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36129 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36130 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36132 var targetWidth = this.minWidth - 2 * x;
36133 var targetHeight = this.minHeight - 2 * y;
36137 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36138 scale = targetWidth / width;
36141 if(x > 0 && y == 0){
36142 scale = targetHeight / height;
36145 if(x > 0 && y > 0){
36146 scale = targetWidth / width;
36148 if(width < height){
36149 scale = targetHeight / height;
36153 context.scale(scale, scale);
36155 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36156 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36158 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36159 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36161 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36163 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36168 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36169 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36171 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36172 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36174 var targetWidth = this.minWidth - 2 * x;
36175 var targetHeight = this.minHeight - 2 * y;
36179 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36180 scale = targetWidth / width;
36183 if(x > 0 && y == 0){
36184 scale = targetHeight / height;
36187 if(x > 0 && y > 0){
36188 scale = targetWidth / width;
36190 if(width < height){
36191 scale = targetHeight / height;
36195 context.scale(scale, scale);
36197 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36198 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36200 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36201 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36203 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36204 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36206 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36211 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36212 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36214 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36215 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36217 var targetWidth = this.minWidth - 2 * x;
36218 var targetHeight = this.minHeight - 2 * y;
36222 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36223 scale = targetWidth / width;
36226 if(x > 0 && y == 0){
36227 scale = targetHeight / height;
36230 if(x > 0 && y > 0){
36231 scale = targetWidth / width;
36233 if(width < height){
36234 scale = targetHeight / height;
36238 context.scale(scale, scale);
36240 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36241 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36243 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36244 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36246 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36248 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36255 this.cropData = canvas.toDataURL(this.cropType);
36257 if(this.fireEvent('crop', this, this.cropData) !== false){
36258 this.process(this.file, this.cropData);
36265 setThumbBoxSize : function()
36269 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36270 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36271 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36273 this.minWidth = width;
36274 this.minHeight = height;
36276 if(this.rotate == 90 || this.rotate == 270){
36277 this.minWidth = height;
36278 this.minHeight = width;
36283 width = Math.ceil(this.minWidth * height / this.minHeight);
36285 if(this.minWidth > this.minHeight){
36287 height = Math.ceil(this.minHeight * width / this.minWidth);
36290 this.thumbEl.setStyle({
36291 width : width + 'px',
36292 height : height + 'px'
36299 setThumbBoxPosition : function()
36301 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36302 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36304 this.thumbEl.setLeft(x);
36305 this.thumbEl.setTop(y);
36309 baseRotateLevel : function()
36311 this.baseRotate = 1;
36314 typeof(this.exif) != 'undefined' &&
36315 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36316 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36318 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36321 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36325 baseScaleLevel : function()
36329 if(this.isDocument){
36331 if(this.baseRotate == 6 || this.baseRotate == 8){
36333 height = this.thumbEl.getHeight();
36334 this.baseScale = height / this.imageEl.OriginWidth;
36336 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36337 width = this.thumbEl.getWidth();
36338 this.baseScale = width / this.imageEl.OriginHeight;
36344 height = this.thumbEl.getHeight();
36345 this.baseScale = height / this.imageEl.OriginHeight;
36347 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36348 width = this.thumbEl.getWidth();
36349 this.baseScale = width / this.imageEl.OriginWidth;
36355 if(this.baseRotate == 6 || this.baseRotate == 8){
36357 width = this.thumbEl.getHeight();
36358 this.baseScale = width / this.imageEl.OriginHeight;
36360 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36361 height = this.thumbEl.getWidth();
36362 this.baseScale = height / this.imageEl.OriginHeight;
36365 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36366 height = this.thumbEl.getWidth();
36367 this.baseScale = height / this.imageEl.OriginHeight;
36369 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36370 width = this.thumbEl.getHeight();
36371 this.baseScale = width / this.imageEl.OriginWidth;
36378 width = this.thumbEl.getWidth();
36379 this.baseScale = width / this.imageEl.OriginWidth;
36381 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36382 height = this.thumbEl.getHeight();
36383 this.baseScale = height / this.imageEl.OriginHeight;
36386 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36388 height = this.thumbEl.getHeight();
36389 this.baseScale = height / this.imageEl.OriginHeight;
36391 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36392 width = this.thumbEl.getWidth();
36393 this.baseScale = width / this.imageEl.OriginWidth;
36401 getScaleLevel : function()
36403 return this.baseScale * Math.pow(1.1, this.scale);
36406 onTouchStart : function(e)
36408 if(!this.canvasLoaded){
36409 this.beforeSelectFile(e);
36413 var touches = e.browserEvent.touches;
36419 if(touches.length == 1){
36420 this.onMouseDown(e);
36424 if(touches.length != 2){
36430 for(var i = 0, finger; finger = touches[i]; i++){
36431 coords.push(finger.pageX, finger.pageY);
36434 var x = Math.pow(coords[0] - coords[2], 2);
36435 var y = Math.pow(coords[1] - coords[3], 2);
36437 this.startDistance = Math.sqrt(x + y);
36439 this.startScale = this.scale;
36441 this.pinching = true;
36442 this.dragable = false;
36446 onTouchMove : function(e)
36448 if(!this.pinching && !this.dragable){
36452 var touches = e.browserEvent.touches;
36459 this.onMouseMove(e);
36465 for(var i = 0, finger; finger = touches[i]; i++){
36466 coords.push(finger.pageX, finger.pageY);
36469 var x = Math.pow(coords[0] - coords[2], 2);
36470 var y = Math.pow(coords[1] - coords[3], 2);
36472 this.endDistance = Math.sqrt(x + y);
36474 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36476 if(!this.zoomable()){
36477 this.scale = this.startScale;
36485 onTouchEnd : function(e)
36487 this.pinching = false;
36488 this.dragable = false;
36492 process : function(file, crop)
36495 this.maskEl.mask(this.loadingText);
36498 this.xhr = new XMLHttpRequest();
36500 file.xhr = this.xhr;
36502 this.xhr.open(this.method, this.url, true);
36505 "Accept": "application/json",
36506 "Cache-Control": "no-cache",
36507 "X-Requested-With": "XMLHttpRequest"
36510 for (var headerName in headers) {
36511 var headerValue = headers[headerName];
36513 this.xhr.setRequestHeader(headerName, headerValue);
36519 this.xhr.onload = function()
36521 _this.xhrOnLoad(_this.xhr);
36524 this.xhr.onerror = function()
36526 _this.xhrOnError(_this.xhr);
36529 var formData = new FormData();
36531 formData.append('returnHTML', 'NO');
36534 formData.append('crop', crop);
36537 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36538 formData.append(this.paramName, file, file.name);
36541 if(typeof(file.filename) != 'undefined'){
36542 formData.append('filename', file.filename);
36545 if(typeof(file.mimetype) != 'undefined'){
36546 formData.append('mimetype', file.mimetype);
36549 if(this.fireEvent('arrange', this, formData) != false){
36550 this.xhr.send(formData);
36554 xhrOnLoad : function(xhr)
36557 this.maskEl.unmask();
36560 if (xhr.readyState !== 4) {
36561 this.fireEvent('exception', this, xhr);
36565 var response = Roo.decode(xhr.responseText);
36567 if(!response.success){
36568 this.fireEvent('exception', this, xhr);
36572 var response = Roo.decode(xhr.responseText);
36574 this.fireEvent('upload', this, response);
36578 xhrOnError : function()
36581 this.maskEl.unmask();
36584 Roo.log('xhr on error');
36586 var response = Roo.decode(xhr.responseText);
36592 prepare : function(file)
36595 this.maskEl.mask(this.loadingText);
36601 if(typeof(file) === 'string'){
36602 this.loadCanvas(file);
36606 if(!file || !this.urlAPI){
36611 this.cropType = file.type;
36615 if(this.fireEvent('prepare', this, this.file) != false){
36617 var reader = new FileReader();
36619 reader.onload = function (e) {
36620 if (e.target.error) {
36621 Roo.log(e.target.error);
36625 var buffer = e.target.result,
36626 dataView = new DataView(buffer),
36628 maxOffset = dataView.byteLength - 4,
36632 if (dataView.getUint16(0) === 0xffd8) {
36633 while (offset < maxOffset) {
36634 markerBytes = dataView.getUint16(offset);
36636 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36637 markerLength = dataView.getUint16(offset + 2) + 2;
36638 if (offset + markerLength > dataView.byteLength) {
36639 Roo.log('Invalid meta data: Invalid segment size.');
36643 if(markerBytes == 0xffe1){
36644 _this.parseExifData(
36651 offset += markerLength;
36661 var url = _this.urlAPI.createObjectURL(_this.file);
36663 _this.loadCanvas(url);
36668 reader.readAsArrayBuffer(this.file);
36674 parseExifData : function(dataView, offset, length)
36676 var tiffOffset = offset + 10,
36680 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36681 // No Exif data, might be XMP data instead
36685 // Check for the ASCII code for "Exif" (0x45786966):
36686 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36687 // No Exif data, might be XMP data instead
36690 if (tiffOffset + 8 > dataView.byteLength) {
36691 Roo.log('Invalid Exif data: Invalid segment size.');
36694 // Check for the two null bytes:
36695 if (dataView.getUint16(offset + 8) !== 0x0000) {
36696 Roo.log('Invalid Exif data: Missing byte alignment offset.');
36699 // Check the byte alignment:
36700 switch (dataView.getUint16(tiffOffset)) {
36702 littleEndian = true;
36705 littleEndian = false;
36708 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36711 // Check for the TIFF tag marker (0x002A):
36712 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36713 Roo.log('Invalid Exif data: Missing TIFF marker.');
36716 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36717 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36719 this.parseExifTags(
36722 tiffOffset + dirOffset,
36727 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36732 if (dirOffset + 6 > dataView.byteLength) {
36733 Roo.log('Invalid Exif data: Invalid directory offset.');
36736 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36737 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36738 if (dirEndOffset + 4 > dataView.byteLength) {
36739 Roo.log('Invalid Exif data: Invalid directory size.');
36742 for (i = 0; i < tagsNumber; i += 1) {
36746 dirOffset + 2 + 12 * i, // tag offset
36750 // Return the offset to the next directory:
36751 return dataView.getUint32(dirEndOffset, littleEndian);
36754 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
36756 var tag = dataView.getUint16(offset, littleEndian);
36758 this.exif[tag] = this.getExifValue(
36762 dataView.getUint16(offset + 2, littleEndian), // tag type
36763 dataView.getUint32(offset + 4, littleEndian), // tag length
36768 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36770 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36779 Roo.log('Invalid Exif data: Invalid tag type.');
36783 tagSize = tagType.size * length;
36784 // Determine if the value is contained in the dataOffset bytes,
36785 // or if the value at the dataOffset is a pointer to the actual data:
36786 dataOffset = tagSize > 4 ?
36787 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36788 if (dataOffset + tagSize > dataView.byteLength) {
36789 Roo.log('Invalid Exif data: Invalid data offset.');
36792 if (length === 1) {
36793 return tagType.getValue(dataView, dataOffset, littleEndian);
36796 for (i = 0; i < length; i += 1) {
36797 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36800 if (tagType.ascii) {
36802 // Concatenate the chars:
36803 for (i = 0; i < values.length; i += 1) {
36805 // Ignore the terminating NULL byte(s):
36806 if (c === '\u0000') {
36818 Roo.apply(Roo.bootstrap.UploadCropbox, {
36820 'Orientation': 0x0112
36824 1: 0, //'top-left',
36826 3: 180, //'bottom-right',
36827 // 4: 'bottom-left',
36829 6: 90, //'right-top',
36830 // 7: 'right-bottom',
36831 8: 270 //'left-bottom'
36835 // byte, 8-bit unsigned int:
36837 getValue: function (dataView, dataOffset) {
36838 return dataView.getUint8(dataOffset);
36842 // ascii, 8-bit byte:
36844 getValue: function (dataView, dataOffset) {
36845 return String.fromCharCode(dataView.getUint8(dataOffset));
36850 // short, 16 bit int:
36852 getValue: function (dataView, dataOffset, littleEndian) {
36853 return dataView.getUint16(dataOffset, littleEndian);
36857 // long, 32 bit int:
36859 getValue: function (dataView, dataOffset, littleEndian) {
36860 return dataView.getUint32(dataOffset, littleEndian);
36864 // rational = two long values, first is numerator, second is denominator:
36866 getValue: function (dataView, dataOffset, littleEndian) {
36867 return dataView.getUint32(dataOffset, littleEndian) /
36868 dataView.getUint32(dataOffset + 4, littleEndian);
36872 // slong, 32 bit signed int:
36874 getValue: function (dataView, dataOffset, littleEndian) {
36875 return dataView.getInt32(dataOffset, littleEndian);
36879 // srational, two slongs, first is numerator, second is denominator:
36881 getValue: function (dataView, dataOffset, littleEndian) {
36882 return dataView.getInt32(dataOffset, littleEndian) /
36883 dataView.getInt32(dataOffset + 4, littleEndian);
36893 cls : 'btn-group roo-upload-cropbox-rotate-left',
36894 action : 'rotate-left',
36898 cls : 'btn btn-default',
36899 html : '<i class="fa fa-undo"></i>'
36905 cls : 'btn-group roo-upload-cropbox-picture',
36906 action : 'picture',
36910 cls : 'btn btn-default',
36911 html : '<i class="fa fa-picture-o"></i>'
36917 cls : 'btn-group roo-upload-cropbox-rotate-right',
36918 action : 'rotate-right',
36922 cls : 'btn btn-default',
36923 html : '<i class="fa fa-repeat"></i>'
36931 cls : 'btn-group roo-upload-cropbox-rotate-left',
36932 action : 'rotate-left',
36936 cls : 'btn btn-default',
36937 html : '<i class="fa fa-undo"></i>'
36943 cls : 'btn-group roo-upload-cropbox-download',
36944 action : 'download',
36948 cls : 'btn btn-default',
36949 html : '<i class="fa fa-download"></i>'
36955 cls : 'btn-group roo-upload-cropbox-crop',
36960 cls : 'btn btn-default',
36961 html : '<i class="fa fa-crop"></i>'
36967 cls : 'btn-group roo-upload-cropbox-trash',
36972 cls : 'btn btn-default',
36973 html : '<i class="fa fa-trash"></i>'
36979 cls : 'btn-group roo-upload-cropbox-rotate-right',
36980 action : 'rotate-right',
36984 cls : 'btn btn-default',
36985 html : '<i class="fa fa-repeat"></i>'
36993 cls : 'btn-group roo-upload-cropbox-rotate-left',
36994 action : 'rotate-left',
36998 cls : 'btn btn-default',
36999 html : '<i class="fa fa-undo"></i>'
37005 cls : 'btn-group roo-upload-cropbox-rotate-right',
37006 action : 'rotate-right',
37010 cls : 'btn btn-default',
37011 html : '<i class="fa fa-repeat"></i>'
37024 * @class Roo.bootstrap.DocumentManager
37025 * @extends Roo.bootstrap.Component
37026 * Bootstrap DocumentManager class
37027 * @cfg {String} paramName default 'imageUpload'
37028 * @cfg {String} toolTipName default 'filename'
37029 * @cfg {String} method default POST
37030 * @cfg {String} url action url
37031 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37032 * @cfg {Boolean} multiple multiple upload default true
37033 * @cfg {Number} thumbSize default 300
37034 * @cfg {String} fieldLabel
37035 * @cfg {Number} labelWidth default 4
37036 * @cfg {String} labelAlign (left|top) default left
37037 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37038 * @cfg {Number} labellg set the width of label (1-12)
37039 * @cfg {Number} labelmd set the width of label (1-12)
37040 * @cfg {Number} labelsm set the width of label (1-12)
37041 * @cfg {Number} labelxs set the width of label (1-12)
37044 * Create a new DocumentManager
37045 * @param {Object} config The config object
37048 Roo.bootstrap.DocumentManager = function(config){
37049 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37052 this.delegates = [];
37057 * Fire when initial the DocumentManager
37058 * @param {Roo.bootstrap.DocumentManager} this
37063 * inspect selected file
37064 * @param {Roo.bootstrap.DocumentManager} this
37065 * @param {File} file
37070 * Fire when xhr load exception
37071 * @param {Roo.bootstrap.DocumentManager} this
37072 * @param {XMLHttpRequest} xhr
37074 "exception" : true,
37076 * @event afterupload
37077 * Fire when xhr load exception
37078 * @param {Roo.bootstrap.DocumentManager} this
37079 * @param {XMLHttpRequest} xhr
37081 "afterupload" : true,
37084 * prepare the form data
37085 * @param {Roo.bootstrap.DocumentManager} this
37086 * @param {Object} formData
37091 * Fire when remove the file
37092 * @param {Roo.bootstrap.DocumentManager} this
37093 * @param {Object} file
37098 * Fire after refresh the file
37099 * @param {Roo.bootstrap.DocumentManager} this
37104 * Fire after click the image
37105 * @param {Roo.bootstrap.DocumentManager} this
37106 * @param {Object} file
37111 * Fire when upload a image and editable set to true
37112 * @param {Roo.bootstrap.DocumentManager} this
37113 * @param {Object} file
37117 * @event beforeselectfile
37118 * Fire before select file
37119 * @param {Roo.bootstrap.DocumentManager} this
37121 "beforeselectfile" : true,
37124 * Fire before process file
37125 * @param {Roo.bootstrap.DocumentManager} this
37126 * @param {Object} file
37130 * @event previewrendered
37131 * Fire when preview rendered
37132 * @param {Roo.bootstrap.DocumentManager} this
37133 * @param {Object} file
37135 "previewrendered" : true,
37138 "previewResize" : true
37143 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37152 paramName : 'imageUpload',
37153 toolTipName : 'filename',
37156 labelAlign : 'left',
37166 getAutoCreate : function()
37168 var managerWidget = {
37170 cls : 'roo-document-manager',
37174 cls : 'roo-document-manager-selector',
37179 cls : 'roo-document-manager-uploader',
37183 cls : 'roo-document-manager-upload-btn',
37184 html : '<i class="fa fa-plus"></i>'
37195 cls : 'column col-md-12',
37200 if(this.fieldLabel.length){
37205 cls : 'column col-md-12',
37206 html : this.fieldLabel
37210 cls : 'column col-md-12',
37215 if(this.labelAlign == 'left'){
37220 html : this.fieldLabel
37229 if(this.labelWidth > 12){
37230 content[0].style = "width: " + this.labelWidth + 'px';
37233 if(this.labelWidth < 13 && this.labelmd == 0){
37234 this.labelmd = this.labelWidth;
37237 if(this.labellg > 0){
37238 content[0].cls += ' col-lg-' + this.labellg;
37239 content[1].cls += ' col-lg-' + (12 - this.labellg);
37242 if(this.labelmd > 0){
37243 content[0].cls += ' col-md-' + this.labelmd;
37244 content[1].cls += ' col-md-' + (12 - this.labelmd);
37247 if(this.labelsm > 0){
37248 content[0].cls += ' col-sm-' + this.labelsm;
37249 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37252 if(this.labelxs > 0){
37253 content[0].cls += ' col-xs-' + this.labelxs;
37254 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37262 cls : 'row clearfix',
37270 initEvents : function()
37272 this.managerEl = this.el.select('.roo-document-manager', true).first();
37273 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37275 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37276 this.selectorEl.hide();
37279 this.selectorEl.attr('multiple', 'multiple');
37282 this.selectorEl.on('change', this.onFileSelected, this);
37284 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37285 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37287 this.uploader.on('click', this.onUploaderClick, this);
37289 this.renderProgressDialog();
37293 window.addEventListener("resize", function() { _this.refresh(); } );
37295 this.fireEvent('initial', this);
37298 renderProgressDialog : function()
37302 this.progressDialog = new Roo.bootstrap.Modal({
37303 cls : 'roo-document-manager-progress-dialog',
37304 allow_close : false,
37315 btnclick : function() {
37316 _this.uploadCancel();
37322 this.progressDialog.render(Roo.get(document.body));
37324 this.progress = new Roo.bootstrap.Progress({
37325 cls : 'roo-document-manager-progress',
37330 this.progress.render(this.progressDialog.getChildContainer());
37332 this.progressBar = new Roo.bootstrap.ProgressBar({
37333 cls : 'roo-document-manager-progress-bar',
37336 aria_valuemax : 12,
37340 this.progressBar.render(this.progress.getChildContainer());
37343 onUploaderClick : function(e)
37345 e.preventDefault();
37347 if(this.fireEvent('beforeselectfile', this) != false){
37348 this.selectorEl.dom.click();
37353 onFileSelected : function(e)
37355 e.preventDefault();
37357 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37361 Roo.each(this.selectorEl.dom.files, function(file){
37362 if(this.fireEvent('inspect', this, file) != false){
37363 this.files.push(file);
37373 this.selectorEl.dom.value = '';
37375 if(!this.files || !this.files.length){
37379 if(this.boxes > 0 && this.files.length > this.boxes){
37380 this.files = this.files.slice(0, this.boxes);
37383 this.uploader.show();
37385 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37386 this.uploader.hide();
37395 Roo.each(this.files, function(file){
37397 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37398 var f = this.renderPreview(file);
37403 if(file.type.indexOf('image') != -1){
37404 this.delegates.push(
37406 _this.process(file);
37407 }).createDelegate(this)
37415 _this.process(file);
37416 }).createDelegate(this)
37421 this.files = files;
37423 this.delegates = this.delegates.concat(docs);
37425 if(!this.delegates.length){
37430 this.progressBar.aria_valuemax = this.delegates.length;
37437 arrange : function()
37439 if(!this.delegates.length){
37440 this.progressDialog.hide();
37445 var delegate = this.delegates.shift();
37447 this.progressDialog.show();
37449 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37451 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37456 refresh : function()
37458 this.uploader.show();
37460 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37461 this.uploader.hide();
37464 Roo.isTouch ? this.closable(false) : this.closable(true);
37466 this.fireEvent('refresh', this);
37469 onRemove : function(e, el, o)
37471 e.preventDefault();
37473 this.fireEvent('remove', this, o);
37477 remove : function(o)
37481 Roo.each(this.files, function(file){
37482 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37491 this.files = files;
37498 Roo.each(this.files, function(file){
37503 file.target.remove();
37512 onClick : function(e, el, o)
37514 e.preventDefault();
37516 this.fireEvent('click', this, o);
37520 closable : function(closable)
37522 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37524 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37536 xhrOnLoad : function(xhr)
37538 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37542 if (xhr.readyState !== 4) {
37544 this.fireEvent('exception', this, xhr);
37548 var response = Roo.decode(xhr.responseText);
37550 if(!response.success){
37552 this.fireEvent('exception', this, xhr);
37556 var file = this.renderPreview(response.data);
37558 this.files.push(file);
37562 this.fireEvent('afterupload', this, xhr);
37566 xhrOnError : function(xhr)
37568 Roo.log('xhr on error');
37570 var response = Roo.decode(xhr.responseText);
37577 process : function(file)
37579 if(this.fireEvent('process', this, file) !== false){
37580 if(this.editable && file.type.indexOf('image') != -1){
37581 this.fireEvent('edit', this, file);
37585 this.uploadStart(file, false);
37592 uploadStart : function(file, crop)
37594 this.xhr = new XMLHttpRequest();
37596 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37601 file.xhr = this.xhr;
37603 this.managerEl.createChild({
37605 cls : 'roo-document-manager-loading',
37609 tooltip : file.name,
37610 cls : 'roo-document-manager-thumb',
37611 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37617 this.xhr.open(this.method, this.url, true);
37620 "Accept": "application/json",
37621 "Cache-Control": "no-cache",
37622 "X-Requested-With": "XMLHttpRequest"
37625 for (var headerName in headers) {
37626 var headerValue = headers[headerName];
37628 this.xhr.setRequestHeader(headerName, headerValue);
37634 this.xhr.onload = function()
37636 _this.xhrOnLoad(_this.xhr);
37639 this.xhr.onerror = function()
37641 _this.xhrOnError(_this.xhr);
37644 var formData = new FormData();
37646 formData.append('returnHTML', 'NO');
37649 formData.append('crop', crop);
37652 formData.append(this.paramName, file, file.name);
37659 if(this.fireEvent('prepare', this, formData, options) != false){
37661 if(options.manually){
37665 this.xhr.send(formData);
37669 this.uploadCancel();
37672 uploadCancel : function()
37678 this.delegates = [];
37680 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37687 renderPreview : function(file)
37689 if(typeof(file.target) != 'undefined' && file.target){
37693 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37695 var previewEl = this.managerEl.createChild({
37697 cls : 'roo-document-manager-preview',
37701 tooltip : file[this.toolTipName],
37702 cls : 'roo-document-manager-thumb',
37703 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37708 html : '<i class="fa fa-times-circle"></i>'
37713 var close = previewEl.select('button.close', true).first();
37715 close.on('click', this.onRemove, this, file);
37717 file.target = previewEl;
37719 var image = previewEl.select('img', true).first();
37723 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37725 image.on('click', this.onClick, this, file);
37727 this.fireEvent('previewrendered', this, file);
37733 onPreviewLoad : function(file, image)
37735 if(typeof(file.target) == 'undefined' || !file.target){
37739 var width = image.dom.naturalWidth || image.dom.width;
37740 var height = image.dom.naturalHeight || image.dom.height;
37742 if(!this.previewResize) {
37746 if(width > height){
37747 file.target.addClass('wide');
37751 file.target.addClass('tall');
37756 uploadFromSource : function(file, crop)
37758 this.xhr = new XMLHttpRequest();
37760 this.managerEl.createChild({
37762 cls : 'roo-document-manager-loading',
37766 tooltip : file.name,
37767 cls : 'roo-document-manager-thumb',
37768 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37774 this.xhr.open(this.method, this.url, true);
37777 "Accept": "application/json",
37778 "Cache-Control": "no-cache",
37779 "X-Requested-With": "XMLHttpRequest"
37782 for (var headerName in headers) {
37783 var headerValue = headers[headerName];
37785 this.xhr.setRequestHeader(headerName, headerValue);
37791 this.xhr.onload = function()
37793 _this.xhrOnLoad(_this.xhr);
37796 this.xhr.onerror = function()
37798 _this.xhrOnError(_this.xhr);
37801 var formData = new FormData();
37803 formData.append('returnHTML', 'NO');
37805 formData.append('crop', crop);
37807 if(typeof(file.filename) != 'undefined'){
37808 formData.append('filename', file.filename);
37811 if(typeof(file.mimetype) != 'undefined'){
37812 formData.append('mimetype', file.mimetype);
37817 if(this.fireEvent('prepare', this, formData) != false){
37818 this.xhr.send(formData);
37828 * @class Roo.bootstrap.DocumentViewer
37829 * @extends Roo.bootstrap.Component
37830 * Bootstrap DocumentViewer class
37831 * @cfg {Boolean} showDownload (true|false) show download button (default true)
37832 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37835 * Create a new DocumentViewer
37836 * @param {Object} config The config object
37839 Roo.bootstrap.DocumentViewer = function(config){
37840 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37845 * Fire after initEvent
37846 * @param {Roo.bootstrap.DocumentViewer} this
37852 * @param {Roo.bootstrap.DocumentViewer} this
37857 * Fire after download button
37858 * @param {Roo.bootstrap.DocumentViewer} this
37863 * Fire after trash button
37864 * @param {Roo.bootstrap.DocumentViewer} this
37871 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
37873 showDownload : true,
37877 getAutoCreate : function()
37881 cls : 'roo-document-viewer',
37885 cls : 'roo-document-viewer-body',
37889 cls : 'roo-document-viewer-thumb',
37893 cls : 'roo-document-viewer-image'
37901 cls : 'roo-document-viewer-footer',
37904 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37908 cls : 'btn-group roo-document-viewer-download',
37912 cls : 'btn btn-default',
37913 html : '<i class="fa fa-download"></i>'
37919 cls : 'btn-group roo-document-viewer-trash',
37923 cls : 'btn btn-default',
37924 html : '<i class="fa fa-trash"></i>'
37937 initEvents : function()
37939 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37940 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37942 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37943 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37945 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37946 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37948 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37949 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37951 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37952 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37954 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37955 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37957 this.bodyEl.on('click', this.onClick, this);
37958 this.downloadBtn.on('click', this.onDownload, this);
37959 this.trashBtn.on('click', this.onTrash, this);
37961 this.downloadBtn.hide();
37962 this.trashBtn.hide();
37964 if(this.showDownload){
37965 this.downloadBtn.show();
37968 if(this.showTrash){
37969 this.trashBtn.show();
37972 if(!this.showDownload && !this.showTrash) {
37973 this.footerEl.hide();
37978 initial : function()
37980 this.fireEvent('initial', this);
37984 onClick : function(e)
37986 e.preventDefault();
37988 this.fireEvent('click', this);
37991 onDownload : function(e)
37993 e.preventDefault();
37995 this.fireEvent('download', this);
37998 onTrash : function(e)
38000 e.preventDefault();
38002 this.fireEvent('trash', this);
38014 * @class Roo.bootstrap.form.FieldLabel
38015 * @extends Roo.bootstrap.Component
38016 * Bootstrap FieldLabel class
38017 * @cfg {String} html contents of the element
38018 * @cfg {String} tag tag of the element default label
38019 * @cfg {String} cls class of the element
38020 * @cfg {String} target label target
38021 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38022 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38023 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38024 * @cfg {String} iconTooltip default "This field is required"
38025 * @cfg {String} indicatorpos (left|right) default left
38028 * Create a new FieldLabel
38029 * @param {Object} config The config object
38032 Roo.bootstrap.form.FieldLabel = function(config){
38033 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38038 * Fires after the field has been marked as invalid.
38039 * @param {Roo.form.FieldLabel} this
38040 * @param {String} msg The validation message
38045 * Fires after the field has been validated with no errors.
38046 * @param {Roo.form.FieldLabel} this
38052 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38059 invalidClass : 'has-warning',
38060 validClass : 'has-success',
38061 iconTooltip : 'This field is required',
38062 indicatorpos : 'left',
38064 getAutoCreate : function(){
38067 if (!this.allowBlank) {
38073 cls : 'roo-bootstrap-field-label ' + this.cls,
38078 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38079 tooltip : this.iconTooltip
38088 if(this.indicatorpos == 'right'){
38091 cls : 'roo-bootstrap-field-label ' + this.cls,
38100 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38101 tooltip : this.iconTooltip
38110 initEvents: function()
38112 Roo.bootstrap.Element.superclass.initEvents.call(this);
38114 this.indicator = this.indicatorEl();
38116 if(this.indicator){
38117 this.indicator.removeClass('visible');
38118 this.indicator.addClass('invisible');
38121 Roo.bootstrap.form.FieldLabel.register(this);
38124 indicatorEl : function()
38126 var indicator = this.el.select('i.roo-required-indicator',true).first();
38137 * Mark this field as valid
38139 markValid : function()
38141 if(this.indicator){
38142 this.indicator.removeClass('visible');
38143 this.indicator.addClass('invisible');
38145 if (Roo.bootstrap.version == 3) {
38146 this.el.removeClass(this.invalidClass);
38147 this.el.addClass(this.validClass);
38149 this.el.removeClass('is-invalid');
38150 this.el.addClass('is-valid');
38154 this.fireEvent('valid', this);
38158 * Mark this field as invalid
38159 * @param {String} msg The validation message
38161 markInvalid : function(msg)
38163 if(this.indicator){
38164 this.indicator.removeClass('invisible');
38165 this.indicator.addClass('visible');
38167 if (Roo.bootstrap.version == 3) {
38168 this.el.removeClass(this.validClass);
38169 this.el.addClass(this.invalidClass);
38171 this.el.removeClass('is-valid');
38172 this.el.addClass('is-invalid');
38176 this.fireEvent('invalid', this, msg);
38182 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38187 * register a FieldLabel Group
38188 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38190 register : function(label)
38192 if(this.groups.hasOwnProperty(label.target)){
38196 this.groups[label.target] = label;
38200 * fetch a FieldLabel Group based on the target
38201 * @param {string} target
38202 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38204 get: function(target) {
38205 if (typeof(this.groups[target]) == 'undefined') {
38209 return this.groups[target] ;
38218 * page DateSplitField.
38224 * @class Roo.bootstrap.form.DateSplitField
38225 * @extends Roo.bootstrap.Component
38226 * Bootstrap DateSplitField class
38227 * @cfg {string} fieldLabel - the label associated
38228 * @cfg {Number} labelWidth set the width of label (0-12)
38229 * @cfg {String} labelAlign (top|left)
38230 * @cfg {Boolean} dayAllowBlank (true|false) default false
38231 * @cfg {Boolean} monthAllowBlank (true|false) default false
38232 * @cfg {Boolean} yearAllowBlank (true|false) default false
38233 * @cfg {string} dayPlaceholder
38234 * @cfg {string} monthPlaceholder
38235 * @cfg {string} yearPlaceholder
38236 * @cfg {string} dayFormat default 'd'
38237 * @cfg {string} monthFormat default 'm'
38238 * @cfg {string} yearFormat default 'Y'
38239 * @cfg {Number} labellg set the width of label (1-12)
38240 * @cfg {Number} labelmd set the width of label (1-12)
38241 * @cfg {Number} labelsm set the width of label (1-12)
38242 * @cfg {Number} labelxs set the width of label (1-12)
38246 * Create a new DateSplitField
38247 * @param {Object} config The config object
38250 Roo.bootstrap.form.DateSplitField = function(config){
38251 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38257 * getting the data of years
38258 * @param {Roo.bootstrap.form.DateSplitField} this
38259 * @param {Object} years
38264 * getting the data of days
38265 * @param {Roo.bootstrap.form.DateSplitField} this
38266 * @param {Object} days
38271 * Fires after the field has been marked as invalid.
38272 * @param {Roo.form.Field} this
38273 * @param {String} msg The validation message
38278 * Fires after the field has been validated with no errors.
38279 * @param {Roo.form.Field} this
38285 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38288 labelAlign : 'top',
38290 dayAllowBlank : false,
38291 monthAllowBlank : false,
38292 yearAllowBlank : false,
38293 dayPlaceholder : '',
38294 monthPlaceholder : '',
38295 yearPlaceholder : '',
38299 isFormField : true,
38305 getAutoCreate : function()
38309 cls : 'row roo-date-split-field-group',
38314 cls : 'form-hidden-field roo-date-split-field-group-value',
38320 var labelCls = 'col-md-12';
38321 var contentCls = 'col-md-4';
38323 if(this.fieldLabel){
38327 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38331 html : this.fieldLabel
38336 if(this.labelAlign == 'left'){
38338 if(this.labelWidth > 12){
38339 label.style = "width: " + this.labelWidth + 'px';
38342 if(this.labelWidth < 13 && this.labelmd == 0){
38343 this.labelmd = this.labelWidth;
38346 if(this.labellg > 0){
38347 labelCls = ' col-lg-' + this.labellg;
38348 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38351 if(this.labelmd > 0){
38352 labelCls = ' col-md-' + this.labelmd;
38353 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38356 if(this.labelsm > 0){
38357 labelCls = ' col-sm-' + this.labelsm;
38358 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38361 if(this.labelxs > 0){
38362 labelCls = ' col-xs-' + this.labelxs;
38363 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38367 label.cls += ' ' + labelCls;
38369 cfg.cn.push(label);
38372 Roo.each(['day', 'month', 'year'], function(t){
38375 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38382 inputEl: function ()
38384 return this.el.select('.roo-date-split-field-group-value', true).first();
38387 onRender : function(ct, position)
38391 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38393 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38395 this.dayField = new Roo.bootstrap.form.ComboBox({
38396 allowBlank : this.dayAllowBlank,
38397 alwaysQuery : true,
38398 displayField : 'value',
38401 forceSelection : true,
38403 placeholder : this.dayPlaceholder,
38404 selectOnFocus : true,
38405 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38406 triggerAction : 'all',
38408 valueField : 'value',
38409 store : new Roo.data.SimpleStore({
38410 data : (function() {
38412 _this.fireEvent('days', _this, days);
38415 fields : [ 'value' ]
38418 select : function (_self, record, index)
38420 _this.setValue(_this.getValue());
38425 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38427 this.monthField = new Roo.bootstrap.form.MonthField({
38428 after : '<i class=\"fa fa-calendar\"></i>',
38429 allowBlank : this.monthAllowBlank,
38430 placeholder : this.monthPlaceholder,
38433 render : function (_self)
38435 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38436 e.preventDefault();
38440 select : function (_self, oldvalue, newvalue)
38442 _this.setValue(_this.getValue());
38447 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38449 this.yearField = new Roo.bootstrap.form.ComboBox({
38450 allowBlank : this.yearAllowBlank,
38451 alwaysQuery : true,
38452 displayField : 'value',
38455 forceSelection : true,
38457 placeholder : this.yearPlaceholder,
38458 selectOnFocus : true,
38459 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38460 triggerAction : 'all',
38462 valueField : 'value',
38463 store : new Roo.data.SimpleStore({
38464 data : (function() {
38466 _this.fireEvent('years', _this, years);
38469 fields : [ 'value' ]
38472 select : function (_self, record, index)
38474 _this.setValue(_this.getValue());
38479 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38482 setValue : function(v, format)
38484 this.inputEl.dom.value = v;
38486 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38488 var d = Date.parseDate(v, f);
38495 this.setDay(d.format(this.dayFormat));
38496 this.setMonth(d.format(this.monthFormat));
38497 this.setYear(d.format(this.yearFormat));
38504 setDay : function(v)
38506 this.dayField.setValue(v);
38507 this.inputEl.dom.value = this.getValue();
38512 setMonth : function(v)
38514 this.monthField.setValue(v, true);
38515 this.inputEl.dom.value = this.getValue();
38520 setYear : function(v)
38522 this.yearField.setValue(v);
38523 this.inputEl.dom.value = this.getValue();
38528 getDay : function()
38530 return this.dayField.getValue();
38533 getMonth : function()
38535 return this.monthField.getValue();
38538 getYear : function()
38540 return this.yearField.getValue();
38543 getValue : function()
38545 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38547 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38557 this.inputEl.dom.value = '';
38562 validate : function()
38564 var d = this.dayField.validate();
38565 var m = this.monthField.validate();
38566 var y = this.yearField.validate();
38571 (!this.dayAllowBlank && !d) ||
38572 (!this.monthAllowBlank && !m) ||
38573 (!this.yearAllowBlank && !y)
38578 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38587 this.markInvalid();
38592 markValid : function()
38595 var label = this.el.select('label', true).first();
38596 var icon = this.el.select('i.fa-star', true).first();
38602 this.fireEvent('valid', this);
38606 * Mark this field as invalid
38607 * @param {String} msg The validation message
38609 markInvalid : function(msg)
38612 var label = this.el.select('label', true).first();
38613 var icon = this.el.select('i.fa-star', true).first();
38615 if(label && !icon){
38616 this.el.select('.roo-date-split-field-label', true).createChild({
38618 cls : 'text-danger fa fa-lg fa-star',
38619 tooltip : 'This field is required',
38620 style : 'margin-right:5px;'
38624 this.fireEvent('invalid', this, msg);
38627 clearInvalid : function()
38629 var label = this.el.select('label', true).first();
38630 var icon = this.el.select('i.fa-star', true).first();
38636 this.fireEvent('valid', this);
38639 getName: function()
38649 * @class Roo.bootstrap.LayoutMasonry
38650 * @extends Roo.bootstrap.Component
38651 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38652 * Bootstrap Layout Masonry class
38655 * http://masonry.desandro.com
38657 * The idea is to render all the bricks based on vertical width...
38659 * The original code extends 'outlayer' - we might need to use that....
38662 * Create a new Element
38663 * @param {Object} config The config object
38666 Roo.bootstrap.LayoutMasonry = function(config){
38668 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38672 Roo.bootstrap.LayoutMasonry.register(this);
38678 * Fire after layout the items
38679 * @param {Roo.bootstrap.LayoutMasonry} this
38680 * @param {Roo.EventObject} e
38687 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
38690 * @cfg {Boolean} isLayoutInstant = no animation?
38692 isLayoutInstant : false, // needed?
38695 * @cfg {Number} boxWidth width of the columns
38700 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
38705 * @cfg {Number} padWidth padding below box..
38710 * @cfg {Number} gutter gutter width..
38715 * @cfg {Number} maxCols maximum number of columns
38721 * @cfg {Boolean} isAutoInitial defalut true
38723 isAutoInitial : true,
38728 * @cfg {Boolean} isHorizontal defalut false
38730 isHorizontal : false,
38732 currentSize : null,
38738 bricks: null, //CompositeElement
38742 _isLayoutInited : false,
38744 // isAlternative : false, // only use for vertical layout...
38747 * @cfg {Number} alternativePadWidth padding below box..
38749 alternativePadWidth : 50,
38751 selectedBrick : [],
38753 getAutoCreate : function(){
38755 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38759 cls: 'blog-masonary-wrapper ' + this.cls,
38761 cls : 'mas-boxes masonary'
38768 getChildContainer: function( )
38770 if (this.boxesEl) {
38771 return this.boxesEl;
38774 this.boxesEl = this.el.select('.mas-boxes').first();
38776 return this.boxesEl;
38780 initEvents : function()
38784 if(this.isAutoInitial){
38785 Roo.log('hook children rendered');
38786 this.on('childrenrendered', function() {
38787 Roo.log('children rendered');
38793 initial : function()
38795 this.selectedBrick = [];
38797 this.currentSize = this.el.getBox(true);
38799 Roo.EventManager.onWindowResize(this.resize, this);
38801 if(!this.isAutoInitial){
38809 //this.layout.defer(500,this);
38813 resize : function()
38815 var cs = this.el.getBox(true);
38818 this.currentSize.width == cs.width &&
38819 this.currentSize.x == cs.x &&
38820 this.currentSize.height == cs.height &&
38821 this.currentSize.y == cs.y
38823 Roo.log("no change in with or X or Y");
38827 this.currentSize = cs;
38833 layout : function()
38835 this._resetLayout();
38837 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38839 this.layoutItems( isInstant );
38841 this._isLayoutInited = true;
38843 this.fireEvent('layout', this);
38847 _resetLayout : function()
38849 if(this.isHorizontal){
38850 this.horizontalMeasureColumns();
38854 this.verticalMeasureColumns();
38858 verticalMeasureColumns : function()
38860 this.getContainerWidth();
38862 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38863 // this.colWidth = Math.floor(this.containerWidth * 0.8);
38867 var boxWidth = this.boxWidth + this.padWidth;
38869 if(this.containerWidth < this.boxWidth){
38870 boxWidth = this.containerWidth
38873 var containerWidth = this.containerWidth;
38875 var cols = Math.floor(containerWidth / boxWidth);
38877 this.cols = Math.max( cols, 1 );
38879 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38881 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38883 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38885 this.colWidth = boxWidth + avail - this.padWidth;
38887 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38888 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
38891 horizontalMeasureColumns : function()
38893 this.getContainerWidth();
38895 var boxWidth = this.boxWidth;
38897 if(this.containerWidth < boxWidth){
38898 boxWidth = this.containerWidth;
38901 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38903 this.el.setHeight(boxWidth);
38907 getContainerWidth : function()
38909 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38912 layoutItems : function( isInstant )
38914 Roo.log(this.bricks);
38916 var items = Roo.apply([], this.bricks);
38918 if(this.isHorizontal){
38919 this._horizontalLayoutItems( items , isInstant );
38923 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38924 // this._verticalAlternativeLayoutItems( items , isInstant );
38928 this._verticalLayoutItems( items , isInstant );
38932 _verticalLayoutItems : function ( items , isInstant)
38934 if ( !items || !items.length ) {
38939 ['xs', 'xs', 'xs', 'tall'],
38940 ['xs', 'xs', 'tall'],
38941 ['xs', 'xs', 'sm'],
38942 ['xs', 'xs', 'xs'],
38948 ['sm', 'xs', 'xs'],
38952 ['tall', 'xs', 'xs', 'xs'],
38953 ['tall', 'xs', 'xs'],
38965 Roo.each(items, function(item, k){
38967 switch (item.size) {
38968 // these layouts take up a full box,
38979 boxes.push([item]);
39002 var filterPattern = function(box, length)
39010 var pattern = box.slice(0, length);
39014 Roo.each(pattern, function(i){
39015 format.push(i.size);
39018 Roo.each(standard, function(s){
39020 if(String(s) != String(format)){
39029 if(!match && length == 1){
39034 filterPattern(box, length - 1);
39038 queue.push(pattern);
39040 box = box.slice(length, box.length);
39042 filterPattern(box, 4);
39048 Roo.each(boxes, function(box, k){
39054 if(box.length == 1){
39059 filterPattern(box, 4);
39063 this._processVerticalLayoutQueue( queue, isInstant );
39067 // _verticalAlternativeLayoutItems : function( items , isInstant )
39069 // if ( !items || !items.length ) {
39073 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39077 _horizontalLayoutItems : function ( items , isInstant)
39079 if ( !items || !items.length || items.length < 3) {
39085 var eItems = items.slice(0, 3);
39087 items = items.slice(3, items.length);
39090 ['xs', 'xs', 'xs', 'wide'],
39091 ['xs', 'xs', 'wide'],
39092 ['xs', 'xs', 'sm'],
39093 ['xs', 'xs', 'xs'],
39099 ['sm', 'xs', 'xs'],
39103 ['wide', 'xs', 'xs', 'xs'],
39104 ['wide', 'xs', 'xs'],
39117 Roo.each(items, function(item, k){
39119 switch (item.size) {
39130 boxes.push([item]);
39154 var filterPattern = function(box, length)
39162 var pattern = box.slice(0, length);
39166 Roo.each(pattern, function(i){
39167 format.push(i.size);
39170 Roo.each(standard, function(s){
39172 if(String(s) != String(format)){
39181 if(!match && length == 1){
39186 filterPattern(box, length - 1);
39190 queue.push(pattern);
39192 box = box.slice(length, box.length);
39194 filterPattern(box, 4);
39200 Roo.each(boxes, function(box, k){
39206 if(box.length == 1){
39211 filterPattern(box, 4);
39218 var pos = this.el.getBox(true);
39222 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39224 var hit_end = false;
39226 Roo.each(queue, function(box){
39230 Roo.each(box, function(b){
39232 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39242 Roo.each(box, function(b){
39244 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39247 mx = Math.max(mx, b.x);
39251 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39255 Roo.each(box, function(b){
39257 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39271 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39274 /** Sets position of item in DOM
39275 * @param {Element} item
39276 * @param {Number} x - horizontal position
39277 * @param {Number} y - vertical position
39278 * @param {Boolean} isInstant - disables transitions
39280 _processVerticalLayoutQueue : function( queue, isInstant )
39282 var pos = this.el.getBox(true);
39287 for (var i = 0; i < this.cols; i++){
39291 Roo.each(queue, function(box, k){
39293 var col = k % this.cols;
39295 Roo.each(box, function(b,kk){
39297 b.el.position('absolute');
39299 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39300 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39302 if(b.size == 'md-left' || b.size == 'md-right'){
39303 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39304 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39307 b.el.setWidth(width);
39308 b.el.setHeight(height);
39310 b.el.select('iframe',true).setSize(width,height);
39314 for (var i = 0; i < this.cols; i++){
39316 if(maxY[i] < maxY[col]){
39321 col = Math.min(col, i);
39325 x = pos.x + col * (this.colWidth + this.padWidth);
39329 var positions = [];
39331 switch (box.length){
39333 positions = this.getVerticalOneBoxColPositions(x, y, box);
39336 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39339 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39342 positions = this.getVerticalFourBoxColPositions(x, y, box);
39348 Roo.each(box, function(b,kk){
39350 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39352 var sz = b.el.getSize();
39354 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39362 for (var i = 0; i < this.cols; i++){
39363 mY = Math.max(mY, maxY[i]);
39366 this.el.setHeight(mY - pos.y);
39370 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39372 // var pos = this.el.getBox(true);
39375 // var maxX = pos.right;
39377 // var maxHeight = 0;
39379 // Roo.each(items, function(item, k){
39383 // item.el.position('absolute');
39385 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39387 // item.el.setWidth(width);
39389 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39391 // item.el.setHeight(height);
39394 // item.el.setXY([x, y], isInstant ? false : true);
39396 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39399 // y = y + height + this.alternativePadWidth;
39401 // maxHeight = maxHeight + height + this.alternativePadWidth;
39405 // this.el.setHeight(maxHeight);
39409 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39411 var pos = this.el.getBox(true);
39416 var maxX = pos.right;
39418 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39420 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39422 Roo.each(queue, function(box, k){
39424 Roo.each(box, function(b, kk){
39426 b.el.position('absolute');
39428 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39429 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39431 if(b.size == 'md-left' || b.size == 'md-right'){
39432 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39433 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39436 b.el.setWidth(width);
39437 b.el.setHeight(height);
39445 var positions = [];
39447 switch (box.length){
39449 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39452 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39455 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39458 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39464 Roo.each(box, function(b,kk){
39466 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39468 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39476 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39478 Roo.each(eItems, function(b,k){
39480 b.size = (k == 0) ? 'sm' : 'xs';
39481 b.x = (k == 0) ? 2 : 1;
39482 b.y = (k == 0) ? 2 : 1;
39484 b.el.position('absolute');
39486 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39488 b.el.setWidth(width);
39490 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39492 b.el.setHeight(height);
39496 var positions = [];
39499 x : maxX - this.unitWidth * 2 - this.gutter,
39504 x : maxX - this.unitWidth,
39505 y : minY + (this.unitWidth + this.gutter) * 2
39509 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39513 Roo.each(eItems, function(b,k){
39515 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39521 getVerticalOneBoxColPositions : function(x, y, box)
39525 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39527 if(box[0].size == 'md-left'){
39531 if(box[0].size == 'md-right'){
39536 x : x + (this.unitWidth + this.gutter) * rand,
39543 getVerticalTwoBoxColPositions : function(x, y, box)
39547 if(box[0].size == 'xs'){
39551 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39555 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39569 x : x + (this.unitWidth + this.gutter) * 2,
39570 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39577 getVerticalThreeBoxColPositions : function(x, y, box)
39581 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39589 x : x + (this.unitWidth + this.gutter) * 1,
39594 x : x + (this.unitWidth + this.gutter) * 2,
39602 if(box[0].size == 'xs' && box[1].size == 'xs'){
39611 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39615 x : x + (this.unitWidth + this.gutter) * 1,
39629 x : x + (this.unitWidth + this.gutter) * 2,
39634 x : x + (this.unitWidth + this.gutter) * 2,
39635 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39642 getVerticalFourBoxColPositions : function(x, y, box)
39646 if(box[0].size == 'xs'){
39655 y : y + (this.unitHeight + this.gutter) * 1
39660 y : y + (this.unitHeight + this.gutter) * 2
39664 x : x + (this.unitWidth + this.gutter) * 1,
39678 x : x + (this.unitWidth + this.gutter) * 2,
39683 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39684 y : y + (this.unitHeight + this.gutter) * 1
39688 x : x + (this.unitWidth + this.gutter) * 2,
39689 y : y + (this.unitWidth + this.gutter) * 2
39696 getHorizontalOneBoxColPositions : function(maxX, minY, box)
39700 if(box[0].size == 'md-left'){
39702 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39709 if(box[0].size == 'md-right'){
39711 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39712 y : minY + (this.unitWidth + this.gutter) * 1
39718 var rand = Math.floor(Math.random() * (4 - box[0].y));
39721 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39722 y : minY + (this.unitWidth + this.gutter) * rand
39729 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39733 if(box[0].size == 'xs'){
39736 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39741 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39742 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39750 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39755 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39756 y : minY + (this.unitWidth + this.gutter) * 2
39763 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39767 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39770 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39775 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39776 y : minY + (this.unitWidth + this.gutter) * 1
39780 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39781 y : minY + (this.unitWidth + this.gutter) * 2
39788 if(box[0].size == 'xs' && box[1].size == 'xs'){
39791 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39796 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39801 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39802 y : minY + (this.unitWidth + this.gutter) * 1
39810 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39815 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39816 y : minY + (this.unitWidth + this.gutter) * 2
39820 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39821 y : minY + (this.unitWidth + this.gutter) * 2
39828 getHorizontalFourBoxColPositions : function(maxX, minY, box)
39832 if(box[0].size == 'xs'){
39835 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39840 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39845 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),
39850 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39851 y : minY + (this.unitWidth + this.gutter) * 1
39859 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39864 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39865 y : minY + (this.unitWidth + this.gutter) * 2
39869 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39870 y : minY + (this.unitWidth + this.gutter) * 2
39874 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),
39875 y : minY + (this.unitWidth + this.gutter) * 2
39883 * remove a Masonry Brick
39884 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39886 removeBrick : function(brick_id)
39892 for (var i = 0; i<this.bricks.length; i++) {
39893 if (this.bricks[i].id == brick_id) {
39894 this.bricks.splice(i,1);
39895 this.el.dom.removeChild(Roo.get(brick_id).dom);
39902 * adds a Masonry Brick
39903 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39905 addBrick : function(cfg)
39907 var cn = new Roo.bootstrap.MasonryBrick(cfg);
39908 //this.register(cn);
39909 cn.parentId = this.id;
39910 cn.render(this.el);
39915 * register a Masonry Brick
39916 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39919 register : function(brick)
39921 this.bricks.push(brick);
39922 brick.masonryId = this.id;
39926 * clear all the Masonry Brick
39928 clearAll : function()
39931 //this.getChildContainer().dom.innerHTML = "";
39932 this.el.dom.innerHTML = '';
39935 getSelected : function()
39937 if (!this.selectedBrick) {
39941 return this.selectedBrick;
39945 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39949 * register a Masonry Layout
39950 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39953 register : function(layout)
39955 this.groups[layout.id] = layout;
39958 * fetch a Masonry Layout based on the masonry layout ID
39959 * @param {string} the masonry layout to add
39960 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39963 get: function(layout_id) {
39964 if (typeof(this.groups[layout_id]) == 'undefined') {
39967 return this.groups[layout_id] ;
39979 * http://masonry.desandro.com
39981 * The idea is to render all the bricks based on vertical width...
39983 * The original code extends 'outlayer' - we might need to use that....
39989 * @class Roo.bootstrap.LayoutMasonryAuto
39990 * @extends Roo.bootstrap.Component
39991 * Bootstrap Layout Masonry class
39994 * Create a new Element
39995 * @param {Object} config The config object
39998 Roo.bootstrap.LayoutMasonryAuto = function(config){
39999 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40002 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40005 * @cfg {Boolean} isFitWidth - resize the width..
40007 isFitWidth : false, // options..
40009 * @cfg {Boolean} isOriginLeft = left align?
40011 isOriginLeft : true,
40013 * @cfg {Boolean} isOriginTop = top align?
40015 isOriginTop : false,
40017 * @cfg {Boolean} isLayoutInstant = no animation?
40019 isLayoutInstant : false, // needed?
40021 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40023 isResizingContainer : true,
40025 * @cfg {Number} columnWidth width of the columns
40031 * @cfg {Number} maxCols maximum number of columns
40036 * @cfg {Number} padHeight padding below box..
40042 * @cfg {Boolean} isAutoInitial defalut true
40045 isAutoInitial : true,
40051 initialColumnWidth : 0,
40052 currentSize : null,
40054 colYs : null, // array.
40061 bricks: null, //CompositeElement
40062 cols : 0, // array?
40063 // element : null, // wrapped now this.el
40064 _isLayoutInited : null,
40067 getAutoCreate : function(){
40071 cls: 'blog-masonary-wrapper ' + this.cls,
40073 cls : 'mas-boxes masonary'
40080 getChildContainer: function( )
40082 if (this.boxesEl) {
40083 return this.boxesEl;
40086 this.boxesEl = this.el.select('.mas-boxes').first();
40088 return this.boxesEl;
40092 initEvents : function()
40096 if(this.isAutoInitial){
40097 Roo.log('hook children rendered');
40098 this.on('childrenrendered', function() {
40099 Roo.log('children rendered');
40106 initial : function()
40108 this.reloadItems();
40110 this.currentSize = this.el.getBox(true);
40112 /// was window resize... - let's see if this works..
40113 Roo.EventManager.onWindowResize(this.resize, this);
40115 if(!this.isAutoInitial){
40120 this.layout.defer(500,this);
40123 reloadItems: function()
40125 this.bricks = this.el.select('.masonry-brick', true);
40127 this.bricks.each(function(b) {
40128 //Roo.log(b.getSize());
40129 if (!b.attr('originalwidth')) {
40130 b.attr('originalwidth', b.getSize().width);
40135 Roo.log(this.bricks.elements.length);
40138 resize : function()
40141 var cs = this.el.getBox(true);
40143 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40144 Roo.log("no change in with or X");
40147 this.currentSize = cs;
40151 layout : function()
40154 this._resetLayout();
40155 //this._manageStamps();
40157 // don't animate first layout
40158 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40159 this.layoutItems( isInstant );
40161 // flag for initalized
40162 this._isLayoutInited = true;
40165 layoutItems : function( isInstant )
40167 //var items = this._getItemsForLayout( this.items );
40168 // original code supports filtering layout items.. we just ignore it..
40170 this._layoutItems( this.bricks , isInstant );
40172 this._postLayout();
40174 _layoutItems : function ( items , isInstant)
40176 //this.fireEvent( 'layout', this, items );
40179 if ( !items || !items.elements.length ) {
40180 // no items, emit event with empty array
40185 items.each(function(item) {
40186 Roo.log("layout item");
40188 // get x/y object from method
40189 var position = this._getItemLayoutPosition( item );
40191 position.item = item;
40192 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40193 queue.push( position );
40196 this._processLayoutQueue( queue );
40198 /** Sets position of item in DOM
40199 * @param {Element} item
40200 * @param {Number} x - horizontal position
40201 * @param {Number} y - vertical position
40202 * @param {Boolean} isInstant - disables transitions
40204 _processLayoutQueue : function( queue )
40206 for ( var i=0, len = queue.length; i < len; i++ ) {
40207 var obj = queue[i];
40208 obj.item.position('absolute');
40209 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40215 * Any logic you want to do after each layout,
40216 * i.e. size the container
40218 _postLayout : function()
40220 this.resizeContainer();
40223 resizeContainer : function()
40225 if ( !this.isResizingContainer ) {
40228 var size = this._getContainerSize();
40230 this.el.setSize(size.width,size.height);
40231 this.boxesEl.setSize(size.width,size.height);
40237 _resetLayout : function()
40239 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40240 this.colWidth = this.el.getWidth();
40241 //this.gutter = this.el.getWidth();
40243 this.measureColumns();
40249 this.colYs.push( 0 );
40255 measureColumns : function()
40257 this.getContainerWidth();
40258 // if columnWidth is 0, default to outerWidth of first item
40259 if ( !this.columnWidth ) {
40260 var firstItem = this.bricks.first();
40261 Roo.log(firstItem);
40262 this.columnWidth = this.containerWidth;
40263 if (firstItem && firstItem.attr('originalwidth') ) {
40264 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40266 // columnWidth fall back to item of first element
40267 Roo.log("set column width?");
40268 this.initialColumnWidth = this.columnWidth ;
40270 // if first elem has no width, default to size of container
40275 if (this.initialColumnWidth) {
40276 this.columnWidth = this.initialColumnWidth;
40281 // column width is fixed at the top - however if container width get's smaller we should
40284 // this bit calcs how man columns..
40286 var columnWidth = this.columnWidth += this.gutter;
40288 // calculate columns
40289 var containerWidth = this.containerWidth + this.gutter;
40291 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40292 // fix rounding errors, typically with gutters
40293 var excess = columnWidth - containerWidth % columnWidth;
40296 // if overshoot is less than a pixel, round up, otherwise floor it
40297 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40298 cols = Math[ mathMethod ]( cols );
40299 this.cols = Math.max( cols, 1 );
40300 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40302 // padding positioning..
40303 var totalColWidth = this.cols * this.columnWidth;
40304 var padavail = this.containerWidth - totalColWidth;
40305 // so for 2 columns - we need 3 'pads'
40307 var padNeeded = (1+this.cols) * this.padWidth;
40309 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40311 this.columnWidth += padExtra
40312 //this.padWidth = Math.floor(padavail / ( this.cols));
40314 // adjust colum width so that padding is fixed??
40316 // we have 3 columns ... total = width * 3
40317 // we have X left over... that should be used by
40319 //if (this.expandC) {
40327 getContainerWidth : function()
40329 /* // container is parent if fit width
40330 var container = this.isFitWidth ? this.element.parentNode : this.element;
40331 // check that this.size and size are there
40332 // IE8 triggers resize on body size change, so they might not be
40334 var size = getSize( container ); //FIXME
40335 this.containerWidth = size && size.innerWidth; //FIXME
40338 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40342 _getItemLayoutPosition : function( item ) // what is item?
40344 // we resize the item to our columnWidth..
40346 item.setWidth(this.columnWidth);
40347 item.autoBoxAdjust = false;
40349 var sz = item.getSize();
40351 // how many columns does this brick span
40352 var remainder = this.containerWidth % this.columnWidth;
40354 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40355 // round if off by 1 pixel, otherwise use ceil
40356 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40357 colSpan = Math.min( colSpan, this.cols );
40359 // normally this should be '1' as we dont' currently allow multi width columns..
40361 var colGroup = this._getColGroup( colSpan );
40362 // get the minimum Y value from the columns
40363 var minimumY = Math.min.apply( Math, colGroup );
40364 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40366 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40368 // position the brick
40370 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40371 y: this.currentSize.y + minimumY + this.padHeight
40375 // apply setHeight to necessary columns
40376 var setHeight = minimumY + sz.height + this.padHeight;
40377 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40379 var setSpan = this.cols + 1 - colGroup.length;
40380 for ( var i = 0; i < setSpan; i++ ) {
40381 this.colYs[ shortColIndex + i ] = setHeight ;
40388 * @param {Number} colSpan - number of columns the element spans
40389 * @returns {Array} colGroup
40391 _getColGroup : function( colSpan )
40393 if ( colSpan < 2 ) {
40394 // if brick spans only one column, use all the column Ys
40399 // how many different places could this brick fit horizontally
40400 var groupCount = this.cols + 1 - colSpan;
40401 // for each group potential horizontal position
40402 for ( var i = 0; i < groupCount; i++ ) {
40403 // make an array of colY values for that one group
40404 var groupColYs = this.colYs.slice( i, i + colSpan );
40405 // and get the max value of the array
40406 colGroup[i] = Math.max.apply( Math, groupColYs );
40411 _manageStamp : function( stamp )
40413 var stampSize = stamp.getSize();
40414 var offset = stamp.getBox();
40415 // get the columns that this stamp affects
40416 var firstX = this.isOriginLeft ? offset.x : offset.right;
40417 var lastX = firstX + stampSize.width;
40418 var firstCol = Math.floor( firstX / this.columnWidth );
40419 firstCol = Math.max( 0, firstCol );
40421 var lastCol = Math.floor( lastX / this.columnWidth );
40422 // lastCol should not go over if multiple of columnWidth #425
40423 lastCol -= lastX % this.columnWidth ? 0 : 1;
40424 lastCol = Math.min( this.cols - 1, lastCol );
40426 // set colYs to bottom of the stamp
40427 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40430 for ( var i = firstCol; i <= lastCol; i++ ) {
40431 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40436 _getContainerSize : function()
40438 this.maxY = Math.max.apply( Math, this.colYs );
40443 if ( this.isFitWidth ) {
40444 size.width = this._getContainerFitWidth();
40450 _getContainerFitWidth : function()
40452 var unusedCols = 0;
40453 // count unused columns
40456 if ( this.colYs[i] !== 0 ) {
40461 // fit container to columns that have been used
40462 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40465 needsResizeLayout : function()
40467 var previousWidth = this.containerWidth;
40468 this.getContainerWidth();
40469 return previousWidth !== this.containerWidth;
40484 * @class Roo.bootstrap.MasonryBrick
40485 * @extends Roo.bootstrap.Component
40486 * Bootstrap MasonryBrick class
40489 * Create a new MasonryBrick
40490 * @param {Object} config The config object
40493 Roo.bootstrap.MasonryBrick = function(config){
40495 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40497 Roo.bootstrap.MasonryBrick.register(this);
40503 * When a MasonryBrick is clcik
40504 * @param {Roo.bootstrap.MasonryBrick} this
40505 * @param {Roo.EventObject} e
40511 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40514 * @cfg {String} title
40518 * @cfg {String} html
40522 * @cfg {String} bgimage
40526 * @cfg {String} videourl
40530 * @cfg {String} cls
40534 * @cfg {String} href
40538 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40543 * @cfg {String} placetitle (center|bottom)
40548 * @cfg {Boolean} isFitContainer defalut true
40550 isFitContainer : true,
40553 * @cfg {Boolean} preventDefault defalut false
40555 preventDefault : false,
40558 * @cfg {Boolean} inverse defalut false
40560 maskInverse : false,
40562 getAutoCreate : function()
40564 if(!this.isFitContainer){
40565 return this.getSplitAutoCreate();
40568 var cls = 'masonry-brick masonry-brick-full';
40570 if(this.href.length){
40571 cls += ' masonry-brick-link';
40574 if(this.bgimage.length){
40575 cls += ' masonry-brick-image';
40578 if(this.maskInverse){
40579 cls += ' mask-inverse';
40582 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40583 cls += ' enable-mask';
40587 cls += ' masonry-' + this.size + '-brick';
40590 if(this.placetitle.length){
40592 switch (this.placetitle) {
40594 cls += ' masonry-center-title';
40597 cls += ' masonry-bottom-title';
40604 if(!this.html.length && !this.bgimage.length){
40605 cls += ' masonry-center-title';
40608 if(!this.html.length && this.bgimage.length){
40609 cls += ' masonry-bottom-title';
40614 cls += ' ' + this.cls;
40618 tag: (this.href.length) ? 'a' : 'div',
40623 cls: 'masonry-brick-mask'
40627 cls: 'masonry-brick-paragraph',
40633 if(this.href.length){
40634 cfg.href = this.href;
40637 var cn = cfg.cn[1].cn;
40639 if(this.title.length){
40642 cls: 'masonry-brick-title',
40647 if(this.html.length){
40650 cls: 'masonry-brick-text',
40655 if (!this.title.length && !this.html.length) {
40656 cfg.cn[1].cls += ' hide';
40659 if(this.bgimage.length){
40662 cls: 'masonry-brick-image-view',
40667 if(this.videourl.length){
40668 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40669 // youtube support only?
40672 cls: 'masonry-brick-image-view',
40675 allowfullscreen : true
40683 getSplitAutoCreate : function()
40685 var cls = 'masonry-brick masonry-brick-split';
40687 if(this.href.length){
40688 cls += ' masonry-brick-link';
40691 if(this.bgimage.length){
40692 cls += ' masonry-brick-image';
40696 cls += ' masonry-' + this.size + '-brick';
40699 switch (this.placetitle) {
40701 cls += ' masonry-center-title';
40704 cls += ' masonry-bottom-title';
40707 if(!this.bgimage.length){
40708 cls += ' masonry-center-title';
40711 if(this.bgimage.length){
40712 cls += ' masonry-bottom-title';
40718 cls += ' ' + this.cls;
40722 tag: (this.href.length) ? 'a' : 'div',
40727 cls: 'masonry-brick-split-head',
40731 cls: 'masonry-brick-paragraph',
40738 cls: 'masonry-brick-split-body',
40744 if(this.href.length){
40745 cfg.href = this.href;
40748 if(this.title.length){
40749 cfg.cn[0].cn[0].cn.push({
40751 cls: 'masonry-brick-title',
40756 if(this.html.length){
40757 cfg.cn[1].cn.push({
40759 cls: 'masonry-brick-text',
40764 if(this.bgimage.length){
40765 cfg.cn[0].cn.push({
40767 cls: 'masonry-brick-image-view',
40772 if(this.videourl.length){
40773 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40774 // youtube support only?
40775 cfg.cn[0].cn.cn.push({
40777 cls: 'masonry-brick-image-view',
40780 allowfullscreen : true
40787 initEvents: function()
40789 switch (this.size) {
40822 this.el.on('touchstart', this.onTouchStart, this);
40823 this.el.on('touchmove', this.onTouchMove, this);
40824 this.el.on('touchend', this.onTouchEnd, this);
40825 this.el.on('contextmenu', this.onContextMenu, this);
40827 this.el.on('mouseenter' ,this.enter, this);
40828 this.el.on('mouseleave', this.leave, this);
40829 this.el.on('click', this.onClick, this);
40832 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40833 this.parent().bricks.push(this);
40838 onClick: function(e, el)
40840 var time = this.endTimer - this.startTimer;
40841 // Roo.log(e.preventDefault());
40844 e.preventDefault();
40849 if(!this.preventDefault){
40853 e.preventDefault();
40855 if (this.activeClass != '') {
40856 this.selectBrick();
40859 this.fireEvent('click', this, e);
40862 enter: function(e, el)
40864 e.preventDefault();
40866 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40870 if(this.bgimage.length && this.html.length){
40871 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40875 leave: function(e, el)
40877 e.preventDefault();
40879 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40883 if(this.bgimage.length && this.html.length){
40884 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40888 onTouchStart: function(e, el)
40890 // e.preventDefault();
40892 this.touchmoved = false;
40894 if(!this.isFitContainer){
40898 if(!this.bgimage.length || !this.html.length){
40902 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40904 this.timer = new Date().getTime();
40908 onTouchMove: function(e, el)
40910 this.touchmoved = true;
40913 onContextMenu : function(e,el)
40915 e.preventDefault();
40916 e.stopPropagation();
40920 onTouchEnd: function(e, el)
40922 // e.preventDefault();
40924 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40931 if(!this.bgimage.length || !this.html.length){
40933 if(this.href.length){
40934 window.location.href = this.href;
40940 if(!this.isFitContainer){
40944 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40946 window.location.href = this.href;
40949 //selection on single brick only
40950 selectBrick : function() {
40952 if (!this.parentId) {
40956 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40957 var index = m.selectedBrick.indexOf(this.id);
40960 m.selectedBrick.splice(index,1);
40961 this.el.removeClass(this.activeClass);
40965 for(var i = 0; i < m.selectedBrick.length; i++) {
40966 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40967 b.el.removeClass(b.activeClass);
40970 m.selectedBrick = [];
40972 m.selectedBrick.push(this.id);
40973 this.el.addClass(this.activeClass);
40977 isSelected : function(){
40978 return this.el.hasClass(this.activeClass);
40983 Roo.apply(Roo.bootstrap.MasonryBrick, {
40986 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40988 * register a Masonry Brick
40989 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40992 register : function(brick)
40994 //this.groups[brick.id] = brick;
40995 this.groups.add(brick.id, brick);
40998 * fetch a masonry brick based on the masonry brick ID
40999 * @param {string} the masonry brick to add
41000 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41003 get: function(brick_id)
41005 // if (typeof(this.groups[brick_id]) == 'undefined') {
41008 // return this.groups[brick_id] ;
41010 if(this.groups.key(brick_id)) {
41011 return this.groups.key(brick_id);
41029 * @class Roo.bootstrap.Brick
41030 * @extends Roo.bootstrap.Component
41031 * Bootstrap Brick class
41034 * Create a new Brick
41035 * @param {Object} config The config object
41038 Roo.bootstrap.Brick = function(config){
41039 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41045 * When a Brick is click
41046 * @param {Roo.bootstrap.Brick} this
41047 * @param {Roo.EventObject} e
41053 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41056 * @cfg {String} title
41060 * @cfg {String} html
41064 * @cfg {String} bgimage
41068 * @cfg {String} cls
41072 * @cfg {String} href
41076 * @cfg {String} video
41080 * @cfg {Boolean} square
41084 getAutoCreate : function()
41086 var cls = 'roo-brick';
41088 if(this.href.length){
41089 cls += ' roo-brick-link';
41092 if(this.bgimage.length){
41093 cls += ' roo-brick-image';
41096 if(!this.html.length && !this.bgimage.length){
41097 cls += ' roo-brick-center-title';
41100 if(!this.html.length && this.bgimage.length){
41101 cls += ' roo-brick-bottom-title';
41105 cls += ' ' + this.cls;
41109 tag: (this.href.length) ? 'a' : 'div',
41114 cls: 'roo-brick-paragraph',
41120 if(this.href.length){
41121 cfg.href = this.href;
41124 var cn = cfg.cn[0].cn;
41126 if(this.title.length){
41129 cls: 'roo-brick-title',
41134 if(this.html.length){
41137 cls: 'roo-brick-text',
41144 if(this.bgimage.length){
41147 cls: 'roo-brick-image-view',
41155 initEvents: function()
41157 if(this.title.length || this.html.length){
41158 this.el.on('mouseenter' ,this.enter, this);
41159 this.el.on('mouseleave', this.leave, this);
41162 Roo.EventManager.onWindowResize(this.resize, this);
41164 if(this.bgimage.length){
41165 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41166 this.imageEl.on('load', this.onImageLoad, this);
41173 onImageLoad : function()
41178 resize : function()
41180 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41182 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41184 if(this.bgimage.length){
41185 var image = this.el.select('.roo-brick-image-view', true).first();
41187 image.setWidth(paragraph.getWidth());
41190 image.setHeight(paragraph.getWidth());
41193 this.el.setHeight(image.getHeight());
41194 paragraph.setHeight(image.getHeight());
41200 enter: function(e, el)
41202 e.preventDefault();
41204 if(this.bgimage.length){
41205 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41206 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41210 leave: function(e, el)
41212 e.preventDefault();
41214 if(this.bgimage.length){
41215 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41216 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41231 * @class Roo.bootstrap.form.NumberField
41232 * @extends Roo.bootstrap.form.Input
41233 * Bootstrap NumberField class
41239 * Create a new NumberField
41240 * @param {Object} config The config object
41243 Roo.bootstrap.form.NumberField = function(config){
41244 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41247 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41250 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41252 allowDecimals : true,
41254 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41256 decimalSeparator : ".",
41258 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41260 decimalPrecision : 2,
41262 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41264 allowNegative : true,
41267 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41271 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41273 minValue : Number.NEGATIVE_INFINITY,
41275 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41277 maxValue : Number.MAX_VALUE,
41279 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41281 minText : "The minimum value for this field is {0}",
41283 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41285 maxText : "The maximum value for this field is {0}",
41287 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41288 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41290 nanText : "{0} is not a valid number",
41292 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41294 thousandsDelimiter : false,
41296 * @cfg {String} valueAlign alignment of value
41298 valueAlign : "left",
41300 getAutoCreate : function()
41302 var hiddenInput = {
41306 cls: 'hidden-number-input'
41310 hiddenInput.name = this.name;
41315 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41317 this.name = hiddenInput.name;
41319 if(cfg.cn.length > 0) {
41320 cfg.cn.push(hiddenInput);
41327 initEvents : function()
41329 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41331 var allowed = "0123456789";
41333 if(this.allowDecimals){
41334 allowed += this.decimalSeparator;
41337 if(this.allowNegative){
41341 if(this.thousandsDelimiter) {
41345 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41347 var keyPress = function(e){
41349 var k = e.getKey();
41351 var c = e.getCharCode();
41354 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41355 allowed.indexOf(String.fromCharCode(c)) === -1
41361 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41365 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41370 this.el.on("keypress", keyPress, this);
41373 validateValue : function(value)
41376 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41380 var num = this.parseValue(value);
41383 this.markInvalid(String.format(this.nanText, value));
41387 if(num < this.minValue){
41388 this.markInvalid(String.format(this.minText, this.minValue));
41392 if(num > this.maxValue){
41393 this.markInvalid(String.format(this.maxText, this.maxValue));
41400 getValue : function()
41402 var v = this.hiddenEl().getValue();
41404 return this.fixPrecision(this.parseValue(v));
41407 parseValue : function(value)
41409 if(this.thousandsDelimiter) {
41411 r = new RegExp(",", "g");
41412 value = value.replace(r, "");
41415 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41416 return isNaN(value) ? '' : value;
41419 fixPrecision : function(value)
41421 if(this.thousandsDelimiter) {
41423 r = new RegExp(",", "g");
41424 value = value.replace(r, "");
41427 var nan = isNaN(value);
41429 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41430 return nan ? '' : value;
41432 return parseFloat(value).toFixed(this.decimalPrecision);
41435 setValue : function(v)
41437 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41443 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41445 this.inputEl().dom.value = (v == '') ? '' :
41446 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41448 if(!this.allowZero && v === '0') {
41449 this.hiddenEl().dom.value = '';
41450 this.inputEl().dom.value = '';
41457 decimalPrecisionFcn : function(v)
41459 return Math.floor(v);
41462 beforeBlur : function()
41464 var v = this.parseValue(this.getRawValue());
41466 if(v || v === 0 || v === ''){
41471 hiddenEl : function()
41473 return this.el.select('input.hidden-number-input',true).first();
41485 * @class Roo.bootstrap.DocumentSlider
41486 * @extends Roo.bootstrap.Component
41487 * Bootstrap DocumentSlider class
41490 * Create a new DocumentViewer
41491 * @param {Object} config The config object
41494 Roo.bootstrap.DocumentSlider = function(config){
41495 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41502 * Fire after initEvent
41503 * @param {Roo.bootstrap.DocumentSlider} this
41508 * Fire after update
41509 * @param {Roo.bootstrap.DocumentSlider} this
41515 * @param {Roo.bootstrap.DocumentSlider} this
41521 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41527 getAutoCreate : function()
41531 cls : 'roo-document-slider',
41535 cls : 'roo-document-slider-header',
41539 cls : 'roo-document-slider-header-title'
41545 cls : 'roo-document-slider-body',
41549 cls : 'roo-document-slider-prev',
41553 cls : 'fa fa-chevron-left'
41559 cls : 'roo-document-slider-thumb',
41563 cls : 'roo-document-slider-image'
41569 cls : 'roo-document-slider-next',
41573 cls : 'fa fa-chevron-right'
41585 initEvents : function()
41587 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41588 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41590 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41591 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41593 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41594 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41596 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41597 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41599 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41600 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41602 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41603 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41605 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41606 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41608 this.thumbEl.on('click', this.onClick, this);
41610 this.prevIndicator.on('click', this.prev, this);
41612 this.nextIndicator.on('click', this.next, this);
41616 initial : function()
41618 if(this.files.length){
41619 this.indicator = 1;
41623 this.fireEvent('initial', this);
41626 update : function()
41628 this.imageEl.attr('src', this.files[this.indicator - 1]);
41630 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41632 this.prevIndicator.show();
41634 if(this.indicator == 1){
41635 this.prevIndicator.hide();
41638 this.nextIndicator.show();
41640 if(this.indicator == this.files.length){
41641 this.nextIndicator.hide();
41644 this.thumbEl.scrollTo('top');
41646 this.fireEvent('update', this);
41649 onClick : function(e)
41651 e.preventDefault();
41653 this.fireEvent('click', this);
41658 e.preventDefault();
41660 this.indicator = Math.max(1, this.indicator - 1);
41667 e.preventDefault();
41669 this.indicator = Math.min(this.files.length, this.indicator + 1);
41683 * @class Roo.bootstrap.form.RadioSet
41684 * @extends Roo.bootstrap.form.Input
41685 * @children Roo.bootstrap.form.Radio
41686 * Bootstrap RadioSet class
41687 * @cfg {String} indicatorpos (left|right) default left
41688 * @cfg {Boolean} inline (true|false) inline the element (default true)
41689 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41691 * Create a new RadioSet
41692 * @param {Object} config The config object
41695 Roo.bootstrap.form.RadioSet = function(config){
41697 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41701 Roo.bootstrap.form.RadioSet.register(this);
41706 * Fires when the element is checked or unchecked.
41707 * @param {Roo.bootstrap.form.RadioSet} this This radio
41708 * @param {Roo.bootstrap.form.Radio} item The checked item
41713 * Fires when the element is click.
41714 * @param {Roo.bootstrap.form.RadioSet} this This radio set
41715 * @param {Roo.bootstrap.form.Radio} item The checked item
41716 * @param {Roo.EventObject} e The event object
41723 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
41731 indicatorpos : 'left',
41733 getAutoCreate : function()
41737 cls : 'roo-radio-set-label',
41741 html : this.fieldLabel
41745 if (Roo.bootstrap.version == 3) {
41748 if(this.indicatorpos == 'left'){
41751 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41752 tooltip : 'This field is required'
41757 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41758 tooltip : 'This field is required'
41764 cls : 'roo-radio-set-items'
41767 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41769 if (align === 'left' && this.fieldLabel.length) {
41772 cls : "roo-radio-set-right",
41778 if(this.labelWidth > 12){
41779 label.style = "width: " + this.labelWidth + 'px';
41782 if(this.labelWidth < 13 && this.labelmd == 0){
41783 this.labelmd = this.labelWidth;
41786 if(this.labellg > 0){
41787 label.cls += ' col-lg-' + this.labellg;
41788 items.cls += ' col-lg-' + (12 - this.labellg);
41791 if(this.labelmd > 0){
41792 label.cls += ' col-md-' + this.labelmd;
41793 items.cls += ' col-md-' + (12 - this.labelmd);
41796 if(this.labelsm > 0){
41797 label.cls += ' col-sm-' + this.labelsm;
41798 items.cls += ' col-sm-' + (12 - this.labelsm);
41801 if(this.labelxs > 0){
41802 label.cls += ' col-xs-' + this.labelxs;
41803 items.cls += ' col-xs-' + (12 - this.labelxs);
41809 cls : 'roo-radio-set',
41813 cls : 'roo-radio-set-input',
41816 value : this.value ? this.value : ''
41823 if(this.weight.length){
41824 cfg.cls += ' roo-radio-' + this.weight;
41828 cfg.cls += ' roo-radio-set-inline';
41832 ['xs','sm','md','lg'].map(function(size){
41833 if (settings[size]) {
41834 cfg.cls += ' col-' + size + '-' + settings[size];
41842 initEvents : function()
41844 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41845 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41847 if(!this.fieldLabel.length){
41848 this.labelEl.hide();
41851 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41852 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41854 this.indicator = this.indicatorEl();
41856 if(this.indicator){
41857 this.indicator.addClass('invisible');
41860 this.originalValue = this.getValue();
41864 inputEl: function ()
41866 return this.el.select('.roo-radio-set-input', true).first();
41869 getChildContainer : function()
41871 return this.itemsEl;
41874 register : function(item)
41876 this.radioes.push(item);
41880 validate : function()
41882 if(this.getVisibilityEl().hasClass('hidden')){
41888 Roo.each(this.radioes, function(i){
41897 if(this.allowBlank) {
41901 if(this.disabled || valid){
41906 this.markInvalid();
41911 markValid : function()
41913 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41914 this.indicatorEl().removeClass('visible');
41915 this.indicatorEl().addClass('invisible');
41919 if (Roo.bootstrap.version == 3) {
41920 this.el.removeClass([this.invalidClass, this.validClass]);
41921 this.el.addClass(this.validClass);
41923 this.el.removeClass(['is-invalid','is-valid']);
41924 this.el.addClass(['is-valid']);
41926 this.fireEvent('valid', this);
41929 markInvalid : function(msg)
41931 if(this.allowBlank || this.disabled){
41935 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41936 this.indicatorEl().removeClass('invisible');
41937 this.indicatorEl().addClass('visible');
41939 if (Roo.bootstrap.version == 3) {
41940 this.el.removeClass([this.invalidClass, this.validClass]);
41941 this.el.addClass(this.invalidClass);
41943 this.el.removeClass(['is-invalid','is-valid']);
41944 this.el.addClass(['is-invalid']);
41947 this.fireEvent('invalid', this, msg);
41951 setValue : function(v, suppressEvent)
41953 if(this.value === v){
41960 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41963 Roo.each(this.radioes, function(i){
41965 i.el.removeClass('checked');
41968 Roo.each(this.radioes, function(i){
41970 if(i.value === v || i.value.toString() === v.toString()){
41972 i.el.addClass('checked');
41974 if(suppressEvent !== true){
41975 this.fireEvent('check', this, i);
41986 clearInvalid : function(){
41988 if(!this.el || this.preventMark){
41992 this.el.removeClass([this.invalidClass]);
41994 this.fireEvent('valid', this);
41999 Roo.apply(Roo.bootstrap.form.RadioSet, {
42003 register : function(set)
42005 this.groups[set.name] = set;
42008 get: function(name)
42010 if (typeof(this.groups[name]) == 'undefined') {
42014 return this.groups[name] ;
42020 * Ext JS Library 1.1.1
42021 * Copyright(c) 2006-2007, Ext JS, LLC.
42023 * Originally Released Under LGPL - original licence link has changed is not relivant.
42026 * <script type="text/javascript">
42031 * @class Roo.bootstrap.SplitBar
42032 * @extends Roo.util.Observable
42033 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42037 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42038 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42039 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42040 split.minSize = 100;
42041 split.maxSize = 600;
42042 split.animate = true;
42043 split.on('moved', splitterMoved);
42046 * Create a new SplitBar
42047 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42048 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42049 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42050 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42051 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42052 position of the SplitBar).
42054 Roo.bootstrap.SplitBar = function(cfg){
42059 // dragElement : elm
42060 // resizingElement: el,
42062 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42063 // placement : Roo.bootstrap.SplitBar.LEFT ,
42064 // existingProxy ???
42067 this.el = Roo.get(cfg.dragElement, true);
42068 this.el.dom.unselectable = "on";
42070 this.resizingEl = Roo.get(cfg.resizingElement, true);
42074 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42075 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42078 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42081 * The minimum size of the resizing element. (Defaults to 0)
42087 * The maximum size of the resizing element. (Defaults to 2000)
42090 this.maxSize = 2000;
42093 * Whether to animate the transition to the new size
42096 this.animate = false;
42099 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42102 this.useShim = false;
42107 if(!cfg.existingProxy){
42109 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42111 this.proxy = Roo.get(cfg.existingProxy).dom;
42114 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42117 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42120 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42123 this.dragSpecs = {};
42126 * @private The adapter to use to positon and resize elements
42128 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42129 this.adapter.init(this);
42131 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42133 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42134 this.el.addClass("roo-splitbar-h");
42137 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42138 this.el.addClass("roo-splitbar-v");
42144 * Fires when the splitter is moved (alias for {@link #event-moved})
42145 * @param {Roo.bootstrap.SplitBar} this
42146 * @param {Number} newSize the new width or height
42151 * Fires when the splitter is moved
42152 * @param {Roo.bootstrap.SplitBar} this
42153 * @param {Number} newSize the new width or height
42157 * @event beforeresize
42158 * Fires before the splitter is dragged
42159 * @param {Roo.bootstrap.SplitBar} this
42161 "beforeresize" : true,
42163 "beforeapply" : true
42166 Roo.util.Observable.call(this);
42169 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42170 onStartProxyDrag : function(x, y){
42171 this.fireEvent("beforeresize", this);
42173 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42175 o.enableDisplayMode("block");
42176 // all splitbars share the same overlay
42177 Roo.bootstrap.SplitBar.prototype.overlay = o;
42179 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42180 this.overlay.show();
42181 Roo.get(this.proxy).setDisplayed("block");
42182 var size = this.adapter.getElementSize(this);
42183 this.activeMinSize = this.getMinimumSize();;
42184 this.activeMaxSize = this.getMaximumSize();;
42185 var c1 = size - this.activeMinSize;
42186 var c2 = Math.max(this.activeMaxSize - size, 0);
42187 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42188 this.dd.resetConstraints();
42189 this.dd.setXConstraint(
42190 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42191 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42193 this.dd.setYConstraint(0, 0);
42195 this.dd.resetConstraints();
42196 this.dd.setXConstraint(0, 0);
42197 this.dd.setYConstraint(
42198 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42199 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42202 this.dragSpecs.startSize = size;
42203 this.dragSpecs.startPoint = [x, y];
42204 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42208 * @private Called after the drag operation by the DDProxy
42210 onEndProxyDrag : function(e){
42211 Roo.get(this.proxy).setDisplayed(false);
42212 var endPoint = Roo.lib.Event.getXY(e);
42214 this.overlay.hide();
42217 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42218 newSize = this.dragSpecs.startSize +
42219 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42220 endPoint[0] - this.dragSpecs.startPoint[0] :
42221 this.dragSpecs.startPoint[0] - endPoint[0]
42224 newSize = this.dragSpecs.startSize +
42225 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42226 endPoint[1] - this.dragSpecs.startPoint[1] :
42227 this.dragSpecs.startPoint[1] - endPoint[1]
42230 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42231 if(newSize != this.dragSpecs.startSize){
42232 if(this.fireEvent('beforeapply', this, newSize) !== false){
42233 this.adapter.setElementSize(this, newSize);
42234 this.fireEvent("moved", this, newSize);
42235 this.fireEvent("resize", this, newSize);
42241 * Get the adapter this SplitBar uses
42242 * @return The adapter object
42244 getAdapter : function(){
42245 return this.adapter;
42249 * Set the adapter this SplitBar uses
42250 * @param {Object} adapter A SplitBar adapter object
42252 setAdapter : function(adapter){
42253 this.adapter = adapter;
42254 this.adapter.init(this);
42258 * Gets the minimum size for the resizing element
42259 * @return {Number} The minimum size
42261 getMinimumSize : function(){
42262 return this.minSize;
42266 * Sets the minimum size for the resizing element
42267 * @param {Number} minSize The minimum size
42269 setMinimumSize : function(minSize){
42270 this.minSize = minSize;
42274 * Gets the maximum size for the resizing element
42275 * @return {Number} The maximum size
42277 getMaximumSize : function(){
42278 return this.maxSize;
42282 * Sets the maximum size for the resizing element
42283 * @param {Number} maxSize The maximum size
42285 setMaximumSize : function(maxSize){
42286 this.maxSize = maxSize;
42290 * Sets the initialize size for the resizing element
42291 * @param {Number} size The initial size
42293 setCurrentSize : function(size){
42294 var oldAnimate = this.animate;
42295 this.animate = false;
42296 this.adapter.setElementSize(this, size);
42297 this.animate = oldAnimate;
42301 * Destroy this splitbar.
42302 * @param {Boolean} removeEl True to remove the element
42304 destroy : function(removeEl){
42306 this.shim.remove();
42309 this.proxy.parentNode.removeChild(this.proxy);
42317 * @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.
42319 Roo.bootstrap.SplitBar.createProxy = function(dir){
42320 var proxy = new Roo.Element(document.createElement("div"));
42321 proxy.unselectable();
42322 var cls = 'roo-splitbar-proxy';
42323 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42324 document.body.appendChild(proxy.dom);
42329 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42330 * Default Adapter. It assumes the splitter and resizing element are not positioned
42331 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42333 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42336 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42337 // do nothing for now
42338 init : function(s){
42342 * Called before drag operations to get the current size of the resizing element.
42343 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42345 getElementSize : function(s){
42346 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42347 return s.resizingEl.getWidth();
42349 return s.resizingEl.getHeight();
42354 * Called after drag operations to set the size of the resizing element.
42355 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42356 * @param {Number} newSize The new size to set
42357 * @param {Function} onComplete A function to be invoked when resizing is complete
42359 setElementSize : function(s, newSize, onComplete){
42360 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42362 s.resizingEl.setWidth(newSize);
42364 onComplete(s, newSize);
42367 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42372 s.resizingEl.setHeight(newSize);
42374 onComplete(s, newSize);
42377 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42384 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42385 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42386 * Adapter that moves the splitter element to align with the resized sizing element.
42387 * Used with an absolute positioned SplitBar.
42388 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42389 * document.body, make sure you assign an id to the body element.
42391 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42392 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42393 this.container = Roo.get(container);
42396 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42397 init : function(s){
42398 this.basic.init(s);
42401 getElementSize : function(s){
42402 return this.basic.getElementSize(s);
42405 setElementSize : function(s, newSize, onComplete){
42406 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42409 moveSplitter : function(s){
42410 var yes = Roo.bootstrap.SplitBar;
42411 switch(s.placement){
42413 s.el.setX(s.resizingEl.getRight());
42416 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42419 s.el.setY(s.resizingEl.getBottom());
42422 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42429 * Orientation constant - Create a vertical SplitBar
42433 Roo.bootstrap.SplitBar.VERTICAL = 1;
42436 * Orientation constant - Create a horizontal SplitBar
42440 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42443 * Placement constant - The resizing element is to the left of the splitter element
42447 Roo.bootstrap.SplitBar.LEFT = 1;
42450 * Placement constant - The resizing element is to the right of the splitter element
42454 Roo.bootstrap.SplitBar.RIGHT = 2;
42457 * Placement constant - The resizing element is positioned above the splitter element
42461 Roo.bootstrap.SplitBar.TOP = 3;
42464 * Placement constant - The resizing element is positioned under splitter element
42468 Roo.bootstrap.SplitBar.BOTTOM = 4;
42471 * Ext JS Library 1.1.1
42472 * Copyright(c) 2006-2007, Ext JS, LLC.
42474 * Originally Released Under LGPL - original licence link has changed is not relivant.
42477 * <script type="text/javascript">
42481 * @class Roo.bootstrap.layout.Manager
42482 * @extends Roo.bootstrap.Component
42484 * Base class for layout managers.
42486 Roo.bootstrap.layout.Manager = function(config)
42488 this.monitorWindowResize = true; // do this before we apply configuration.
42490 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42496 /** false to disable window resize monitoring @type Boolean */
42502 * Fires when a layout is performed.
42503 * @param {Roo.LayoutManager} this
42507 * @event regionresized
42508 * Fires when the user resizes a region.
42509 * @param {Roo.LayoutRegion} region The resized region
42510 * @param {Number} newSize The new size (width for east/west, height for north/south)
42512 "regionresized" : true,
42514 * @event regioncollapsed
42515 * Fires when a region is collapsed.
42516 * @param {Roo.LayoutRegion} region The collapsed region
42518 "regioncollapsed" : true,
42520 * @event regionexpanded
42521 * Fires when a region is expanded.
42522 * @param {Roo.LayoutRegion} region The expanded region
42524 "regionexpanded" : true
42526 this.updating = false;
42529 this.el = Roo.get(config.el);
42535 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42540 monitorWindowResize : true,
42546 onRender : function(ct, position)
42549 this.el = Roo.get(ct);
42552 //this.fireEvent('render',this);
42556 initEvents: function()
42560 // ie scrollbar fix
42561 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42562 document.body.scroll = "no";
42563 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42564 this.el.position('relative');
42566 this.id = this.el.id;
42567 this.el.addClass("roo-layout-container");
42568 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42569 if(this.el.dom != document.body ) {
42570 this.el.on('resize', this.layout,this);
42571 this.el.on('show', this.layout,this);
42577 * Returns true if this layout is currently being updated
42578 * @return {Boolean}
42580 isUpdating : function(){
42581 return this.updating;
42585 * Suspend the LayoutManager from doing auto-layouts while
42586 * making multiple add or remove calls
42588 beginUpdate : function(){
42589 this.updating = true;
42593 * Restore auto-layouts and optionally disable the manager from performing a layout
42594 * @param {Boolean} noLayout true to disable a layout update
42596 endUpdate : function(noLayout){
42597 this.updating = false;
42603 layout: function(){
42607 onRegionResized : function(region, newSize){
42608 this.fireEvent("regionresized", region, newSize);
42612 onRegionCollapsed : function(region){
42613 this.fireEvent("regioncollapsed", region);
42616 onRegionExpanded : function(region){
42617 this.fireEvent("regionexpanded", region);
42621 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42622 * performs box-model adjustments.
42623 * @return {Object} The size as an object {width: (the width), height: (the height)}
42625 getViewSize : function()
42628 if(this.el.dom != document.body){
42629 size = this.el.getSize();
42631 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42633 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42634 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42639 * Returns the Element this layout is bound to.
42640 * @return {Roo.Element}
42642 getEl : function(){
42647 * Returns the specified region.
42648 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42649 * @return {Roo.LayoutRegion}
42651 getRegion : function(target){
42652 return this.regions[target.toLowerCase()];
42655 onWindowResize : function(){
42656 if(this.monitorWindowResize){
42663 * Ext JS Library 1.1.1
42664 * Copyright(c) 2006-2007, Ext JS, LLC.
42666 * Originally Released Under LGPL - original licence link has changed is not relivant.
42669 * <script type="text/javascript">
42672 * @class Roo.bootstrap.layout.Border
42673 * @extends Roo.bootstrap.layout.Manager
42674 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42675 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42676 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42677 * please see: examples/bootstrap/nested.html<br><br>
42679 <b>The container the layout is rendered into can be either the body element or any other element.
42680 If it is not the body element, the container needs to either be an absolute positioned element,
42681 or you will need to add "position:relative" to the css of the container. You will also need to specify
42682 the container size if it is not the body element.</b>
42685 * Create a new Border
42686 * @param {Object} config Configuration options
42688 Roo.bootstrap.layout.Border = function(config){
42689 config = config || {};
42690 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42694 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42695 if(config[region]){
42696 config[region].region = region;
42697 this.addRegion(config[region]);
42703 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
42705 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42708 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42711 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42714 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42717 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42720 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42726 parent : false, // this might point to a 'nest' or a ???
42729 * Creates and adds a new region if it doesn't already exist.
42730 * @param {String} target The target region key (north, south, east, west or center).
42731 * @param {Object} config The regions config object
42732 * @return {BorderLayoutRegion} The new region
42734 addRegion : function(config)
42736 if(!this.regions[config.region]){
42737 var r = this.factory(config);
42738 this.bindRegion(r);
42740 return this.regions[config.region];
42744 bindRegion : function(r){
42745 this.regions[r.config.region] = r;
42747 r.on("visibilitychange", this.layout, this);
42748 r.on("paneladded", this.layout, this);
42749 r.on("panelremoved", this.layout, this);
42750 r.on("invalidated", this.layout, this);
42751 r.on("resized", this.onRegionResized, this);
42752 r.on("collapsed", this.onRegionCollapsed, this);
42753 r.on("expanded", this.onRegionExpanded, this);
42757 * Performs a layout update.
42759 layout : function()
42761 if(this.updating) {
42765 // render all the rebions if they have not been done alreayd?
42766 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42767 if(this.regions[region] && !this.regions[region].bodyEl){
42768 this.regions[region].onRender(this.el)
42772 var size = this.getViewSize();
42773 var w = size.width;
42774 var h = size.height;
42779 //var x = 0, y = 0;
42781 var rs = this.regions;
42782 var north = rs["north"];
42783 var south = rs["south"];
42784 var west = rs["west"];
42785 var east = rs["east"];
42786 var center = rs["center"];
42787 //if(this.hideOnLayout){ // not supported anymore
42788 //c.el.setStyle("display", "none");
42790 if(north && north.isVisible()){
42791 var b = north.getBox();
42792 var m = north.getMargins();
42793 b.width = w - (m.left+m.right);
42796 centerY = b.height + b.y + m.bottom;
42797 centerH -= centerY;
42798 north.updateBox(this.safeBox(b));
42800 if(south && south.isVisible()){
42801 var b = south.getBox();
42802 var m = south.getMargins();
42803 b.width = w - (m.left+m.right);
42805 var totalHeight = (b.height + m.top + m.bottom);
42806 b.y = h - totalHeight + m.top;
42807 centerH -= totalHeight;
42808 south.updateBox(this.safeBox(b));
42810 if(west && west.isVisible()){
42811 var b = west.getBox();
42812 var m = west.getMargins();
42813 b.height = centerH - (m.top+m.bottom);
42815 b.y = centerY + m.top;
42816 var totalWidth = (b.width + m.left + m.right);
42817 centerX += totalWidth;
42818 centerW -= totalWidth;
42819 west.updateBox(this.safeBox(b));
42821 if(east && east.isVisible()){
42822 var b = east.getBox();
42823 var m = east.getMargins();
42824 b.height = centerH - (m.top+m.bottom);
42825 var totalWidth = (b.width + m.left + m.right);
42826 b.x = w - totalWidth + m.left;
42827 b.y = centerY + m.top;
42828 centerW -= totalWidth;
42829 east.updateBox(this.safeBox(b));
42832 var m = center.getMargins();
42834 x: centerX + m.left,
42835 y: centerY + m.top,
42836 width: centerW - (m.left+m.right),
42837 height: centerH - (m.top+m.bottom)
42839 //if(this.hideOnLayout){
42840 //center.el.setStyle("display", "block");
42842 center.updateBox(this.safeBox(centerBox));
42845 this.fireEvent("layout", this);
42849 safeBox : function(box){
42850 box.width = Math.max(0, box.width);
42851 box.height = Math.max(0, box.height);
42856 * Adds a ContentPanel (or subclass) to this layout.
42857 * @param {String} target The target region key (north, south, east, west or center).
42858 * @param {Roo.ContentPanel} panel The panel to add
42859 * @return {Roo.ContentPanel} The added panel
42861 add : function(target, panel){
42863 target = target.toLowerCase();
42864 return this.regions[target].add(panel);
42868 * Remove a ContentPanel (or subclass) to this layout.
42869 * @param {String} target The target region key (north, south, east, west or center).
42870 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42871 * @return {Roo.ContentPanel} The removed panel
42873 remove : function(target, panel){
42874 target = target.toLowerCase();
42875 return this.regions[target].remove(panel);
42879 * Searches all regions for a panel with the specified id
42880 * @param {String} panelId
42881 * @return {Roo.ContentPanel} The panel or null if it wasn't found
42883 findPanel : function(panelId){
42884 var rs = this.regions;
42885 for(var target in rs){
42886 if(typeof rs[target] != "function"){
42887 var p = rs[target].getPanel(panelId);
42897 * Searches all regions for a panel with the specified id and activates (shows) it.
42898 * @param {String/ContentPanel} panelId The panels id or the panel itself
42899 * @return {Roo.ContentPanel} The shown panel or null
42901 showPanel : function(panelId) {
42902 var rs = this.regions;
42903 for(var target in rs){
42904 var r = rs[target];
42905 if(typeof r != "function"){
42906 if(r.hasPanel(panelId)){
42907 return r.showPanel(panelId);
42915 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42916 * @param {Roo.state.Provider} provider (optional) An alternate state provider
42919 restoreState : function(provider){
42921 provider = Roo.state.Manager;
42923 var sm = new Roo.LayoutStateManager();
42924 sm.init(this, provider);
42930 * Adds a xtype elements to the layout.
42934 xtype : 'ContentPanel',
42941 xtype : 'NestedLayoutPanel',
42947 items : [ ... list of content panels or nested layout panels.. ]
42951 * @param {Object} cfg Xtype definition of item to add.
42953 addxtype : function(cfg)
42955 // basically accepts a pannel...
42956 // can accept a layout region..!?!?
42957 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42960 // theory? children can only be panels??
42962 //if (!cfg.xtype.match(/Panel$/)) {
42967 if (typeof(cfg.region) == 'undefined') {
42968 Roo.log("Failed to add Panel, region was not set");
42972 var region = cfg.region;
42978 xitems = cfg.items;
42983 if ( region == 'center') {
42984 Roo.log("Center: " + cfg.title);
42990 case 'Content': // ContentPanel (el, cfg)
42991 case 'Scroll': // ContentPanel (el, cfg)
42993 cfg.autoCreate = cfg.autoCreate || true;
42994 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42996 // var el = this.el.createChild();
42997 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43000 this.add(region, ret);
43004 case 'TreePanel': // our new panel!
43005 cfg.el = this.el.createChild();
43006 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43007 this.add(region, ret);
43012 // create a new Layout (which is a Border Layout...
43014 var clayout = cfg.layout;
43015 clayout.el = this.el.createChild();
43016 clayout.items = clayout.items || [];
43020 // replace this exitems with the clayout ones..
43021 xitems = clayout.items;
43023 // force background off if it's in center...
43024 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43025 cfg.background = false;
43027 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43030 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43031 //console.log('adding nested layout panel ' + cfg.toSource());
43032 this.add(region, ret);
43033 nb = {}; /// find first...
43038 // needs grid and region
43040 //var el = this.getRegion(region).el.createChild();
43042 *var el = this.el.createChild();
43043 // create the grid first...
43044 cfg.grid.container = el;
43045 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43048 if (region == 'center' && this.active ) {
43049 cfg.background = false;
43052 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43054 this.add(region, ret);
43056 if (cfg.background) {
43057 // render grid on panel activation (if panel background)
43058 ret.on('activate', function(gp) {
43059 if (!gp.grid.rendered) {
43060 // gp.grid.render(el);
43064 // cfg.grid.render(el);
43070 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43071 // it was the old xcomponent building that caused this before.
43072 // espeically if border is the top element in the tree.
43082 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43084 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43085 this.add(region, ret);
43089 throw "Can not add '" + cfg.xtype + "' to Border";
43095 this.beginUpdate();
43099 Roo.each(xitems, function(i) {
43100 region = nb && i.region ? i.region : false;
43102 var add = ret.addxtype(i);
43105 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43106 if (!i.background) {
43107 abn[region] = nb[region] ;
43114 // make the last non-background panel active..
43115 //if (nb) { Roo.log(abn); }
43118 for(var r in abn) {
43119 region = this.getRegion(r);
43121 // tried using nb[r], but it does not work..
43123 region.showPanel(abn[r]);
43134 factory : function(cfg)
43137 var validRegions = Roo.bootstrap.layout.Border.regions;
43139 var target = cfg.region;
43142 var r = Roo.bootstrap.layout;
43146 return new r.North(cfg);
43148 return new r.South(cfg);
43150 return new r.East(cfg);
43152 return new r.West(cfg);
43154 return new r.Center(cfg);
43156 throw 'Layout region "'+target+'" not supported.';
43163 * Ext JS Library 1.1.1
43164 * Copyright(c) 2006-2007, Ext JS, LLC.
43166 * Originally Released Under LGPL - original licence link has changed is not relivant.
43169 * <script type="text/javascript">
43173 * @class Roo.bootstrap.layout.Basic
43174 * @extends Roo.util.Observable
43175 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43176 * and does not have a titlebar, tabs or any other features. All it does is size and position
43177 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43178 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43179 * @cfg {string} region the region that it inhabits..
43180 * @cfg {bool} skipConfig skip config?
43184 Roo.bootstrap.layout.Basic = function(config){
43186 this.mgr = config.mgr;
43188 this.position = config.region;
43190 var skipConfig = config.skipConfig;
43194 * @scope Roo.BasicLayoutRegion
43198 * @event beforeremove
43199 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43200 * @param {Roo.LayoutRegion} this
43201 * @param {Roo.ContentPanel} panel The panel
43202 * @param {Object} e The cancel event object
43204 "beforeremove" : true,
43206 * @event invalidated
43207 * Fires when the layout for this region is changed.
43208 * @param {Roo.LayoutRegion} this
43210 "invalidated" : true,
43212 * @event visibilitychange
43213 * Fires when this region is shown or hidden
43214 * @param {Roo.LayoutRegion} this
43215 * @param {Boolean} visibility true or false
43217 "visibilitychange" : true,
43219 * @event paneladded
43220 * Fires when a panel is added.
43221 * @param {Roo.LayoutRegion} this
43222 * @param {Roo.ContentPanel} panel The panel
43224 "paneladded" : true,
43226 * @event panelremoved
43227 * Fires when a panel is removed.
43228 * @param {Roo.LayoutRegion} this
43229 * @param {Roo.ContentPanel} panel The panel
43231 "panelremoved" : true,
43233 * @event beforecollapse
43234 * Fires when this region before collapse.
43235 * @param {Roo.LayoutRegion} this
43237 "beforecollapse" : true,
43240 * Fires when this region is collapsed.
43241 * @param {Roo.LayoutRegion} this
43243 "collapsed" : true,
43246 * Fires when this region is expanded.
43247 * @param {Roo.LayoutRegion} this
43252 * Fires when this region is slid into view.
43253 * @param {Roo.LayoutRegion} this
43255 "slideshow" : true,
43258 * Fires when this region slides out of view.
43259 * @param {Roo.LayoutRegion} this
43261 "slidehide" : true,
43263 * @event panelactivated
43264 * Fires when a panel is activated.
43265 * @param {Roo.LayoutRegion} this
43266 * @param {Roo.ContentPanel} panel The activated panel
43268 "panelactivated" : true,
43271 * Fires when the user resizes this region.
43272 * @param {Roo.LayoutRegion} this
43273 * @param {Number} newSize The new size (width for east/west, height for north/south)
43277 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43278 this.panels = new Roo.util.MixedCollection();
43279 this.panels.getKey = this.getPanelId.createDelegate(this);
43281 this.activePanel = null;
43282 // ensure listeners are added...
43284 if (config.listeners || config.events) {
43285 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43286 listeners : config.listeners || {},
43287 events : config.events || {}
43291 if(skipConfig !== true){
43292 this.applyConfig(config);
43296 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43298 getPanelId : function(p){
43302 applyConfig : function(config){
43303 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43304 this.config = config;
43309 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43310 * the width, for horizontal (north, south) the height.
43311 * @param {Number} newSize The new width or height
43313 resizeTo : function(newSize){
43314 var el = this.el ? this.el :
43315 (this.activePanel ? this.activePanel.getEl() : null);
43317 switch(this.position){
43320 el.setWidth(newSize);
43321 this.fireEvent("resized", this, newSize);
43325 el.setHeight(newSize);
43326 this.fireEvent("resized", this, newSize);
43332 getBox : function(){
43333 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43336 getMargins : function(){
43337 return this.margins;
43340 updateBox : function(box){
43342 var el = this.activePanel.getEl();
43343 el.dom.style.left = box.x + "px";
43344 el.dom.style.top = box.y + "px";
43345 this.activePanel.setSize(box.width, box.height);
43349 * Returns the container element for this region.
43350 * @return {Roo.Element}
43352 getEl : function(){
43353 return this.activePanel;
43357 * Returns true if this region is currently visible.
43358 * @return {Boolean}
43360 isVisible : function(){
43361 return this.activePanel ? true : false;
43364 setActivePanel : function(panel){
43365 panel = this.getPanel(panel);
43366 if(this.activePanel && this.activePanel != panel){
43367 this.activePanel.setActiveState(false);
43368 this.activePanel.getEl().setLeftTop(-10000,-10000);
43370 this.activePanel = panel;
43371 panel.setActiveState(true);
43373 panel.setSize(this.box.width, this.box.height);
43375 this.fireEvent("panelactivated", this, panel);
43376 this.fireEvent("invalidated");
43380 * Show the specified panel.
43381 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43382 * @return {Roo.ContentPanel} The shown panel or null
43384 showPanel : function(panel){
43385 panel = this.getPanel(panel);
43387 this.setActivePanel(panel);
43393 * Get the active panel for this region.
43394 * @return {Roo.ContentPanel} The active panel or null
43396 getActivePanel : function(){
43397 return this.activePanel;
43401 * Add the passed ContentPanel(s)
43402 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43403 * @return {Roo.ContentPanel} The panel added (if only one was added)
43405 add : function(panel){
43406 if(arguments.length > 1){
43407 for(var i = 0, len = arguments.length; i < len; i++) {
43408 this.add(arguments[i]);
43412 if(this.hasPanel(panel)){
43413 this.showPanel(panel);
43416 var el = panel.getEl();
43417 if(el.dom.parentNode != this.mgr.el.dom){
43418 this.mgr.el.dom.appendChild(el.dom);
43420 if(panel.setRegion){
43421 panel.setRegion(this);
43423 this.panels.add(panel);
43424 el.setStyle("position", "absolute");
43425 if(!panel.background){
43426 this.setActivePanel(panel);
43427 if(this.config.initialSize && this.panels.getCount()==1){
43428 this.resizeTo(this.config.initialSize);
43431 this.fireEvent("paneladded", this, panel);
43436 * Returns true if the panel is in this region.
43437 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43438 * @return {Boolean}
43440 hasPanel : function(panel){
43441 if(typeof panel == "object"){ // must be panel obj
43442 panel = panel.getId();
43444 return this.getPanel(panel) ? true : false;
43448 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43449 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43450 * @param {Boolean} preservePanel Overrides the config preservePanel option
43451 * @return {Roo.ContentPanel} The panel that was removed
43453 remove : function(panel, preservePanel){
43454 panel = this.getPanel(panel);
43459 this.fireEvent("beforeremove", this, panel, e);
43460 if(e.cancel === true){
43463 var panelId = panel.getId();
43464 this.panels.removeKey(panelId);
43469 * Returns the panel specified or null if it's not in this region.
43470 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43471 * @return {Roo.ContentPanel}
43473 getPanel : function(id){
43474 if(typeof id == "object"){ // must be panel obj
43477 return this.panels.get(id);
43481 * Returns this regions position (north/south/east/west/center).
43484 getPosition: function(){
43485 return this.position;
43489 * Ext JS Library 1.1.1
43490 * Copyright(c) 2006-2007, Ext JS, LLC.
43492 * Originally Released Under LGPL - original licence link has changed is not relivant.
43495 * <script type="text/javascript">
43499 * @class Roo.bootstrap.layout.Region
43500 * @extends Roo.bootstrap.layout.Basic
43501 * This class represents a region in a layout manager.
43503 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43504 * @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})
43505 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43506 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43507 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43508 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43509 * @cfg {String} title The title for the region (overrides panel titles)
43510 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43511 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43512 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43513 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43514 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43515 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43516 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43517 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43518 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43519 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43521 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43522 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43523 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43524 * @cfg {Number} width For East/West panels
43525 * @cfg {Number} height For North/South panels
43526 * @cfg {Boolean} split To show the splitter
43527 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43529 * @cfg {string} cls Extra CSS classes to add to region
43531 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43532 * @cfg {string} region the region that it inhabits..
43535 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43536 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43538 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43539 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43540 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43542 Roo.bootstrap.layout.Region = function(config)
43544 this.applyConfig(config);
43546 var mgr = config.mgr;
43547 var pos = config.region;
43548 config.skipConfig = true;
43549 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43552 this.onRender(mgr.el);
43555 this.visible = true;
43556 this.collapsed = false;
43557 this.unrendered_panels = [];
43560 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43562 position: '', // set by wrapper (eg. north/south etc..)
43563 unrendered_panels : null, // unrendered panels.
43565 tabPosition : false,
43567 mgr: false, // points to 'Border'
43570 createBody : function(){
43571 /** This region's body element
43572 * @type Roo.Element */
43573 this.bodyEl = this.el.createChild({
43575 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43579 onRender: function(ctr, pos)
43581 var dh = Roo.DomHelper;
43582 /** This region's container element
43583 * @type Roo.Element */
43584 this.el = dh.append(ctr.dom, {
43586 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43588 /** This region's title element
43589 * @type Roo.Element */
43591 this.titleEl = dh.append(this.el.dom, {
43593 unselectable: "on",
43594 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43596 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43597 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43601 this.titleEl.enableDisplayMode();
43602 /** This region's title text element
43603 * @type HTMLElement */
43604 this.titleTextEl = this.titleEl.dom.firstChild;
43605 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43607 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43608 this.closeBtn.enableDisplayMode();
43609 this.closeBtn.on("click", this.closeClicked, this);
43610 this.closeBtn.hide();
43612 this.createBody(this.config);
43613 if(this.config.hideWhenEmpty){
43615 this.on("paneladded", this.validateVisibility, this);
43616 this.on("panelremoved", this.validateVisibility, this);
43618 if(this.autoScroll){
43619 this.bodyEl.setStyle("overflow", "auto");
43621 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43623 //if(c.titlebar !== false){
43624 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43625 this.titleEl.hide();
43627 this.titleEl.show();
43628 if(this.config.title){
43629 this.titleTextEl.innerHTML = this.config.title;
43633 if(this.config.collapsed){
43634 this.collapse(true);
43636 if(this.config.hidden){
43640 if (this.unrendered_panels && this.unrendered_panels.length) {
43641 for (var i =0;i< this.unrendered_panels.length; i++) {
43642 this.add(this.unrendered_panels[i]);
43644 this.unrendered_panels = null;
43650 applyConfig : function(c)
43653 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43654 var dh = Roo.DomHelper;
43655 if(c.titlebar !== false){
43656 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43657 this.collapseBtn.on("click", this.collapse, this);
43658 this.collapseBtn.enableDisplayMode();
43660 if(c.showPin === true || this.showPin){
43661 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43662 this.stickBtn.enableDisplayMode();
43663 this.stickBtn.on("click", this.expand, this);
43664 this.stickBtn.hide();
43669 /** This region's collapsed element
43670 * @type Roo.Element */
43673 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43674 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43677 if(c.floatable !== false){
43678 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43679 this.collapsedEl.on("click", this.collapseClick, this);
43682 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43683 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43684 id: "message", unselectable: "on", style:{"float":"left"}});
43685 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43687 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43688 this.expandBtn.on("click", this.expand, this);
43692 if(this.collapseBtn){
43693 this.collapseBtn.setVisible(c.collapsible == true);
43696 this.cmargins = c.cmargins || this.cmargins ||
43697 (this.position == "west" || this.position == "east" ?
43698 {top: 0, left: 2, right:2, bottom: 0} :
43699 {top: 2, left: 0, right:0, bottom: 2});
43701 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43704 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43706 this.autoScroll = c.autoScroll || false;
43711 this.duration = c.duration || .30;
43712 this.slideDuration = c.slideDuration || .45;
43717 * Returns true if this region is currently visible.
43718 * @return {Boolean}
43720 isVisible : function(){
43721 return this.visible;
43725 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43726 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
43728 //setCollapsedTitle : function(title){
43729 // title = title || " ";
43730 // if(this.collapsedTitleTextEl){
43731 // this.collapsedTitleTextEl.innerHTML = title;
43735 getBox : function(){
43737 // if(!this.collapsed){
43738 b = this.el.getBox(false, true);
43740 // b = this.collapsedEl.getBox(false, true);
43745 getMargins : function(){
43746 return this.margins;
43747 //return this.collapsed ? this.cmargins : this.margins;
43750 highlight : function(){
43751 this.el.addClass("x-layout-panel-dragover");
43754 unhighlight : function(){
43755 this.el.removeClass("x-layout-panel-dragover");
43758 updateBox : function(box)
43760 if (!this.bodyEl) {
43761 return; // not rendered yet..
43765 if(!this.collapsed){
43766 this.el.dom.style.left = box.x + "px";
43767 this.el.dom.style.top = box.y + "px";
43768 this.updateBody(box.width, box.height);
43770 this.collapsedEl.dom.style.left = box.x + "px";
43771 this.collapsedEl.dom.style.top = box.y + "px";
43772 this.collapsedEl.setSize(box.width, box.height);
43775 this.tabs.autoSizeTabs();
43779 updateBody : function(w, h)
43782 this.el.setWidth(w);
43783 w -= this.el.getBorderWidth("rl");
43784 if(this.config.adjustments){
43785 w += this.config.adjustments[0];
43788 if(h !== null && h > 0){
43789 this.el.setHeight(h);
43790 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43791 h -= this.el.getBorderWidth("tb");
43792 if(this.config.adjustments){
43793 h += this.config.adjustments[1];
43795 this.bodyEl.setHeight(h);
43797 h = this.tabs.syncHeight(h);
43800 if(this.panelSize){
43801 w = w !== null ? w : this.panelSize.width;
43802 h = h !== null ? h : this.panelSize.height;
43804 if(this.activePanel){
43805 var el = this.activePanel.getEl();
43806 w = w !== null ? w : el.getWidth();
43807 h = h !== null ? h : el.getHeight();
43808 this.panelSize = {width: w, height: h};
43809 this.activePanel.setSize(w, h);
43811 if(Roo.isIE && this.tabs){
43812 this.tabs.el.repaint();
43817 * Returns the container element for this region.
43818 * @return {Roo.Element}
43820 getEl : function(){
43825 * Hides this region.
43828 //if(!this.collapsed){
43829 this.el.dom.style.left = "-2000px";
43832 // this.collapsedEl.dom.style.left = "-2000px";
43833 // this.collapsedEl.hide();
43835 this.visible = false;
43836 this.fireEvent("visibilitychange", this, false);
43840 * Shows this region if it was previously hidden.
43843 //if(!this.collapsed){
43846 // this.collapsedEl.show();
43848 this.visible = true;
43849 this.fireEvent("visibilitychange", this, true);
43852 closeClicked : function(){
43853 if(this.activePanel){
43854 this.remove(this.activePanel);
43858 collapseClick : function(e){
43860 e.stopPropagation();
43863 e.stopPropagation();
43869 * Collapses this region.
43870 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43873 collapse : function(skipAnim, skipCheck = false){
43874 if(this.collapsed) {
43878 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43880 this.collapsed = true;
43882 this.split.el.hide();
43884 if(this.config.animate && skipAnim !== true){
43885 this.fireEvent("invalidated", this);
43886 this.animateCollapse();
43888 this.el.setLocation(-20000,-20000);
43890 this.collapsedEl.show();
43891 this.fireEvent("collapsed", this);
43892 this.fireEvent("invalidated", this);
43898 animateCollapse : function(){
43903 * Expands this region if it was previously collapsed.
43904 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43905 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43908 expand : function(e, skipAnim){
43910 e.stopPropagation();
43912 if(!this.collapsed || this.el.hasActiveFx()) {
43916 this.afterSlideIn();
43919 this.collapsed = false;
43920 if(this.config.animate && skipAnim !== true){
43921 this.animateExpand();
43925 this.split.el.show();
43927 this.collapsedEl.setLocation(-2000,-2000);
43928 this.collapsedEl.hide();
43929 this.fireEvent("invalidated", this);
43930 this.fireEvent("expanded", this);
43934 animateExpand : function(){
43938 initTabs : function()
43940 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43942 var ts = new Roo.bootstrap.panel.Tabs({
43943 el: this.bodyEl.dom,
43945 tabPosition: this.tabPosition ? this.tabPosition : 'top',
43946 disableTooltips: this.config.disableTabTips,
43947 toolbar : this.config.toolbar
43950 if(this.config.hideTabs){
43951 ts.stripWrap.setDisplayed(false);
43954 ts.resizeTabs = this.config.resizeTabs === true;
43955 ts.minTabWidth = this.config.minTabWidth || 40;
43956 ts.maxTabWidth = this.config.maxTabWidth || 250;
43957 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43958 ts.monitorResize = false;
43959 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43960 ts.bodyEl.addClass('roo-layout-tabs-body');
43961 this.panels.each(this.initPanelAsTab, this);
43964 initPanelAsTab : function(panel){
43965 var ti = this.tabs.addTab(
43969 this.config.closeOnTab && panel.isClosable(),
43972 if(panel.tabTip !== undefined){
43973 ti.setTooltip(panel.tabTip);
43975 ti.on("activate", function(){
43976 this.setActivePanel(panel);
43979 if(this.config.closeOnTab){
43980 ti.on("beforeclose", function(t, e){
43982 this.remove(panel);
43986 panel.tabItem = ti;
43991 updatePanelTitle : function(panel, title)
43993 if(this.activePanel == panel){
43994 this.updateTitle(title);
43997 var ti = this.tabs.getTab(panel.getEl().id);
43999 if(panel.tabTip !== undefined){
44000 ti.setTooltip(panel.tabTip);
44005 updateTitle : function(title){
44006 if(this.titleTextEl && !this.config.title){
44007 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44011 setActivePanel : function(panel)
44013 panel = this.getPanel(panel);
44014 if(this.activePanel && this.activePanel != panel){
44015 if(this.activePanel.setActiveState(false) === false){
44019 this.activePanel = panel;
44020 panel.setActiveState(true);
44021 if(this.panelSize){
44022 panel.setSize(this.panelSize.width, this.panelSize.height);
44025 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44027 this.updateTitle(panel.getTitle());
44029 this.fireEvent("invalidated", this);
44031 this.fireEvent("panelactivated", this, panel);
44035 * Shows the specified panel.
44036 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44037 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44039 showPanel : function(panel)
44041 panel = this.getPanel(panel);
44044 var tab = this.tabs.getTab(panel.getEl().id);
44045 if(tab.isHidden()){
44046 this.tabs.unhideTab(tab.id);
44050 this.setActivePanel(panel);
44057 * Get the active panel for this region.
44058 * @return {Roo.ContentPanel} The active panel or null
44060 getActivePanel : function(){
44061 return this.activePanel;
44064 validateVisibility : function(){
44065 if(this.panels.getCount() < 1){
44066 this.updateTitle(" ");
44067 this.closeBtn.hide();
44070 if(!this.isVisible()){
44077 * Adds the passed ContentPanel(s) to this region.
44078 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44079 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44081 add : function(panel)
44083 if(arguments.length > 1){
44084 for(var i = 0, len = arguments.length; i < len; i++) {
44085 this.add(arguments[i]);
44090 // if we have not been rendered yet, then we can not really do much of this..
44091 if (!this.bodyEl) {
44092 this.unrendered_panels.push(panel);
44099 if(this.hasPanel(panel)){
44100 this.showPanel(panel);
44103 panel.setRegion(this);
44104 this.panels.add(panel);
44105 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44106 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44107 // and hide them... ???
44108 this.bodyEl.dom.appendChild(panel.getEl().dom);
44109 if(panel.background !== true){
44110 this.setActivePanel(panel);
44112 this.fireEvent("paneladded", this, panel);
44119 this.initPanelAsTab(panel);
44123 if(panel.background !== true){
44124 this.tabs.activate(panel.getEl().id);
44126 this.fireEvent("paneladded", this, panel);
44131 * Hides the tab for the specified panel.
44132 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44134 hidePanel : function(panel){
44135 if(this.tabs && (panel = this.getPanel(panel))){
44136 this.tabs.hideTab(panel.getEl().id);
44141 * Unhides the tab for a previously hidden panel.
44142 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44144 unhidePanel : function(panel){
44145 if(this.tabs && (panel = this.getPanel(panel))){
44146 this.tabs.unhideTab(panel.getEl().id);
44150 clearPanels : function(){
44151 while(this.panels.getCount() > 0){
44152 this.remove(this.panels.first());
44157 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44158 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44159 * @param {Boolean} preservePanel Overrides the config preservePanel option
44160 * @return {Roo.ContentPanel} The panel that was removed
44162 remove : function(panel, preservePanel)
44164 panel = this.getPanel(panel);
44169 this.fireEvent("beforeremove", this, panel, e);
44170 if(e.cancel === true){
44173 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44174 var panelId = panel.getId();
44175 this.panels.removeKey(panelId);
44177 document.body.appendChild(panel.getEl().dom);
44180 this.tabs.removeTab(panel.getEl().id);
44181 }else if (!preservePanel){
44182 this.bodyEl.dom.removeChild(panel.getEl().dom);
44184 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44185 var p = this.panels.first();
44186 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44187 tempEl.appendChild(p.getEl().dom);
44188 this.bodyEl.update("");
44189 this.bodyEl.dom.appendChild(p.getEl().dom);
44191 this.updateTitle(p.getTitle());
44193 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44194 this.setActivePanel(p);
44196 panel.setRegion(null);
44197 if(this.activePanel == panel){
44198 this.activePanel = null;
44200 if(this.config.autoDestroy !== false && preservePanel !== true){
44201 try{panel.destroy();}catch(e){}
44203 this.fireEvent("panelremoved", this, panel);
44208 * Returns the TabPanel component used by this region
44209 * @return {Roo.TabPanel}
44211 getTabs : function(){
44215 createTool : function(parentEl, className){
44216 var btn = Roo.DomHelper.append(parentEl, {
44218 cls: "x-layout-tools-button",
44221 cls: "roo-layout-tools-button-inner " + className,
44225 btn.addClassOnOver("roo-layout-tools-button-over");
44230 * Ext JS Library 1.1.1
44231 * Copyright(c) 2006-2007, Ext JS, LLC.
44233 * Originally Released Under LGPL - original licence link has changed is not relivant.
44236 * <script type="text/javascript">
44242 * @class Roo.SplitLayoutRegion
44243 * @extends Roo.LayoutRegion
44244 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44246 Roo.bootstrap.layout.Split = function(config){
44247 this.cursor = config.cursor;
44248 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44251 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44253 splitTip : "Drag to resize.",
44254 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44255 useSplitTips : false,
44257 applyConfig : function(config){
44258 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44261 onRender : function(ctr,pos) {
44263 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44264 if(!this.config.split){
44269 var splitEl = Roo.DomHelper.append(ctr.dom, {
44271 id: this.el.id + "-split",
44272 cls: "roo-layout-split roo-layout-split-"+this.position,
44275 /** The SplitBar for this region
44276 * @type Roo.SplitBar */
44277 // does not exist yet...
44278 Roo.log([this.position, this.orientation]);
44280 this.split = new Roo.bootstrap.SplitBar({
44281 dragElement : splitEl,
44282 resizingElement: this.el,
44283 orientation : this.orientation
44286 this.split.on("moved", this.onSplitMove, this);
44287 this.split.useShim = this.config.useShim === true;
44288 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44289 if(this.useSplitTips){
44290 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44292 //if(config.collapsible){
44293 // this.split.el.on("dblclick", this.collapse, this);
44296 if(typeof this.config.minSize != "undefined"){
44297 this.split.minSize = this.config.minSize;
44299 if(typeof this.config.maxSize != "undefined"){
44300 this.split.maxSize = this.config.maxSize;
44302 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44303 this.hideSplitter();
44308 getHMaxSize : function(){
44309 var cmax = this.config.maxSize || 10000;
44310 var center = this.mgr.getRegion("center");
44311 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44314 getVMaxSize : function(){
44315 var cmax = this.config.maxSize || 10000;
44316 var center = this.mgr.getRegion("center");
44317 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44320 onSplitMove : function(split, newSize){
44321 this.fireEvent("resized", this, newSize);
44325 * Returns the {@link Roo.SplitBar} for this region.
44326 * @return {Roo.SplitBar}
44328 getSplitBar : function(){
44333 this.hideSplitter();
44334 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44337 hideSplitter : function(){
44339 this.split.el.setLocation(-2000,-2000);
44340 this.split.el.hide();
44346 this.split.el.show();
44348 Roo.bootstrap.layout.Split.superclass.show.call(this);
44351 beforeSlide: function(){
44352 if(Roo.isGecko){// firefox overflow auto bug workaround
44353 this.bodyEl.clip();
44355 this.tabs.bodyEl.clip();
44357 if(this.activePanel){
44358 this.activePanel.getEl().clip();
44360 if(this.activePanel.beforeSlide){
44361 this.activePanel.beforeSlide();
44367 afterSlide : function(){
44368 if(Roo.isGecko){// firefox overflow auto bug workaround
44369 this.bodyEl.unclip();
44371 this.tabs.bodyEl.unclip();
44373 if(this.activePanel){
44374 this.activePanel.getEl().unclip();
44375 if(this.activePanel.afterSlide){
44376 this.activePanel.afterSlide();
44382 initAutoHide : function(){
44383 if(this.autoHide !== false){
44384 if(!this.autoHideHd){
44385 var st = new Roo.util.DelayedTask(this.slideIn, this);
44386 this.autoHideHd = {
44387 "mouseout": function(e){
44388 if(!e.within(this.el, true)){
44392 "mouseover" : function(e){
44398 this.el.on(this.autoHideHd);
44402 clearAutoHide : function(){
44403 if(this.autoHide !== false){
44404 this.el.un("mouseout", this.autoHideHd.mouseout);
44405 this.el.un("mouseover", this.autoHideHd.mouseover);
44409 clearMonitor : function(){
44410 Roo.get(document).un("click", this.slideInIf, this);
44413 // these names are backwards but not changed for compat
44414 slideOut : function(){
44415 if(this.isSlid || this.el.hasActiveFx()){
44418 this.isSlid = true;
44419 if(this.collapseBtn){
44420 this.collapseBtn.hide();
44422 this.closeBtnState = this.closeBtn.getStyle('display');
44423 this.closeBtn.hide();
44425 this.stickBtn.show();
44428 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44429 this.beforeSlide();
44430 this.el.setStyle("z-index", 10001);
44431 this.el.slideIn(this.getSlideAnchor(), {
44432 callback: function(){
44434 this.initAutoHide();
44435 Roo.get(document).on("click", this.slideInIf, this);
44436 this.fireEvent("slideshow", this);
44443 afterSlideIn : function(){
44444 this.clearAutoHide();
44445 this.isSlid = false;
44446 this.clearMonitor();
44447 this.el.setStyle("z-index", "");
44448 if(this.collapseBtn){
44449 this.collapseBtn.show();
44451 this.closeBtn.setStyle('display', this.closeBtnState);
44453 this.stickBtn.hide();
44455 this.fireEvent("slidehide", this);
44458 slideIn : function(cb){
44459 if(!this.isSlid || this.el.hasActiveFx()){
44463 this.isSlid = false;
44464 this.beforeSlide();
44465 this.el.slideOut(this.getSlideAnchor(), {
44466 callback: function(){
44467 this.el.setLeftTop(-10000, -10000);
44469 this.afterSlideIn();
44477 slideInIf : function(e){
44478 if(!e.within(this.el)){
44483 animateCollapse : function(){
44484 this.beforeSlide();
44485 this.el.setStyle("z-index", 20000);
44486 var anchor = this.getSlideAnchor();
44487 this.el.slideOut(anchor, {
44488 callback : function(){
44489 this.el.setStyle("z-index", "");
44490 this.collapsedEl.slideIn(anchor, {duration:.3});
44492 this.el.setLocation(-10000,-10000);
44494 this.fireEvent("collapsed", this);
44501 animateExpand : function(){
44502 this.beforeSlide();
44503 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44504 this.el.setStyle("z-index", 20000);
44505 this.collapsedEl.hide({
44508 this.el.slideIn(this.getSlideAnchor(), {
44509 callback : function(){
44510 this.el.setStyle("z-index", "");
44513 this.split.el.show();
44515 this.fireEvent("invalidated", this);
44516 this.fireEvent("expanded", this);
44544 getAnchor : function(){
44545 return this.anchors[this.position];
44548 getCollapseAnchor : function(){
44549 return this.canchors[this.position];
44552 getSlideAnchor : function(){
44553 return this.sanchors[this.position];
44556 getAlignAdj : function(){
44557 var cm = this.cmargins;
44558 switch(this.position){
44574 getExpandAdj : function(){
44575 var c = this.collapsedEl, cm = this.cmargins;
44576 switch(this.position){
44578 return [-(cm.right+c.getWidth()+cm.left), 0];
44581 return [cm.right+c.getWidth()+cm.left, 0];
44584 return [0, -(cm.top+cm.bottom+c.getHeight())];
44587 return [0, cm.top+cm.bottom+c.getHeight()];
44593 * Ext JS Library 1.1.1
44594 * Copyright(c) 2006-2007, Ext JS, LLC.
44596 * Originally Released Under LGPL - original licence link has changed is not relivant.
44599 * <script type="text/javascript">
44602 * These classes are private internal classes
44604 Roo.bootstrap.layout.Center = function(config){
44605 config.region = "center";
44606 Roo.bootstrap.layout.Region.call(this, config);
44607 this.visible = true;
44608 this.minWidth = config.minWidth || 20;
44609 this.minHeight = config.minHeight || 20;
44612 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44614 // center panel can't be hidden
44618 // center panel can't be hidden
44621 getMinWidth: function(){
44622 return this.minWidth;
44625 getMinHeight: function(){
44626 return this.minHeight;
44640 Roo.bootstrap.layout.North = function(config)
44642 config.region = 'north';
44643 config.cursor = 'n-resize';
44645 Roo.bootstrap.layout.Split.call(this, config);
44649 this.split.placement = Roo.bootstrap.SplitBar.TOP;
44650 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44651 this.split.el.addClass("roo-layout-split-v");
44653 //var size = config.initialSize || config.height;
44654 //if(this.el && typeof size != "undefined"){
44655 // this.el.setHeight(size);
44658 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44660 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44663 onRender : function(ctr, pos)
44665 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44666 var size = this.config.initialSize || this.config.height;
44667 if(this.el && typeof size != "undefined"){
44668 this.el.setHeight(size);
44673 getBox : function(){
44674 if(this.collapsed){
44675 return this.collapsedEl.getBox();
44677 var box = this.el.getBox();
44679 box.height += this.split.el.getHeight();
44684 updateBox : function(box){
44685 if(this.split && !this.collapsed){
44686 box.height -= this.split.el.getHeight();
44687 this.split.el.setLeft(box.x);
44688 this.split.el.setTop(box.y+box.height);
44689 this.split.el.setWidth(box.width);
44691 if(this.collapsed){
44692 this.updateBody(box.width, null);
44694 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44702 Roo.bootstrap.layout.South = function(config){
44703 config.region = 'south';
44704 config.cursor = 's-resize';
44705 Roo.bootstrap.layout.Split.call(this, config);
44707 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44708 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44709 this.split.el.addClass("roo-layout-split-v");
44714 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44715 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44717 onRender : function(ctr, pos)
44719 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44720 var size = this.config.initialSize || this.config.height;
44721 if(this.el && typeof size != "undefined"){
44722 this.el.setHeight(size);
44727 getBox : function(){
44728 if(this.collapsed){
44729 return this.collapsedEl.getBox();
44731 var box = this.el.getBox();
44733 var sh = this.split.el.getHeight();
44740 updateBox : function(box){
44741 if(this.split && !this.collapsed){
44742 var sh = this.split.el.getHeight();
44745 this.split.el.setLeft(box.x);
44746 this.split.el.setTop(box.y-sh);
44747 this.split.el.setWidth(box.width);
44749 if(this.collapsed){
44750 this.updateBody(box.width, null);
44752 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44756 Roo.bootstrap.layout.East = function(config){
44757 config.region = "east";
44758 config.cursor = "e-resize";
44759 Roo.bootstrap.layout.Split.call(this, config);
44761 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44762 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44763 this.split.el.addClass("roo-layout-split-h");
44767 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44768 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44770 onRender : function(ctr, pos)
44772 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44773 var size = this.config.initialSize || this.config.width;
44774 if(this.el && typeof size != "undefined"){
44775 this.el.setWidth(size);
44780 getBox : function(){
44781 if(this.collapsed){
44782 return this.collapsedEl.getBox();
44784 var box = this.el.getBox();
44786 var sw = this.split.el.getWidth();
44793 updateBox : function(box){
44794 if(this.split && !this.collapsed){
44795 var sw = this.split.el.getWidth();
44797 this.split.el.setLeft(box.x);
44798 this.split.el.setTop(box.y);
44799 this.split.el.setHeight(box.height);
44802 if(this.collapsed){
44803 this.updateBody(null, box.height);
44805 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44809 Roo.bootstrap.layout.West = function(config){
44810 config.region = "west";
44811 config.cursor = "w-resize";
44813 Roo.bootstrap.layout.Split.call(this, config);
44815 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44816 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44817 this.split.el.addClass("roo-layout-split-h");
44821 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44822 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44824 onRender: function(ctr, pos)
44826 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44827 var size = this.config.initialSize || this.config.width;
44828 if(typeof size != "undefined"){
44829 this.el.setWidth(size);
44833 getBox : function(){
44834 if(this.collapsed){
44835 return this.collapsedEl.getBox();
44837 var box = this.el.getBox();
44838 if (box.width == 0) {
44839 box.width = this.config.width; // kludge?
44842 box.width += this.split.el.getWidth();
44847 updateBox : function(box){
44848 if(this.split && !this.collapsed){
44849 var sw = this.split.el.getWidth();
44851 this.split.el.setLeft(box.x+box.width);
44852 this.split.el.setTop(box.y);
44853 this.split.el.setHeight(box.height);
44855 if(this.collapsed){
44856 this.updateBody(null, box.height);
44858 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44862 * Ext JS Library 1.1.1
44863 * Copyright(c) 2006-2007, Ext JS, LLC.
44865 * Originally Released Under LGPL - original licence link has changed is not relivant.
44868 * <script type="text/javascript">
44871 * @class Roo.bootstrap.paenl.Content
44872 * @extends Roo.util.Observable
44873 * @children Roo.bootstrap.Component
44874 * @parent builder Roo.bootstrap.layout.Border
44875 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44876 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
44877 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
44878 * @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
44879 * @cfg {Boolean} closable True if the panel can be closed/removed
44880 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44881 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44882 * @cfg {Toolbar} toolbar A toolbar for this panel
44883 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44884 * @cfg {String} title The title for this panel
44885 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44886 * @cfg {String} url Calls {@link #setUrl} with this value
44887 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44888 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44889 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44890 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
44891 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
44892 * @cfg {Boolean} badges render the badges
44893 * @cfg {String} cls extra classes to use
44894 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44897 * Create a new ContentPanel.
44898 * @param {String/Object} config A string to set only the title or a config object
44901 Roo.bootstrap.panel.Content = function( config){
44903 this.tpl = config.tpl || false;
44905 var el = config.el;
44906 var content = config.content;
44908 if(config.autoCreate){ // xtype is available if this is called from factory
44911 this.el = Roo.get(el);
44912 if(!this.el && config && config.autoCreate){
44913 if(typeof config.autoCreate == "object"){
44914 if(!config.autoCreate.id){
44915 config.autoCreate.id = config.id||el;
44917 this.el = Roo.DomHelper.append(document.body,
44918 config.autoCreate, true);
44922 cls: (config.cls || '') +
44923 (config.background ? ' bg-' + config.background : '') +
44924 " roo-layout-inactive-content",
44927 if (config.iframe) {
44931 style : 'border: 0px',
44932 src : 'about:blank'
44938 elcfg.html = config.html;
44942 this.el = Roo.DomHelper.append(document.body, elcfg , true);
44943 if (config.iframe) {
44944 this.iframeEl = this.el.select('iframe',true).first();
44949 this.closable = false;
44950 this.loaded = false;
44951 this.active = false;
44954 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44956 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44958 this.wrapEl = this.el; //this.el.wrap();
44960 if (config.toolbar.items) {
44961 ti = config.toolbar.items ;
44962 delete config.toolbar.items ;
44966 this.toolbar.render(this.wrapEl, 'before');
44967 for(var i =0;i < ti.length;i++) {
44968 // Roo.log(['add child', items[i]]);
44969 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44971 this.toolbar.items = nitems;
44972 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44973 delete config.toolbar;
44977 // xtype created footer. - not sure if will work as we normally have to render first..
44978 if (this.footer && !this.footer.el && this.footer.xtype) {
44979 if (!this.wrapEl) {
44980 this.wrapEl = this.el.wrap();
44983 this.footer.container = this.wrapEl.createChild();
44985 this.footer = Roo.factory(this.footer, Roo);
44990 if(typeof config == "string"){
44991 this.title = config;
44993 Roo.apply(this, config);
44997 this.resizeEl = Roo.get(this.resizeEl, true);
44999 this.resizeEl = this.el;
45001 // handle view.xtype
45009 * Fires when this panel is activated.
45010 * @param {Roo.ContentPanel} this
45014 * @event deactivate
45015 * Fires when this panel is activated.
45016 * @param {Roo.ContentPanel} this
45018 "deactivate" : true,
45022 * Fires when this panel is resized if fitToFrame is true.
45023 * @param {Roo.ContentPanel} this
45024 * @param {Number} width The width after any component adjustments
45025 * @param {Number} height The height after any component adjustments
45031 * Fires when this tab is created
45032 * @param {Roo.ContentPanel} this
45038 * Fires when this content is scrolled
45039 * @param {Roo.ContentPanel} this
45040 * @param {Event} scrollEvent
45051 if(this.autoScroll && !this.iframe){
45052 this.resizeEl.setStyle("overflow", "auto");
45053 this.resizeEl.on('scroll', this.onScroll, this);
45055 // fix randome scrolling
45056 //this.el.on('scroll', function() {
45057 // Roo.log('fix random scolling');
45058 // this.scrollTo('top',0);
45061 content = content || this.content;
45063 this.setContent(content);
45065 if(config && config.url){
45066 this.setUrl(this.url, this.params, this.loadOnce);
45071 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45073 if (this.view && typeof(this.view.xtype) != 'undefined') {
45074 this.view.el = this.el.appendChild(document.createElement("div"));
45075 this.view = Roo.factory(this.view);
45076 this.view.render && this.view.render(false, '');
45080 this.fireEvent('render', this);
45083 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45093 /* Resize Element - use this to work out scroll etc. */
45096 setRegion : function(region){
45097 this.region = region;
45098 this.setActiveClass(region && !this.background);
45102 setActiveClass: function(state)
45105 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45106 this.el.setStyle('position','relative');
45108 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45109 this.el.setStyle('position', 'absolute');
45114 * Returns the toolbar for this Panel if one was configured.
45115 * @return {Roo.Toolbar}
45117 getToolbar : function(){
45118 return this.toolbar;
45121 setActiveState : function(active)
45123 this.active = active;
45124 this.setActiveClass(active);
45126 if(this.fireEvent("deactivate", this) === false){
45131 this.fireEvent("activate", this);
45135 * Updates this panel's element (not for iframe)
45136 * @param {String} content The new content
45137 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45139 setContent : function(content, loadScripts){
45144 this.el.update(content, loadScripts);
45147 ignoreResize : function(w, h)
45149 //return false; // always resize?
45150 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45153 this.lastSize = {width: w, height: h};
45158 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45159 * @return {Roo.UpdateManager} The UpdateManager
45161 getUpdateManager : function(){
45165 return this.el.getUpdateManager();
45168 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45169 * Does not work with IFRAME contents
45170 * @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:
45173 url: "your-url.php",
45174 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45175 callback: yourFunction,
45176 scope: yourObject, //(optional scope)
45179 text: "Loading...",
45185 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45186 * 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.
45187 * @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}
45188 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45189 * @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.
45190 * @return {Roo.ContentPanel} this
45198 var um = this.el.getUpdateManager();
45199 um.update.apply(um, arguments);
45205 * 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.
45206 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45207 * @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)
45208 * @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)
45209 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45211 setUrl : function(url, params, loadOnce){
45213 this.iframeEl.dom.src = url;
45217 if(this.refreshDelegate){
45218 this.removeListener("activate", this.refreshDelegate);
45220 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45221 this.on("activate", this.refreshDelegate);
45222 return this.el.getUpdateManager();
45225 _handleRefresh : function(url, params, loadOnce){
45226 if(!loadOnce || !this.loaded){
45227 var updater = this.el.getUpdateManager();
45228 updater.update(url, params, this._setLoaded.createDelegate(this));
45232 _setLoaded : function(){
45233 this.loaded = true;
45237 * Returns this panel's id
45240 getId : function(){
45245 * Returns this panel's element - used by regiosn to add.
45246 * @return {Roo.Element}
45248 getEl : function(){
45249 return this.wrapEl || this.el;
45254 adjustForComponents : function(width, height)
45256 //Roo.log('adjustForComponents ');
45257 if(this.resizeEl != this.el){
45258 width -= this.el.getFrameWidth('lr');
45259 height -= this.el.getFrameWidth('tb');
45262 var te = this.toolbar.getEl();
45263 te.setWidth(width);
45264 height -= te.getHeight();
45267 var te = this.footer.getEl();
45268 te.setWidth(width);
45269 height -= te.getHeight();
45273 if(this.adjustments){
45274 width += this.adjustments[0];
45275 height += this.adjustments[1];
45277 return {"width": width, "height": height};
45280 setSize : function(width, height){
45281 if(this.fitToFrame && !this.ignoreResize(width, height)){
45282 if(this.fitContainer && this.resizeEl != this.el){
45283 this.el.setSize(width, height);
45285 var size = this.adjustForComponents(width, height);
45287 this.iframeEl.setSize(width,height);
45290 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45291 this.fireEvent('resize', this, size.width, size.height);
45298 * Returns this panel's title
45301 getTitle : function(){
45303 if (typeof(this.title) != 'object') {
45308 for (var k in this.title) {
45309 if (!this.title.hasOwnProperty(k)) {
45313 if (k.indexOf('-') >= 0) {
45314 var s = k.split('-');
45315 for (var i = 0; i<s.length; i++) {
45316 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45319 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45326 * Set this panel's title
45327 * @param {String} title
45329 setTitle : function(title){
45330 this.title = title;
45332 this.region.updatePanelTitle(this, title);
45337 * Returns true is this panel was configured to be closable
45338 * @return {Boolean}
45340 isClosable : function(){
45341 return this.closable;
45344 beforeSlide : function(){
45346 this.resizeEl.clip();
45349 afterSlide : function(){
45351 this.resizeEl.unclip();
45355 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45356 * Will fail silently if the {@link #setUrl} method has not been called.
45357 * This does not activate the panel, just updates its content.
45359 refresh : function(){
45360 if(this.refreshDelegate){
45361 this.loaded = false;
45362 this.refreshDelegate();
45367 * Destroys this panel
45369 destroy : function(){
45370 this.el.removeAllListeners();
45371 var tempEl = document.createElement("span");
45372 tempEl.appendChild(this.el.dom);
45373 tempEl.innerHTML = "";
45379 * form - if the content panel contains a form - this is a reference to it.
45380 * @type {Roo.form.Form}
45384 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45385 * This contains a reference to it.
45391 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45401 * @param {Object} cfg Xtype definition of item to add.
45405 getChildContainer: function () {
45406 return this.getEl();
45410 onScroll : function(e)
45412 this.fireEvent('scroll', this, e);
45417 var ret = new Roo.factory(cfg);
45422 if (cfg.xtype.match(/^Form$/)) {
45425 //if (this.footer) {
45426 // el = this.footer.container.insertSibling(false, 'before');
45428 el = this.el.createChild();
45431 this.form = new Roo.form.Form(cfg);
45434 if ( this.form.allItems.length) {
45435 this.form.render(el.dom);
45439 // should only have one of theses..
45440 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45441 // views.. should not be just added - used named prop 'view''
45443 cfg.el = this.el.appendChild(document.createElement("div"));
45446 var ret = new Roo.factory(cfg);
45448 ret.render && ret.render(false, ''); // render blank..
45458 * @class Roo.bootstrap.panel.Grid
45459 * @extends Roo.bootstrap.panel.Content
45461 * Create a new GridPanel.
45462 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45463 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45464 * @param {Object} config A the config object
45470 Roo.bootstrap.panel.Grid = function(config)
45474 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45475 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45477 config.el = this.wrapper;
45478 //this.el = this.wrapper;
45480 if (config.container) {
45481 // ctor'ed from a Border/panel.grid
45484 this.wrapper.setStyle("overflow", "hidden");
45485 this.wrapper.addClass('roo-grid-container');
45490 if(config.toolbar){
45491 var tool_el = this.wrapper.createChild();
45492 this.toolbar = Roo.factory(config.toolbar);
45494 if (config.toolbar.items) {
45495 ti = config.toolbar.items ;
45496 delete config.toolbar.items ;
45500 this.toolbar.render(tool_el);
45501 for(var i =0;i < ti.length;i++) {
45502 // Roo.log(['add child', items[i]]);
45503 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45505 this.toolbar.items = nitems;
45507 delete config.toolbar;
45510 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45511 config.grid.scrollBody = true;;
45512 config.grid.monitorWindowResize = false; // turn off autosizing
45513 config.grid.autoHeight = false;
45514 config.grid.autoWidth = false;
45516 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45518 if (config.background) {
45519 // render grid on panel activation (if panel background)
45520 this.on('activate', function(gp) {
45521 if (!gp.grid.rendered) {
45522 gp.grid.render(this.wrapper);
45523 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45528 this.grid.render(this.wrapper);
45529 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45532 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45533 // ??? needed ??? config.el = this.wrapper;
45538 // xtype created footer. - not sure if will work as we normally have to render first..
45539 if (this.footer && !this.footer.el && this.footer.xtype) {
45541 var ctr = this.grid.getView().getFooterPanel(true);
45542 this.footer.dataSource = this.grid.dataSource;
45543 this.footer = Roo.factory(this.footer, Roo);
45544 this.footer.render(ctr);
45554 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45557 getId : function(){
45558 return this.grid.id;
45562 * Returns the grid for this panel
45563 * @return {Roo.bootstrap.Table}
45565 getGrid : function(){
45569 setSize : function(width, height)
45572 //if(!this.ignoreResize(width, height)){
45573 var grid = this.grid;
45574 var size = this.adjustForComponents(width, height);
45575 // tfoot is not a footer?
45578 var gridel = grid.getGridEl();
45579 gridel.setSize(size.width, size.height);
45581 var tbd = grid.getGridEl().select('tbody', true).first();
45582 var thd = grid.getGridEl().select('thead',true).first();
45583 var tbf= grid.getGridEl().select('tfoot', true).first();
45586 size.height -= tbf.getHeight();
45589 size.height -= thd.getHeight();
45592 tbd.setSize(size.width, size.height );
45593 // this is for the account management tab -seems to work there.
45594 var thd = grid.getGridEl().select('thead',true).first();
45596 // tbd.setSize(size.width, size.height - thd.getHeight());
45606 beforeSlide : function(){
45607 this.grid.getView().scroller.clip();
45610 afterSlide : function(){
45611 this.grid.getView().scroller.unclip();
45614 destroy : function(){
45615 this.grid.destroy();
45617 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45622 * @class Roo.bootstrap.panel.Nest
45623 * @extends Roo.bootstrap.panel.Content
45625 * Create a new Panel, that can contain a layout.Border.
45628 * @param {String/Object} config A string to set only the title or a config object
45630 Roo.bootstrap.panel.Nest = function(config)
45632 // construct with only one argument..
45633 /* FIXME - implement nicer consturctors
45634 if (layout.layout) {
45636 layout = config.layout;
45637 delete config.layout;
45639 if (layout.xtype && !layout.getEl) {
45640 // then layout needs constructing..
45641 layout = Roo.factory(layout, Roo);
45645 config.el = config.layout.getEl();
45647 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45649 config.layout.monitorWindowResize = false; // turn off autosizing
45650 this.layout = config.layout;
45651 this.layout.getEl().addClass("roo-layout-nested-layout");
45652 this.layout.parent = this;
45659 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45661 * @cfg {Roo.BorderLayout} layout The layout for this panel
45665 setSize : function(width, height){
45666 if(!this.ignoreResize(width, height)){
45667 var size = this.adjustForComponents(width, height);
45668 var el = this.layout.getEl();
45669 if (size.height < 1) {
45670 el.setWidth(size.width);
45672 el.setSize(size.width, size.height);
45674 var touch = el.dom.offsetWidth;
45675 this.layout.layout();
45676 // ie requires a double layout on the first pass
45677 if(Roo.isIE && !this.initialized){
45678 this.initialized = true;
45679 this.layout.layout();
45684 // activate all subpanels if not currently active..
45686 setActiveState : function(active){
45687 this.active = active;
45688 this.setActiveClass(active);
45691 this.fireEvent("deactivate", this);
45695 this.fireEvent("activate", this);
45696 // not sure if this should happen before or after..
45697 if (!this.layout) {
45698 return; // should not happen..
45701 for (var r in this.layout.regions) {
45702 reg = this.layout.getRegion(r);
45703 if (reg.getActivePanel()) {
45704 //reg.showPanel(reg.getActivePanel()); // force it to activate..
45705 reg.setActivePanel(reg.getActivePanel());
45708 if (!reg.panels.length) {
45711 reg.showPanel(reg.getPanel(0));
45720 * Returns the nested BorderLayout for this panel
45721 * @return {Roo.BorderLayout}
45723 getLayout : function(){
45724 return this.layout;
45728 * Adds a xtype elements to the layout of the nested panel
45732 xtype : 'ContentPanel',
45739 xtype : 'NestedLayoutPanel',
45745 items : [ ... list of content panels or nested layout panels.. ]
45749 * @param {Object} cfg Xtype definition of item to add.
45751 addxtype : function(cfg) {
45752 return this.layout.addxtype(cfg);
45757 * Ext JS Library 1.1.1
45758 * Copyright(c) 2006-2007, Ext JS, LLC.
45760 * Originally Released Under LGPL - original licence link has changed is not relivant.
45763 * <script type="text/javascript">
45766 * @class Roo.TabPanel
45767 * @extends Roo.util.Observable
45768 * A lightweight tab container.
45772 // basic tabs 1, built from existing content
45773 var tabs = new Roo.TabPanel("tabs1");
45774 tabs.addTab("script", "View Script");
45775 tabs.addTab("markup", "View Markup");
45776 tabs.activate("script");
45778 // more advanced tabs, built from javascript
45779 var jtabs = new Roo.TabPanel("jtabs");
45780 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45782 // set up the UpdateManager
45783 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45784 var updater = tab2.getUpdateManager();
45785 updater.setDefaultUrl("ajax1.htm");
45786 tab2.on('activate', updater.refresh, updater, true);
45788 // Use setUrl for Ajax loading
45789 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45790 tab3.setUrl("ajax2.htm", null, true);
45793 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45796 jtabs.activate("jtabs-1");
45799 * Create a new TabPanel.
45800 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45801 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45803 Roo.bootstrap.panel.Tabs = function(config){
45805 * The container element for this TabPanel.
45806 * @type Roo.Element
45808 this.el = Roo.get(config.el);
45811 if(typeof config == "boolean"){
45812 this.tabPosition = config ? "bottom" : "top";
45814 Roo.apply(this, config);
45818 if(this.tabPosition == "bottom"){
45819 // if tabs are at the bottom = create the body first.
45820 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45821 this.el.addClass("roo-tabs-bottom");
45823 // next create the tabs holders
45825 if (this.tabPosition == "west"){
45827 var reg = this.region; // fake it..
45829 if (!reg.mgr.parent) {
45832 reg = reg.mgr.parent.region;
45834 Roo.log("got nest?");
45836 if (reg.mgr.getRegion('west')) {
45837 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45838 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45839 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45840 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45841 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45849 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45850 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45851 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45852 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45857 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45860 // finally - if tabs are at the top, then create the body last..
45861 if(this.tabPosition != "bottom"){
45862 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45863 * @type Roo.Element
45865 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45866 this.el.addClass("roo-tabs-top");
45870 this.bodyEl.setStyle("position", "relative");
45872 this.active = null;
45873 this.activateDelegate = this.activate.createDelegate(this);
45878 * Fires when the active tab changes
45879 * @param {Roo.TabPanel} this
45880 * @param {Roo.TabPanelItem} activePanel The new active tab
45884 * @event beforetabchange
45885 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45886 * @param {Roo.TabPanel} this
45887 * @param {Object} e Set cancel to true on this object to cancel the tab change
45888 * @param {Roo.TabPanelItem} tab The tab being changed to
45890 "beforetabchange" : true
45893 Roo.EventManager.onWindowResize(this.onResize, this);
45894 this.cpad = this.el.getPadding("lr");
45895 this.hiddenCount = 0;
45898 // toolbar on the tabbar support...
45899 if (this.toolbar) {
45900 alert("no toolbar support yet");
45901 this.toolbar = false;
45903 var tcfg = this.toolbar;
45904 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
45905 this.toolbar = new Roo.Toolbar(tcfg);
45906 if (Roo.isSafari) {
45907 var tbl = tcfg.container.child('table', true);
45908 tbl.setAttribute('width', '100%');
45916 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45919 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45921 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45923 tabPosition : "top",
45925 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45927 currentTabWidth : 0,
45929 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45933 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45937 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45939 preferredTabWidth : 175,
45941 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45943 resizeTabs : false,
45945 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45947 monitorResize : true,
45949 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
45951 toolbar : false, // set by caller..
45953 region : false, /// set by caller
45955 disableTooltips : true, // not used yet...
45958 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45959 * @param {String} id The id of the div to use <b>or create</b>
45960 * @param {String} text The text for the tab
45961 * @param {String} content (optional) Content to put in the TabPanelItem body
45962 * @param {Boolean} closable (optional) True to create a close icon on the tab
45963 * @return {Roo.TabPanelItem} The created TabPanelItem
45965 addTab : function(id, text, content, closable, tpl)
45967 var item = new Roo.bootstrap.panel.TabItem({
45971 closable : closable,
45974 this.addTabItem(item);
45976 item.setContent(content);
45982 * Returns the {@link Roo.TabPanelItem} with the specified id/index
45983 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45984 * @return {Roo.TabPanelItem}
45986 getTab : function(id){
45987 return this.items[id];
45991 * Hides the {@link Roo.TabPanelItem} with the specified id/index
45992 * @param {String/Number} id The id or index of the TabPanelItem to hide.
45994 hideTab : function(id){
45995 var t = this.items[id];
45998 this.hiddenCount++;
45999 this.autoSizeTabs();
46004 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46005 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46007 unhideTab : function(id){
46008 var t = this.items[id];
46010 t.setHidden(false);
46011 this.hiddenCount--;
46012 this.autoSizeTabs();
46017 * Adds an existing {@link Roo.TabPanelItem}.
46018 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46020 addTabItem : function(item)
46022 this.items[item.id] = item;
46023 this.items.push(item);
46024 this.autoSizeTabs();
46025 // if(this.resizeTabs){
46026 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46027 // this.autoSizeTabs();
46029 // item.autoSize();
46034 * Removes a {@link Roo.TabPanelItem}.
46035 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46037 removeTab : function(id){
46038 var items = this.items;
46039 var tab = items[id];
46040 if(!tab) { return; }
46041 var index = items.indexOf(tab);
46042 if(this.active == tab && items.length > 1){
46043 var newTab = this.getNextAvailable(index);
46048 this.stripEl.dom.removeChild(tab.pnode.dom);
46049 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46050 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46052 items.splice(index, 1);
46053 delete this.items[tab.id];
46054 tab.fireEvent("close", tab);
46055 tab.purgeListeners();
46056 this.autoSizeTabs();
46059 getNextAvailable : function(start){
46060 var items = this.items;
46062 // look for a next tab that will slide over to
46063 // replace the one being removed
46064 while(index < items.length){
46065 var item = items[++index];
46066 if(item && !item.isHidden()){
46070 // if one isn't found select the previous tab (on the left)
46073 var item = items[--index];
46074 if(item && !item.isHidden()){
46082 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46083 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46085 disableTab : function(id){
46086 var tab = this.items[id];
46087 if(tab && this.active != tab){
46093 * Enables a {@link Roo.TabPanelItem} that is disabled.
46094 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46096 enableTab : function(id){
46097 var tab = this.items[id];
46102 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46103 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46104 * @return {Roo.TabPanelItem} The TabPanelItem.
46106 activate : function(id)
46108 //Roo.log('activite:' + id);
46110 var tab = this.items[id];
46114 if(tab == this.active || tab.disabled){
46118 this.fireEvent("beforetabchange", this, e, tab);
46119 if(e.cancel !== true && !tab.disabled){
46121 this.active.hide();
46123 this.active = this.items[id];
46124 this.active.show();
46125 this.fireEvent("tabchange", this, this.active);
46131 * Gets the active {@link Roo.TabPanelItem}.
46132 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46134 getActiveTab : function(){
46135 return this.active;
46139 * Updates the tab body element to fit the height of the container element
46140 * for overflow scrolling
46141 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46143 syncHeight : function(targetHeight){
46144 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46145 var bm = this.bodyEl.getMargins();
46146 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46147 this.bodyEl.setHeight(newHeight);
46151 onResize : function(){
46152 if(this.monitorResize){
46153 this.autoSizeTabs();
46158 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46160 beginUpdate : function(){
46161 this.updating = true;
46165 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46167 endUpdate : function(){
46168 this.updating = false;
46169 this.autoSizeTabs();
46173 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46175 autoSizeTabs : function()
46177 var count = this.items.length;
46178 var vcount = count - this.hiddenCount;
46181 this.stripEl.hide();
46183 this.stripEl.show();
46186 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46191 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46192 var availWidth = Math.floor(w / vcount);
46193 var b = this.stripBody;
46194 if(b.getWidth() > w){
46195 var tabs = this.items;
46196 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46197 if(availWidth < this.minTabWidth){
46198 /*if(!this.sleft){ // incomplete scrolling code
46199 this.createScrollButtons();
46202 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46205 if(this.currentTabWidth < this.preferredTabWidth){
46206 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46212 * Returns the number of tabs in this TabPanel.
46215 getCount : function(){
46216 return this.items.length;
46220 * Resizes all the tabs to the passed width
46221 * @param {Number} The new width
46223 setTabWidth : function(width){
46224 this.currentTabWidth = width;
46225 for(var i = 0, len = this.items.length; i < len; i++) {
46226 if(!this.items[i].isHidden()) {
46227 this.items[i].setWidth(width);
46233 * Destroys this TabPanel
46234 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46236 destroy : function(removeEl){
46237 Roo.EventManager.removeResizeListener(this.onResize, this);
46238 for(var i = 0, len = this.items.length; i < len; i++){
46239 this.items[i].purgeListeners();
46241 if(removeEl === true){
46242 this.el.update("");
46247 createStrip : function(container)
46249 var strip = document.createElement("nav");
46250 strip.className = Roo.bootstrap.version == 4 ?
46251 "navbar-light bg-light" :
46252 "navbar navbar-default"; //"x-tabs-wrap";
46253 container.appendChild(strip);
46257 createStripList : function(strip)
46259 // div wrapper for retard IE
46260 // returns the "tr" element.
46261 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46262 //'<div class="x-tabs-strip-wrap">'+
46263 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46264 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46265 return strip.firstChild; //.firstChild.firstChild.firstChild;
46267 createBody : function(container)
46269 var body = document.createElement("div");
46270 Roo.id(body, "tab-body");
46271 //Roo.fly(body).addClass("x-tabs-body");
46272 Roo.fly(body).addClass("tab-content");
46273 container.appendChild(body);
46276 createItemBody :function(bodyEl, id){
46277 var body = Roo.getDom(id);
46279 body = document.createElement("div");
46282 //Roo.fly(body).addClass("x-tabs-item-body");
46283 Roo.fly(body).addClass("tab-pane");
46284 bodyEl.insertBefore(body, bodyEl.firstChild);
46288 createStripElements : function(stripEl, text, closable, tpl)
46290 var td = document.createElement("li"); // was td..
46291 td.className = 'nav-item';
46293 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46296 stripEl.appendChild(td);
46298 td.className = "x-tabs-closable";
46299 if(!this.closeTpl){
46300 this.closeTpl = new Roo.Template(
46301 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46302 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46303 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46306 var el = this.closeTpl.overwrite(td, {"text": text});
46307 var close = el.getElementsByTagName("div")[0];
46308 var inner = el.getElementsByTagName("em")[0];
46309 return {"el": el, "close": close, "inner": inner};
46312 // not sure what this is..
46313 // if(!this.tabTpl){
46314 //this.tabTpl = new Roo.Template(
46315 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46316 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46318 // this.tabTpl = new Roo.Template(
46319 // '<a href="#">' +
46320 // '<span unselectable="on"' +
46321 // (this.disableTooltips ? '' : ' title="{text}"') +
46322 // ' >{text}</span></a>'
46328 var template = tpl || this.tabTpl || false;
46331 template = new Roo.Template(
46332 Roo.bootstrap.version == 4 ?
46334 '<a class="nav-link" href="#" unselectable="on"' +
46335 (this.disableTooltips ? '' : ' title="{text}"') +
46338 '<a class="nav-link" href="#">' +
46339 '<span unselectable="on"' +
46340 (this.disableTooltips ? '' : ' title="{text}"') +
46341 ' >{text}</span></a>'
46346 switch (typeof(template)) {
46350 template = new Roo.Template(template);
46356 var el = template.overwrite(td, {"text": text});
46358 var inner = el.getElementsByTagName("span")[0];
46360 return {"el": el, "inner": inner};
46368 * @class Roo.TabPanelItem
46369 * @extends Roo.util.Observable
46370 * Represents an individual item (tab plus body) in a TabPanel.
46371 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46372 * @param {String} id The id of this TabPanelItem
46373 * @param {String} text The text for the tab of this TabPanelItem
46374 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46376 Roo.bootstrap.panel.TabItem = function(config){
46378 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46379 * @type Roo.TabPanel
46381 this.tabPanel = config.panel;
46383 * The id for this TabPanelItem
46386 this.id = config.id;
46388 this.disabled = false;
46390 this.text = config.text;
46392 this.loaded = false;
46393 this.closable = config.closable;
46396 * The body element for this TabPanelItem.
46397 * @type Roo.Element
46399 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46400 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46401 this.bodyEl.setStyle("display", "block");
46402 this.bodyEl.setStyle("zoom", "1");
46403 //this.hideAction();
46405 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46407 this.el = Roo.get(els.el);
46408 this.inner = Roo.get(els.inner, true);
46409 this.textEl = Roo.bootstrap.version == 4 ?
46410 this.el : Roo.get(this.el.dom.firstChild, true);
46412 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46413 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46416 // this.el.on("mousedown", this.onTabMouseDown, this);
46417 this.el.on("click", this.onTabClick, this);
46419 if(config.closable){
46420 var c = Roo.get(els.close, true);
46421 c.dom.title = this.closeText;
46422 c.addClassOnOver("close-over");
46423 c.on("click", this.closeClick, this);
46429 * Fires when this tab becomes the active tab.
46430 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46431 * @param {Roo.TabPanelItem} this
46435 * @event beforeclose
46436 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46437 * @param {Roo.TabPanelItem} this
46438 * @param {Object} e Set cancel to true on this object to cancel the close.
46440 "beforeclose": true,
46443 * Fires when this tab is closed.
46444 * @param {Roo.TabPanelItem} this
46448 * @event deactivate
46449 * Fires when this tab is no longer the active tab.
46450 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46451 * @param {Roo.TabPanelItem} this
46453 "deactivate" : true
46455 this.hidden = false;
46457 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46460 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46462 purgeListeners : function(){
46463 Roo.util.Observable.prototype.purgeListeners.call(this);
46464 this.el.removeAllListeners();
46467 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46470 this.status_node.addClass("active");
46473 this.tabPanel.stripWrap.repaint();
46475 this.fireEvent("activate", this.tabPanel, this);
46479 * Returns true if this tab is the active tab.
46480 * @return {Boolean}
46482 isActive : function(){
46483 return this.tabPanel.getActiveTab() == this;
46487 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46490 this.status_node.removeClass("active");
46492 this.fireEvent("deactivate", this.tabPanel, this);
46495 hideAction : function(){
46496 this.bodyEl.hide();
46497 this.bodyEl.setStyle("position", "absolute");
46498 this.bodyEl.setLeft("-20000px");
46499 this.bodyEl.setTop("-20000px");
46502 showAction : function(){
46503 this.bodyEl.setStyle("position", "relative");
46504 this.bodyEl.setTop("");
46505 this.bodyEl.setLeft("");
46506 this.bodyEl.show();
46510 * Set the tooltip for the tab.
46511 * @param {String} tooltip The tab's tooltip
46513 setTooltip : function(text){
46514 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46515 this.textEl.dom.qtip = text;
46516 this.textEl.dom.removeAttribute('title');
46518 this.textEl.dom.title = text;
46522 onTabClick : function(e){
46523 e.preventDefault();
46524 this.tabPanel.activate(this.id);
46527 onTabMouseDown : function(e){
46528 e.preventDefault();
46529 this.tabPanel.activate(this.id);
46532 getWidth : function(){
46533 return this.inner.getWidth();
46536 setWidth : function(width){
46537 var iwidth = width - this.linode.getPadding("lr");
46538 this.inner.setWidth(iwidth);
46539 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46540 this.linode.setWidth(width);
46544 * Show or hide the tab
46545 * @param {Boolean} hidden True to hide or false to show.
46547 setHidden : function(hidden){
46548 this.hidden = hidden;
46549 this.linode.setStyle("display", hidden ? "none" : "");
46553 * Returns true if this tab is "hidden"
46554 * @return {Boolean}
46556 isHidden : function(){
46557 return this.hidden;
46561 * Returns the text for this tab
46564 getText : function(){
46568 autoSize : function(){
46569 //this.el.beginMeasure();
46570 this.textEl.setWidth(1);
46572 * #2804 [new] Tabs in Roojs
46573 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46575 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46576 //this.el.endMeasure();
46580 * Sets the text for the tab (Note: this also sets the tooltip text)
46581 * @param {String} text The tab's text and tooltip
46583 setText : function(text){
46585 this.textEl.update(text);
46586 this.setTooltip(text);
46587 //if(!this.tabPanel.resizeTabs){
46588 // this.autoSize();
46592 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46594 activate : function(){
46595 this.tabPanel.activate(this.id);
46599 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46601 disable : function(){
46602 if(this.tabPanel.active != this){
46603 this.disabled = true;
46604 this.status_node.addClass("disabled");
46609 * Enables this TabPanelItem if it was previously disabled.
46611 enable : function(){
46612 this.disabled = false;
46613 this.status_node.removeClass("disabled");
46617 * Sets the content for this TabPanelItem.
46618 * @param {String} content The content
46619 * @param {Boolean} loadScripts true to look for and load scripts
46621 setContent : function(content, loadScripts){
46622 this.bodyEl.update(content, loadScripts);
46626 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46627 * @return {Roo.UpdateManager} The UpdateManager
46629 getUpdateManager : function(){
46630 return this.bodyEl.getUpdateManager();
46634 * Set a URL to be used to load the content for this TabPanelItem.
46635 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46636 * @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)
46637 * @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)
46638 * @return {Roo.UpdateManager} The UpdateManager
46640 setUrl : function(url, params, loadOnce){
46641 if(this.refreshDelegate){
46642 this.un('activate', this.refreshDelegate);
46644 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46645 this.on("activate", this.refreshDelegate);
46646 return this.bodyEl.getUpdateManager();
46650 _handleRefresh : function(url, params, loadOnce){
46651 if(!loadOnce || !this.loaded){
46652 var updater = this.bodyEl.getUpdateManager();
46653 updater.update(url, params, this._setLoaded.createDelegate(this));
46658 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
46659 * Will fail silently if the setUrl method has not been called.
46660 * This does not activate the panel, just updates its content.
46662 refresh : function(){
46663 if(this.refreshDelegate){
46664 this.loaded = false;
46665 this.refreshDelegate();
46670 _setLoaded : function(){
46671 this.loaded = true;
46675 closeClick : function(e){
46678 this.fireEvent("beforeclose", this, o);
46679 if(o.cancel !== true){
46680 this.tabPanel.removeTab(this.id);
46684 * The text displayed in the tooltip for the close icon.
46687 closeText : "Close this tab"
46690 * This script refer to:
46691 * Title: International Telephone Input
46692 * Author: Jack O'Connor
46693 * Code version: v12.1.12
46694 * Availability: https://github.com/jackocnr/intl-tel-input.git
46697 Roo.bootstrap.form.PhoneInputData = function() {
46700 "Afghanistan (افغانستان)",
46705 "Albania (Shqipëri)",
46710 "Algeria (الجزائر)",
46735 "Antigua and Barbuda",
46745 "Armenia (Հայաստան)",
46761 "Austria (Österreich)",
46766 "Azerbaijan (Azərbaycan)",
46776 "Bahrain (البحرين)",
46781 "Bangladesh (বাংলাদেশ)",
46791 "Belarus (Беларусь)",
46796 "Belgium (België)",
46826 "Bosnia and Herzegovina (Босна и Херцеговина)",
46841 "British Indian Ocean Territory",
46846 "British Virgin Islands",
46856 "Bulgaria (България)",
46866 "Burundi (Uburundi)",
46871 "Cambodia (កម្ពុជា)",
46876 "Cameroon (Cameroun)",
46885 ["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"]
46888 "Cape Verde (Kabu Verdi)",
46893 "Caribbean Netherlands",
46904 "Central African Republic (République centrafricaine)",
46924 "Christmas Island",
46930 "Cocos (Keeling) Islands",
46941 "Comoros (جزر القمر)",
46946 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46951 "Congo (Republic) (Congo-Brazzaville)",
46971 "Croatia (Hrvatska)",
46992 "Czech Republic (Česká republika)",
46997 "Denmark (Danmark)",
47012 "Dominican Republic (República Dominicana)",
47016 ["809", "829", "849"]
47034 "Equatorial Guinea (Guinea Ecuatorial)",
47054 "Falkland Islands (Islas Malvinas)",
47059 "Faroe Islands (Føroyar)",
47080 "French Guiana (Guyane française)",
47085 "French Polynesia (Polynésie française)",
47100 "Georgia (საქართველო)",
47105 "Germany (Deutschland)",
47125 "Greenland (Kalaallit Nunaat)",
47162 "Guinea-Bissau (Guiné Bissau)",
47187 "Hungary (Magyarország)",
47192 "Iceland (Ísland)",
47212 "Iraq (العراق)",
47228 "Israel (ישראל)",
47255 "Jordan (الأردن)",
47260 "Kazakhstan (Казахстан)",
47281 "Kuwait (الكويت)",
47286 "Kyrgyzstan (Кыргызстан)",
47296 "Latvia (Latvija)",
47301 "Lebanon (لبنان)",
47316 "Libya (ليبيا)",
47326 "Lithuania (Lietuva)",
47341 "Macedonia (FYROM) (Македонија)",
47346 "Madagascar (Madagasikara)",
47376 "Marshall Islands",
47386 "Mauritania (موريتانيا)",
47391 "Mauritius (Moris)",
47412 "Moldova (Republica Moldova)",
47422 "Mongolia (Монгол)",
47427 "Montenegro (Crna Gora)",
47437 "Morocco (المغرب)",
47443 "Mozambique (Moçambique)",
47448 "Myanmar (Burma) (မြန်မာ)",
47453 "Namibia (Namibië)",
47468 "Netherlands (Nederland)",
47473 "New Caledonia (Nouvelle-Calédonie)",
47508 "North Korea (조선 민주주의 인민 공화국)",
47513 "Northern Mariana Islands",
47529 "Pakistan (پاکستان)",
47539 "Palestine (فلسطين)",
47549 "Papua New Guinea",
47591 "Réunion (La Réunion)",
47597 "Romania (România)",
47613 "Saint Barthélemy",
47624 "Saint Kitts and Nevis",
47634 "Saint Martin (Saint-Martin (partie française))",
47640 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47645 "Saint Vincent and the Grenadines",
47660 "São Tomé and Príncipe (São Tomé e Príncipe)",
47665 "Saudi Arabia (المملكة العربية السعودية)",
47670 "Senegal (Sénégal)",
47700 "Slovakia (Slovensko)",
47705 "Slovenia (Slovenija)",
47715 "Somalia (Soomaaliya)",
47725 "South Korea (대한민국)",
47730 "South Sudan (جنوب السودان)",
47740 "Sri Lanka (ශ්රී ලංකාව)",
47745 "Sudan (السودان)",
47755 "Svalbard and Jan Mayen",
47766 "Sweden (Sverige)",
47771 "Switzerland (Schweiz)",
47776 "Syria (سوريا)",
47821 "Trinidad and Tobago",
47826 "Tunisia (تونس)",
47831 "Turkey (Türkiye)",
47841 "Turks and Caicos Islands",
47851 "U.S. Virgin Islands",
47861 "Ukraine (Україна)",
47866 "United Arab Emirates (الإمارات العربية المتحدة)",
47888 "Uzbekistan (Oʻzbekiston)",
47898 "Vatican City (Città del Vaticano)",
47909 "Vietnam (Việt Nam)",
47914 "Wallis and Futuna (Wallis-et-Futuna)",
47919 "Western Sahara (الصحراء الغربية)",
47925 "Yemen (اليمن)",
47949 * This script refer to:
47950 * Title: International Telephone Input
47951 * Author: Jack O'Connor
47952 * Code version: v12.1.12
47953 * Availability: https://github.com/jackocnr/intl-tel-input.git
47957 * @class Roo.bootstrap.form.PhoneInput
47958 * @extends Roo.bootstrap.form.TriggerField
47959 * An input with International dial-code selection
47961 * @cfg {String} defaultDialCode default '+852'
47962 * @cfg {Array} preferedCountries default []
47965 * Create a new PhoneInput.
47966 * @param {Object} config Configuration options
47969 Roo.bootstrap.form.PhoneInput = function(config) {
47970 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47973 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47975 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47977 listWidth: undefined,
47979 selectedClass: 'active',
47981 invalidClass : "has-warning",
47983 validClass: 'has-success',
47985 allowed: '0123456789',
47990 * @cfg {String} defaultDialCode The default dial code when initializing the input
47992 defaultDialCode: '+852',
47995 * @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
47997 preferedCountries: false,
47999 getAutoCreate : function()
48001 var data = Roo.bootstrap.form.PhoneInputData();
48002 var align = this.labelAlign || this.parentLabelAlign();
48005 this.allCountries = [];
48006 this.dialCodeMapping = [];
48008 for (var i = 0; i < data.length; i++) {
48010 this.allCountries[i] = {
48014 priority: c[3] || 0,
48015 areaCodes: c[4] || null
48017 this.dialCodeMapping[c[2]] = {
48020 priority: c[3] || 0,
48021 areaCodes: c[4] || null
48033 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48034 maxlength: this.max_length,
48035 cls : 'form-control tel-input',
48036 autocomplete: 'new-password'
48039 var hiddenInput = {
48042 cls: 'hidden-tel-input'
48046 hiddenInput.name = this.name;
48049 if (this.disabled) {
48050 input.disabled = true;
48053 var flag_container = {
48070 cls: this.hasFeedback ? 'has-feedback' : '',
48076 cls: 'dial-code-holder',
48083 cls: 'roo-select2-container input-group',
48090 if (this.fieldLabel.length) {
48093 tooltip: 'This field is required'
48099 cls: 'control-label',
48105 html: this.fieldLabel
48108 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48114 if(this.indicatorpos == 'right') {
48115 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48122 if(align == 'left') {
48130 if(this.labelWidth > 12){
48131 label.style = "width: " + this.labelWidth + 'px';
48133 if(this.labelWidth < 13 && this.labelmd == 0){
48134 this.labelmd = this.labelWidth;
48136 if(this.labellg > 0){
48137 label.cls += ' col-lg-' + this.labellg;
48138 input.cls += ' col-lg-' + (12 - this.labellg);
48140 if(this.labelmd > 0){
48141 label.cls += ' col-md-' + this.labelmd;
48142 container.cls += ' col-md-' + (12 - this.labelmd);
48144 if(this.labelsm > 0){
48145 label.cls += ' col-sm-' + this.labelsm;
48146 container.cls += ' col-sm-' + (12 - this.labelsm);
48148 if(this.labelxs > 0){
48149 label.cls += ' col-xs-' + this.labelxs;
48150 container.cls += ' col-xs-' + (12 - this.labelxs);
48160 var settings = this;
48162 ['xs','sm','md','lg'].map(function(size){
48163 if (settings[size]) {
48164 cfg.cls += ' col-' + size + '-' + settings[size];
48168 this.store = new Roo.data.Store({
48169 proxy : new Roo.data.MemoryProxy({}),
48170 reader : new Roo.data.JsonReader({
48181 'name' : 'dialCode',
48185 'name' : 'priority',
48189 'name' : 'areaCodes',
48196 if(!this.preferedCountries) {
48197 this.preferedCountries = [
48204 var p = this.preferedCountries.reverse();
48207 for (var i = 0; i < p.length; i++) {
48208 for (var j = 0; j < this.allCountries.length; j++) {
48209 if(this.allCountries[j].iso2 == p[i]) {
48210 var t = this.allCountries[j];
48211 this.allCountries.splice(j,1);
48212 this.allCountries.unshift(t);
48218 this.store.proxy.data = {
48220 data: this.allCountries
48226 initEvents : function()
48229 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48231 this.indicator = this.indicatorEl();
48232 this.flag = this.flagEl();
48233 this.dialCodeHolder = this.dialCodeHolderEl();
48235 this.trigger = this.el.select('div.flag-box',true).first();
48236 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48241 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48242 _this.list.setWidth(lw);
48245 this.list.on('mouseover', this.onViewOver, this);
48246 this.list.on('mousemove', this.onViewMove, this);
48247 this.inputEl().on("keyup", this.onKeyUp, this);
48248 this.inputEl().on("keypress", this.onKeyPress, this);
48250 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48252 this.view = new Roo.View(this.list, this.tpl, {
48253 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48256 this.view.on('click', this.onViewClick, this);
48257 this.setValue(this.defaultDialCode);
48260 onTriggerClick : function(e)
48262 Roo.log('trigger click');
48267 if(this.isExpanded()){
48269 this.hasFocus = false;
48271 this.store.load({});
48272 this.hasFocus = true;
48277 isExpanded : function()
48279 return this.list.isVisible();
48282 collapse : function()
48284 if(!this.isExpanded()){
48288 Roo.get(document).un('mousedown', this.collapseIf, this);
48289 Roo.get(document).un('mousewheel', this.collapseIf, this);
48290 this.fireEvent('collapse', this);
48294 expand : function()
48298 if(this.isExpanded() || !this.hasFocus){
48302 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48303 this.list.setWidth(lw);
48306 this.restrictHeight();
48308 Roo.get(document).on('mousedown', this.collapseIf, this);
48309 Roo.get(document).on('mousewheel', this.collapseIf, this);
48311 this.fireEvent('expand', this);
48314 restrictHeight : function()
48316 this.list.alignTo(this.inputEl(), this.listAlign);
48317 this.list.alignTo(this.inputEl(), this.listAlign);
48320 onViewOver : function(e, t)
48322 if(this.inKeyMode){
48325 var item = this.view.findItemFromChild(t);
48328 var index = this.view.indexOf(item);
48329 this.select(index, false);
48334 onViewClick : function(view, doFocus, el, e)
48336 var index = this.view.getSelectedIndexes()[0];
48338 var r = this.store.getAt(index);
48341 this.onSelect(r, index);
48343 if(doFocus !== false && !this.blockFocus){
48344 this.inputEl().focus();
48348 onViewMove : function(e, t)
48350 this.inKeyMode = false;
48353 select : function(index, scrollIntoView)
48355 this.selectedIndex = index;
48356 this.view.select(index);
48357 if(scrollIntoView !== false){
48358 var el = this.view.getNode(index);
48360 this.list.scrollChildIntoView(el, false);
48365 createList : function()
48367 this.list = Roo.get(document.body).createChild({
48369 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48370 style: 'display:none'
48373 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48376 collapseIf : function(e)
48378 var in_combo = e.within(this.el);
48379 var in_list = e.within(this.list);
48380 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48382 if (in_combo || in_list || is_list) {
48388 onSelect : function(record, index)
48390 if(this.fireEvent('beforeselect', this, record, index) !== false){
48392 this.setFlagClass(record.data.iso2);
48393 this.setDialCode(record.data.dialCode);
48394 this.hasFocus = false;
48396 this.fireEvent('select', this, record, index);
48400 flagEl : function()
48402 var flag = this.el.select('div.flag',true).first();
48409 dialCodeHolderEl : function()
48411 var d = this.el.select('input.dial-code-holder',true).first();
48418 setDialCode : function(v)
48420 this.dialCodeHolder.dom.value = '+'+v;
48423 setFlagClass : function(n)
48425 this.flag.dom.className = 'flag '+n;
48428 getValue : function()
48430 var v = this.inputEl().getValue();
48431 if(this.dialCodeHolder) {
48432 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48437 setValue : function(v)
48439 var d = this.getDialCode(v);
48441 //invalid dial code
48442 if(v.length == 0 || !d || d.length == 0) {
48444 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48445 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48451 this.setFlagClass(this.dialCodeMapping[d].iso2);
48452 this.setDialCode(d);
48453 this.inputEl().dom.value = v.replace('+'+d,'');
48454 this.hiddenEl().dom.value = this.getValue();
48459 getDialCode : function(v)
48463 if (v.length == 0) {
48464 return this.dialCodeHolder.dom.value;
48468 if (v.charAt(0) != "+") {
48471 var numericChars = "";
48472 for (var i = 1; i < v.length; i++) {
48473 var c = v.charAt(i);
48476 if (this.dialCodeMapping[numericChars]) {
48477 dialCode = v.substr(1, i);
48479 if (numericChars.length == 4) {
48489 this.setValue(this.defaultDialCode);
48493 hiddenEl : function()
48495 return this.el.select('input.hidden-tel-input',true).first();
48498 // after setting val
48499 onKeyUp : function(e){
48500 this.setValue(this.getValue());
48503 onKeyPress : function(e){
48504 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48511 * @class Roo.bootstrap.form.MoneyField
48512 * @extends Roo.bootstrap.form.ComboBox
48513 * Bootstrap MoneyField class
48516 * Create a new MoneyField.
48517 * @param {Object} config Configuration options
48520 Roo.bootstrap.form.MoneyField = function(config) {
48522 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48526 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48529 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48531 allowDecimals : true,
48533 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48535 decimalSeparator : ".",
48537 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48539 decimalPrecision : 0,
48541 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48543 allowNegative : true,
48545 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48549 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48551 minValue : Number.NEGATIVE_INFINITY,
48553 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48555 maxValue : Number.MAX_VALUE,
48557 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48559 minText : "The minimum value for this field is {0}",
48561 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48563 maxText : "The maximum value for this field is {0}",
48565 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48566 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48568 nanText : "{0} is not a valid number",
48570 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48574 * @cfg {String} defaults currency of the MoneyField
48575 * value should be in lkey
48577 defaultCurrency : false,
48579 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48581 thousandsDelimiter : false,
48583 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48592 * @cfg {Roo.data.Store} store Store to lookup currency??
48596 getAutoCreate : function()
48598 var align = this.labelAlign || this.parentLabelAlign();
48610 cls : 'form-control roo-money-amount-input',
48611 autocomplete: 'new-password'
48614 var hiddenInput = {
48618 cls: 'hidden-number-input'
48621 if(this.max_length) {
48622 input.maxlength = this.max_length;
48626 hiddenInput.name = this.name;
48629 if (this.disabled) {
48630 input.disabled = true;
48633 var clg = 12 - this.inputlg;
48634 var cmd = 12 - this.inputmd;
48635 var csm = 12 - this.inputsm;
48636 var cxs = 12 - this.inputxs;
48640 cls : 'row roo-money-field',
48644 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48648 cls: 'roo-select2-container input-group',
48652 cls : 'form-control roo-money-currency-input',
48653 autocomplete: 'new-password',
48655 name : this.currencyName
48659 cls : 'input-group-addon',
48673 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48677 cls: this.hasFeedback ? 'has-feedback' : '',
48688 if (this.fieldLabel.length) {
48691 tooltip: 'This field is required'
48697 cls: 'control-label',
48703 html: this.fieldLabel
48706 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48712 if(this.indicatorpos == 'right') {
48713 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48720 if(align == 'left') {
48728 if(this.labelWidth > 12){
48729 label.style = "width: " + this.labelWidth + 'px';
48731 if(this.labelWidth < 13 && this.labelmd == 0){
48732 this.labelmd = this.labelWidth;
48734 if(this.labellg > 0){
48735 label.cls += ' col-lg-' + this.labellg;
48736 input.cls += ' col-lg-' + (12 - this.labellg);
48738 if(this.labelmd > 0){
48739 label.cls += ' col-md-' + this.labelmd;
48740 container.cls += ' col-md-' + (12 - this.labelmd);
48742 if(this.labelsm > 0){
48743 label.cls += ' col-sm-' + this.labelsm;
48744 container.cls += ' col-sm-' + (12 - this.labelsm);
48746 if(this.labelxs > 0){
48747 label.cls += ' col-xs-' + this.labelxs;
48748 container.cls += ' col-xs-' + (12 - this.labelxs);
48759 var settings = this;
48761 ['xs','sm','md','lg'].map(function(size){
48762 if (settings[size]) {
48763 cfg.cls += ' col-' + size + '-' + settings[size];
48770 initEvents : function()
48772 this.indicator = this.indicatorEl();
48774 this.initCurrencyEvent();
48776 this.initNumberEvent();
48779 initCurrencyEvent : function()
48782 throw "can not find store for combo";
48785 this.store = Roo.factory(this.store, Roo.data);
48786 this.store.parent = this;
48790 this.triggerEl = this.el.select('.input-group-addon', true).first();
48792 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48797 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48798 _this.list.setWidth(lw);
48801 this.list.on('mouseover', this.onViewOver, this);
48802 this.list.on('mousemove', this.onViewMove, this);
48803 this.list.on('scroll', this.onViewScroll, this);
48806 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48809 this.view = new Roo.View(this.list, this.tpl, {
48810 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48813 this.view.on('click', this.onViewClick, this);
48815 this.store.on('beforeload', this.onBeforeLoad, this);
48816 this.store.on('load', this.onLoad, this);
48817 this.store.on('loadexception', this.onLoadException, this);
48819 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48820 "up" : function(e){
48821 this.inKeyMode = true;
48825 "down" : function(e){
48826 if(!this.isExpanded()){
48827 this.onTriggerClick();
48829 this.inKeyMode = true;
48834 "enter" : function(e){
48837 if(this.fireEvent("specialkey", this, e)){
48838 this.onViewClick(false);
48844 "esc" : function(e){
48848 "tab" : function(e){
48851 if(this.fireEvent("specialkey", this, e)){
48852 this.onViewClick(false);
48860 doRelay : function(foo, bar, hname){
48861 if(hname == 'down' || this.scope.isExpanded()){
48862 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48870 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48874 initNumberEvent : function(e)
48876 this.inputEl().on("keydown" , this.fireKey, this);
48877 this.inputEl().on("focus", this.onFocus, this);
48878 this.inputEl().on("blur", this.onBlur, this);
48880 this.inputEl().relayEvent('keyup', this);
48882 if(this.indicator){
48883 this.indicator.addClass('invisible');
48886 this.originalValue = this.getValue();
48888 if(this.validationEvent == 'keyup'){
48889 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48890 this.inputEl().on('keyup', this.filterValidation, this);
48892 else if(this.validationEvent !== false){
48893 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48896 if(this.selectOnFocus){
48897 this.on("focus", this.preFocus, this);
48900 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48901 this.inputEl().on("keypress", this.filterKeys, this);
48903 this.inputEl().relayEvent('keypress', this);
48906 var allowed = "0123456789";
48908 if(this.allowDecimals){
48909 allowed += this.decimalSeparator;
48912 if(this.allowNegative){
48916 if(this.thousandsDelimiter) {
48920 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48922 var keyPress = function(e){
48924 var k = e.getKey();
48926 var c = e.getCharCode();
48929 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48930 allowed.indexOf(String.fromCharCode(c)) === -1
48936 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48940 if(allowed.indexOf(String.fromCharCode(c)) === -1){
48945 this.inputEl().on("keypress", keyPress, this);
48949 onTriggerClick : function(e)
48956 this.loadNext = false;
48958 if(this.isExpanded()){
48963 this.hasFocus = true;
48965 if(this.triggerAction == 'all') {
48966 this.doQuery(this.allQuery, true);
48970 this.doQuery(this.getRawValue());
48973 getCurrency : function()
48975 var v = this.currencyEl().getValue();
48980 restrictHeight : function()
48982 this.list.alignTo(this.currencyEl(), this.listAlign);
48983 this.list.alignTo(this.currencyEl(), this.listAlign);
48986 onViewClick : function(view, doFocus, el, e)
48988 var index = this.view.getSelectedIndexes()[0];
48990 var r = this.store.getAt(index);
48993 this.onSelect(r, index);
48997 onSelect : function(record, index){
48999 if(this.fireEvent('beforeselect', this, record, index) !== false){
49001 this.setFromCurrencyData(index > -1 ? record.data : false);
49005 this.fireEvent('select', this, record, index);
49009 setFromCurrencyData : function(o)
49013 this.lastCurrency = o;
49015 if (this.currencyField) {
49016 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49018 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49021 this.lastSelectionText = currency;
49023 //setting default currency
49024 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49025 this.setCurrency(this.defaultCurrency);
49029 this.setCurrency(currency);
49032 setFromData : function(o)
49036 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49038 this.setFromCurrencyData(c);
49043 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49045 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49048 this.setValue(value);
49052 setCurrency : function(v)
49054 this.currencyValue = v;
49057 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49062 setValue : function(v)
49064 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49070 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49072 this.inputEl().dom.value = (v == '') ? '' :
49073 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49075 if(!this.allowZero && v === '0') {
49076 this.hiddenEl().dom.value = '';
49077 this.inputEl().dom.value = '';
49084 getRawValue : function()
49086 var v = this.inputEl().getValue();
49091 getValue : function()
49093 return this.fixPrecision(this.parseValue(this.getRawValue()));
49096 parseValue : function(value)
49098 if(this.thousandsDelimiter) {
49100 r = new RegExp(",", "g");
49101 value = value.replace(r, "");
49104 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49105 return isNaN(value) ? '' : value;
49109 fixPrecision : function(value)
49111 if(this.thousandsDelimiter) {
49113 r = new RegExp(",", "g");
49114 value = value.replace(r, "");
49117 var nan = isNaN(value);
49119 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49120 return nan ? '' : value;
49122 return parseFloat(value).toFixed(this.decimalPrecision);
49125 decimalPrecisionFcn : function(v)
49127 return Math.floor(v);
49130 validateValue : function(value)
49132 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49136 var num = this.parseValue(value);
49139 this.markInvalid(String.format(this.nanText, value));
49143 if(num < this.minValue){
49144 this.markInvalid(String.format(this.minText, this.minValue));
49148 if(num > this.maxValue){
49149 this.markInvalid(String.format(this.maxText, this.maxValue));
49156 validate : function()
49158 if(this.disabled || this.allowBlank){
49163 var currency = this.getCurrency();
49165 if(this.validateValue(this.getRawValue()) && currency.length){
49170 this.markInvalid();
49174 getName: function()
49179 beforeBlur : function()
49185 var v = this.parseValue(this.getRawValue());
49192 onBlur : function()
49196 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49197 //this.el.removeClass(this.focusClass);
49200 this.hasFocus = false;
49202 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49206 var v = this.getValue();
49208 if(String(v) !== String(this.startValue)){
49209 this.fireEvent('change', this, v, this.startValue);
49212 this.fireEvent("blur", this);
49215 inputEl : function()
49217 return this.el.select('.roo-money-amount-input', true).first();
49220 currencyEl : function()
49222 return this.el.select('.roo-money-currency-input', true).first();
49225 hiddenEl : function()
49227 return this.el.select('input.hidden-number-input',true).first();
49231 * @class Roo.bootstrap.BezierSignature
49232 * @extends Roo.bootstrap.Component
49233 * Bootstrap BezierSignature class
49234 * This script refer to:
49235 * Title: Signature Pad
49237 * Availability: https://github.com/szimek/signature_pad
49240 * Create a new BezierSignature
49241 * @param {Object} config The config object
49244 Roo.bootstrap.BezierSignature = function(config){
49245 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49251 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49258 mouse_btn_down: true,
49261 * @cfg {int} canvas height
49263 canvas_height: '200px',
49266 * @cfg {float|function} Radius of a single dot.
49271 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49276 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49281 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49286 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49291 * @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.
49293 bg_color: 'rgba(0, 0, 0, 0)',
49296 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49298 dot_color: 'black',
49301 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49303 velocity_filter_weight: 0.7,
49306 * @cfg {function} Callback when stroke begin.
49311 * @cfg {function} Callback when stroke end.
49315 getAutoCreate : function()
49317 var cls = 'roo-signature column';
49320 cls += ' ' + this.cls;
49330 for(var i = 0; i < col_sizes.length; i++) {
49331 if(this[col_sizes[i]]) {
49332 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49342 cls: 'roo-signature-body',
49346 cls: 'roo-signature-body-canvas',
49347 height: this.canvas_height,
49348 width: this.canvas_width
49355 style: 'display: none'
49363 initEvents: function()
49365 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49367 var canvas = this.canvasEl();
49369 // mouse && touch event swapping...
49370 canvas.dom.style.touchAction = 'none';
49371 canvas.dom.style.msTouchAction = 'none';
49373 this.mouse_btn_down = false;
49374 canvas.on('mousedown', this._handleMouseDown, this);
49375 canvas.on('mousemove', this._handleMouseMove, this);
49376 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49378 if (window.PointerEvent) {
49379 canvas.on('pointerdown', this._handleMouseDown, this);
49380 canvas.on('pointermove', this._handleMouseMove, this);
49381 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49384 if ('ontouchstart' in window) {
49385 canvas.on('touchstart', this._handleTouchStart, this);
49386 canvas.on('touchmove', this._handleTouchMove, this);
49387 canvas.on('touchend', this._handleTouchEnd, this);
49390 Roo.EventManager.onWindowResize(this.resize, this, true);
49392 // file input event
49393 this.fileEl().on('change', this.uploadImage, this);
49400 resize: function(){
49402 var canvas = this.canvasEl().dom;
49403 var ctx = this.canvasElCtx();
49404 var img_data = false;
49406 if(canvas.width > 0) {
49407 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49409 // setting canvas width will clean img data
49412 var style = window.getComputedStyle ?
49413 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49415 var padding_left = parseInt(style.paddingLeft) || 0;
49416 var padding_right = parseInt(style.paddingRight) || 0;
49418 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49421 ctx.putImageData(img_data, 0, 0);
49425 _handleMouseDown: function(e)
49427 if (e.browserEvent.which === 1) {
49428 this.mouse_btn_down = true;
49429 this.strokeBegin(e);
49433 _handleMouseMove: function (e)
49435 if (this.mouse_btn_down) {
49436 this.strokeMoveUpdate(e);
49440 _handleMouseUp: function (e)
49442 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49443 this.mouse_btn_down = false;
49448 _handleTouchStart: function (e) {
49450 e.preventDefault();
49451 if (e.browserEvent.targetTouches.length === 1) {
49452 // var touch = e.browserEvent.changedTouches[0];
49453 // this.strokeBegin(touch);
49455 this.strokeBegin(e); // assume e catching the correct xy...
49459 _handleTouchMove: function (e) {
49460 e.preventDefault();
49461 // var touch = event.targetTouches[0];
49462 // _this._strokeMoveUpdate(touch);
49463 this.strokeMoveUpdate(e);
49466 _handleTouchEnd: function (e) {
49467 var wasCanvasTouched = e.target === this.canvasEl().dom;
49468 if (wasCanvasTouched) {
49469 e.preventDefault();
49470 // var touch = event.changedTouches[0];
49471 // _this._strokeEnd(touch);
49476 reset: function () {
49477 this._lastPoints = [];
49478 this._lastVelocity = 0;
49479 this._lastWidth = (this.min_width + this.max_width) / 2;
49480 this.canvasElCtx().fillStyle = this.dot_color;
49483 strokeMoveUpdate: function(e)
49485 this.strokeUpdate(e);
49487 if (this.throttle) {
49488 this.throttleStroke(this.strokeUpdate, this.throttle);
49491 this.strokeUpdate(e);
49495 strokeBegin: function(e)
49497 var newPointGroup = {
49498 color: this.dot_color,
49502 if (typeof this.onBegin === 'function') {
49506 this.curve_data.push(newPointGroup);
49508 this.strokeUpdate(e);
49511 strokeUpdate: function(e)
49513 var rect = this.canvasEl().dom.getBoundingClientRect();
49514 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49515 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49516 var lastPoints = lastPointGroup.points;
49517 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49518 var isLastPointTooClose = lastPoint
49519 ? point.distanceTo(lastPoint) <= this.min_distance
49521 var color = lastPointGroup.color;
49522 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49523 var curve = this.addPoint(point);
49525 this.drawDot({color: color, point: point});
49528 this.drawCurve({color: color, curve: curve});
49538 strokeEnd: function(e)
49540 this.strokeUpdate(e);
49541 if (typeof this.onEnd === 'function') {
49546 addPoint: function (point) {
49547 var _lastPoints = this._lastPoints;
49548 _lastPoints.push(point);
49549 if (_lastPoints.length > 2) {
49550 if (_lastPoints.length === 3) {
49551 _lastPoints.unshift(_lastPoints[0]);
49553 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49554 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49555 _lastPoints.shift();
49561 calculateCurveWidths: function (startPoint, endPoint) {
49562 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49563 (1 - this.velocity_filter_weight) * this._lastVelocity;
49565 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49568 start: this._lastWidth
49571 this._lastVelocity = velocity;
49572 this._lastWidth = newWidth;
49576 drawDot: function (_a) {
49577 var color = _a.color, point = _a.point;
49578 var ctx = this.canvasElCtx();
49579 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49581 this.drawCurveSegment(point.x, point.y, width);
49583 ctx.fillStyle = color;
49587 drawCurve: function (_a) {
49588 var color = _a.color, curve = _a.curve;
49589 var ctx = this.canvasElCtx();
49590 var widthDelta = curve.endWidth - curve.startWidth;
49591 var drawSteps = Math.floor(curve.length()) * 2;
49593 ctx.fillStyle = color;
49594 for (var i = 0; i < drawSteps; i += 1) {
49595 var t = i / drawSteps;
49601 var x = uuu * curve.startPoint.x;
49602 x += 3 * uu * t * curve.control1.x;
49603 x += 3 * u * tt * curve.control2.x;
49604 x += ttt * curve.endPoint.x;
49605 var y = uuu * curve.startPoint.y;
49606 y += 3 * uu * t * curve.control1.y;
49607 y += 3 * u * tt * curve.control2.y;
49608 y += ttt * curve.endPoint.y;
49609 var width = curve.startWidth + ttt * widthDelta;
49610 this.drawCurveSegment(x, y, width);
49616 drawCurveSegment: function (x, y, width) {
49617 var ctx = this.canvasElCtx();
49619 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49620 this.is_empty = false;
49625 var ctx = this.canvasElCtx();
49626 var canvas = this.canvasEl().dom;
49627 ctx.fillStyle = this.bg_color;
49628 ctx.clearRect(0, 0, canvas.width, canvas.height);
49629 ctx.fillRect(0, 0, canvas.width, canvas.height);
49630 this.curve_data = [];
49632 this.is_empty = true;
49637 return this.el.select('input',true).first();
49640 canvasEl: function()
49642 return this.el.select('canvas',true).first();
49645 canvasElCtx: function()
49647 return this.el.select('canvas',true).first().dom.getContext('2d');
49650 getImage: function(type)
49652 if(this.is_empty) {
49657 return this.canvasEl().dom.toDataURL('image/'+type, 1);
49660 drawFromImage: function(img_src)
49662 var img = new Image();
49664 img.onload = function(){
49665 this.canvasElCtx().drawImage(img, 0, 0);
49670 this.is_empty = false;
49673 selectImage: function()
49675 this.fileEl().dom.click();
49678 uploadImage: function(e)
49680 var reader = new FileReader();
49682 reader.onload = function(e){
49683 var img = new Image();
49684 img.onload = function(){
49686 this.canvasElCtx().drawImage(img, 0, 0);
49688 img.src = e.target.result;
49691 reader.readAsDataURL(e.target.files[0]);
49694 // Bezier Point Constructor
49695 Point: (function () {
49696 function Point(x, y, time) {
49699 this.time = time || Date.now();
49701 Point.prototype.distanceTo = function (start) {
49702 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49704 Point.prototype.equals = function (other) {
49705 return this.x === other.x && this.y === other.y && this.time === other.time;
49707 Point.prototype.velocityFrom = function (start) {
49708 return this.time !== start.time
49709 ? this.distanceTo(start) / (this.time - start.time)
49716 // Bezier Constructor
49717 Bezier: (function () {
49718 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49719 this.startPoint = startPoint;
49720 this.control2 = control2;
49721 this.control1 = control1;
49722 this.endPoint = endPoint;
49723 this.startWidth = startWidth;
49724 this.endWidth = endWidth;
49726 Bezier.fromPoints = function (points, widths, scope) {
49727 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49728 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49729 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49731 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49732 var dx1 = s1.x - s2.x;
49733 var dy1 = s1.y - s2.y;
49734 var dx2 = s2.x - s3.x;
49735 var dy2 = s2.y - s3.y;
49736 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49737 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49738 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49739 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49740 var dxm = m1.x - m2.x;
49741 var dym = m1.y - m2.y;
49742 var k = l2 / (l1 + l2);
49743 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49744 var tx = s2.x - cm.x;
49745 var ty = s2.y - cm.y;
49747 c1: new scope.Point(m1.x + tx, m1.y + ty),
49748 c2: new scope.Point(m2.x + tx, m2.y + ty)
49751 Bezier.prototype.length = function () {
49756 for (var i = 0; i <= steps; i += 1) {
49758 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49759 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49761 var xdiff = cx - px;
49762 var ydiff = cy - py;
49763 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49770 Bezier.prototype.point = function (t, start, c1, c2, end) {
49771 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49772 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49773 + (3.0 * c2 * (1.0 - t) * t * t)
49774 + (end * t * t * t);
49779 throttleStroke: function(fn, wait) {
49780 if (wait === void 0) { wait = 250; }
49782 var timeout = null;
49786 var later = function () {
49787 previous = Date.now();
49789 result = fn.apply(storedContext, storedArgs);
49791 storedContext = null;
49795 return function wrapper() {
49797 for (var _i = 0; _i < arguments.length; _i++) {
49798 args[_i] = arguments[_i];
49800 var now = Date.now();
49801 var remaining = wait - (now - previous);
49802 storedContext = this;
49804 if (remaining <= 0 || remaining > wait) {
49806 clearTimeout(timeout);
49810 result = fn.apply(storedContext, storedArgs);
49812 storedContext = null;
49816 else if (!timeout) {
49817 timeout = window.setTimeout(later, remaining);
49827 // old names for form elements
49828 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
49829 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
49830 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
49831 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
49832 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
49833 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
49834 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
49835 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
49836 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
49837 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
49838 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
49839 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
49840 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
49841 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
49842 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
49843 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
49844 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
49845 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
49846 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
49847 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
49848 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
49849 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
49850 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
49851 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
49852 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
49853 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
49855 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
49856 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49858 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
49859 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
49861 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
49862 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49863 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
49864 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator