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'
13956 if (Roo.bootstrap.version == 4) {
13959 style : 'display:none'
13964 if (align ==='left' && this.fieldLabel.length) {
13966 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13973 cls : 'control-label',
13974 html : this.fieldLabel
13986 var labelCfg = cfg.cn[1];
13987 var contentCfg = cfg.cn[2];
13989 if(this.indicatorpos == 'right'){
13994 cls : 'control-label',
13998 html : this.fieldLabel
14012 labelCfg = cfg.cn[0];
14013 contentCfg = cfg.cn[1];
14016 if(this.labelWidth > 12){
14017 labelCfg.style = "width: " + this.labelWidth + 'px';
14020 if(this.labelWidth < 13 && this.labelmd == 0){
14021 this.labelmd = this.labelWidth;
14024 if(this.labellg > 0){
14025 labelCfg.cls += ' col-lg-' + this.labellg;
14026 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14029 if(this.labelmd > 0){
14030 labelCfg.cls += ' col-md-' + this.labelmd;
14031 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14034 if(this.labelsm > 0){
14035 labelCfg.cls += ' col-sm-' + this.labelsm;
14036 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14039 if(this.labelxs > 0){
14040 labelCfg.cls += ' col-xs-' + this.labelxs;
14041 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14044 } else if ( this.fieldLabel.length) {
14045 // Roo.log(" label");
14050 //cls : 'input-group-addon',
14051 html : this.fieldLabel
14059 if(this.indicatorpos == 'right'){
14067 html : this.fieldLabel
14081 // Roo.log(" no label && no align");
14088 ['xs','sm','md','lg'].map(function(size){
14089 if (settings[size]) {
14090 cfg.cls += ' col-' + size + '-' + settings[size];
14101 onResize : function(w, h){
14102 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14103 // if(typeof w == 'number'){
14104 // var x = w - this.trigger.getWidth();
14105 // this.inputEl().setWidth(this.adjustWidth('input', x));
14106 // this.trigger.setStyle('left', x+'px');
14111 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14114 getResizeEl : function(){
14115 return this.inputEl();
14119 getPositionEl : function(){
14120 return this.inputEl();
14124 alignErrorIcon : function(){
14125 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14129 initEvents : function(){
14133 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14134 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14135 if(!this.multiple && this.showToggleBtn){
14136 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14137 if(this.hideTrigger){
14138 this.trigger.setDisplayed(false);
14140 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14144 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14147 if(this.removable && !this.editable && !this.tickable){
14148 var close = this.closeTriggerEl();
14151 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14152 close.on('click', this.removeBtnClick, this, close);
14156 //this.trigger.addClassOnOver('x-form-trigger-over');
14157 //this.trigger.addClassOnClick('x-form-trigger-click');
14160 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14164 closeTriggerEl : function()
14166 var close = this.el.select('.roo-combo-removable-btn', true).first();
14167 return close ? close : false;
14170 removeBtnClick : function(e, h, el)
14172 e.preventDefault();
14174 if(this.fireEvent("remove", this) !== false){
14176 this.fireEvent("afterremove", this)
14180 createList : function()
14182 this.list = Roo.get(document.body).createChild({
14183 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14184 cls: 'typeahead typeahead-long dropdown-menu shadow',
14185 style: 'display:none'
14188 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14193 initTrigger : function(){
14198 onDestroy : function(){
14200 this.trigger.removeAllListeners();
14201 // this.trigger.remove();
14204 // this.wrap.remove();
14206 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14210 onFocus : function(){
14211 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14213 if(!this.mimicing){
14214 this.wrap.addClass('x-trigger-wrap-focus');
14215 this.mimicing = true;
14216 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14217 if(this.monitorTab){
14218 this.el.on("keydown", this.checkTab, this);
14225 checkTab : function(e){
14226 if(e.getKey() == e.TAB){
14227 this.triggerBlur();
14232 onBlur : function(){
14237 mimicBlur : function(e, t){
14239 if(!this.wrap.contains(t) && this.validateBlur()){
14240 this.triggerBlur();
14246 triggerBlur : function(){
14247 this.mimicing = false;
14248 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14249 if(this.monitorTab){
14250 this.el.un("keydown", this.checkTab, this);
14252 //this.wrap.removeClass('x-trigger-wrap-focus');
14253 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14257 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14258 validateBlur : function(e, t){
14263 onDisable : function(){
14264 this.inputEl().dom.disabled = true;
14265 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14267 // this.wrap.addClass('x-item-disabled');
14272 onEnable : function(){
14273 this.inputEl().dom.disabled = false;
14274 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14276 // this.el.removeClass('x-item-disabled');
14281 onShow : function(){
14282 var ae = this.getActionEl();
14285 ae.dom.style.display = '';
14286 ae.dom.style.visibility = 'visible';
14292 onHide : function(){
14293 var ae = this.getActionEl();
14294 ae.dom.style.display = 'none';
14298 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14299 * by an implementing function.
14301 * @param {EventObject} e
14303 onTriggerClick : Roo.emptyFn
14311 * @class Roo.bootstrap.form.CardUploader
14312 * @extends Roo.bootstrap.Button
14313 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14314 * @cfg {Number} errorTimeout default 3000
14315 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14316 * @cfg {Array} html The button text.
14320 * Create a new CardUploader
14321 * @param {Object} config The config object
14324 Roo.bootstrap.form.CardUploader = function(config){
14328 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14331 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14339 * When a image is clicked on - and needs to display a slideshow or similar..
14340 * @param {Roo.bootstrap.Card} this
14341 * @param {Object} The image information data
14347 * When a the download link is clicked
14348 * @param {Roo.bootstrap.Card} this
14349 * @param {Object} The image information data contains
14356 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14359 errorTimeout : 3000,
14363 fileCollection : false,
14366 getAutoCreate : function()
14370 cls :'form-group' ,
14375 //cls : 'input-group-addon',
14376 html : this.fieldLabel
14384 value : this.value,
14385 cls : 'd-none form-control'
14390 multiple : 'multiple',
14392 cls : 'd-none roo-card-upload-selector'
14396 cls : 'roo-card-uploader-button-container w-100 mb-2'
14399 cls : 'card-columns roo-card-uploader-container'
14409 getChildContainer : function() /// what children are added to.
14411 return this.containerEl;
14414 getButtonContainer : function() /// what children are added to.
14416 return this.el.select(".roo-card-uploader-button-container").first();
14419 initEvents : function()
14422 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14426 xns: Roo.bootstrap,
14429 container_method : 'getButtonContainer' ,
14430 html : this.html, // fix changable?
14433 'click' : function(btn, e) {
14442 this.urlAPI = (window.createObjectURL && window) ||
14443 (window.URL && URL.revokeObjectURL && URL) ||
14444 (window.webkitURL && webkitURL);
14449 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14451 this.selectorEl.on('change', this.onFileSelected, this);
14454 this.images.forEach(function(img) {
14457 this.images = false;
14459 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14465 onClick : function(e)
14467 e.preventDefault();
14469 this.selectorEl.dom.click();
14473 onFileSelected : function(e)
14475 e.preventDefault();
14477 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14481 Roo.each(this.selectorEl.dom.files, function(file){
14482 this.addFile(file);
14491 addFile : function(file)
14494 if(typeof(file) === 'string'){
14495 throw "Add file by name?"; // should not happen
14499 if(!file || !this.urlAPI){
14509 var url = _this.urlAPI.createObjectURL( file);
14512 id : Roo.bootstrap.form.CardUploader.ID--,
14513 is_uploaded : false,
14517 mimetype : file.type,
14525 * addCard - add an Attachment to the uploader
14526 * @param data - the data about the image to upload
14530 title : "Title of file",
14531 is_uploaded : false,
14532 src : "http://.....",
14533 srcfile : { the File upload object },
14534 mimetype : file.type,
14537 .. any other data...
14543 addCard : function (data)
14545 // hidden input element?
14546 // if the file is not an image...
14547 //then we need to use something other that and header_image
14552 xns : Roo.bootstrap,
14553 xtype : 'CardFooter',
14556 xns : Roo.bootstrap,
14562 xns : Roo.bootstrap,
14564 html : String.format("<small>{0}</small>", data.title),
14565 cls : 'col-10 text-left',
14570 click : function() {
14572 t.fireEvent( "download", t, data );
14578 xns : Roo.bootstrap,
14580 style: 'max-height: 28px; ',
14586 click : function() {
14587 t.removeCard(data.id)
14599 var cn = this.addxtype(
14602 xns : Roo.bootstrap,
14605 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14606 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14607 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14612 initEvents : function() {
14613 Roo.bootstrap.Card.prototype.initEvents.call(this);
14615 this.imgEl = this.el.select('.card-img-top').first();
14617 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14618 this.imgEl.set({ 'pointer' : 'cursor' });
14621 this.getCardFooter().addClass('p-1');
14628 // dont' really need ot update items.
14629 // this.items.push(cn);
14630 this.fileCollection.add(cn);
14632 if (!data.srcfile) {
14633 this.updateInput();
14638 var reader = new FileReader();
14639 reader.addEventListener("load", function() {
14640 data.srcdata = reader.result;
14643 reader.readAsDataURL(data.srcfile);
14648 removeCard : function(id)
14651 var card = this.fileCollection.get(id);
14652 card.data.is_deleted = 1;
14653 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14654 //this.fileCollection.remove(card);
14655 //this.items = this.items.filter(function(e) { return e != card });
14656 // dont' really need ot update items.
14657 card.el.dom.parentNode.removeChild(card.el.dom);
14658 this.updateInput();
14664 this.fileCollection.each(function(card) {
14665 if (card.el.dom && card.el.dom.parentNode) {
14666 card.el.dom.parentNode.removeChild(card.el.dom);
14669 this.fileCollection.clear();
14670 this.updateInput();
14673 updateInput : function()
14676 this.fileCollection.each(function(e) {
14680 this.inputEl().dom.value = JSON.stringify(data);
14690 Roo.bootstrap.form.CardUploader.ID = -1;/*
14692 * Ext JS Library 1.1.1
14693 * Copyright(c) 2006-2007, Ext JS, LLC.
14695 * Originally Released Under LGPL - original licence link has changed is not relivant.
14698 * <script type="text/javascript">
14703 * @class Roo.data.SortTypes
14705 * Defines the default sorting (casting?) comparison functions used when sorting data.
14707 Roo.data.SortTypes = {
14709 * Default sort that does nothing
14710 * @param {Mixed} s The value being converted
14711 * @return {Mixed} The comparison value
14713 none : function(s){
14718 * The regular expression used to strip tags
14722 stripTagsRE : /<\/?[^>]+>/gi,
14725 * Strips all HTML tags to sort on text only
14726 * @param {Mixed} s The value being converted
14727 * @return {String} The comparison value
14729 asText : function(s){
14730 return String(s).replace(this.stripTagsRE, "");
14734 * Strips all HTML tags to sort on text only - Case insensitive
14735 * @param {Mixed} s The value being converted
14736 * @return {String} The comparison value
14738 asUCText : function(s){
14739 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14743 * Case insensitive string
14744 * @param {Mixed} s The value being converted
14745 * @return {String} The comparison value
14747 asUCString : function(s) {
14748 return String(s).toUpperCase();
14753 * @param {Mixed} s The value being converted
14754 * @return {Number} The comparison value
14756 asDate : function(s) {
14760 if(s instanceof Date){
14761 return s.getTime();
14763 return Date.parse(String(s));
14768 * @param {Mixed} s The value being converted
14769 * @return {Float} The comparison value
14771 asFloat : function(s) {
14772 var val = parseFloat(String(s).replace(/,/g, ""));
14781 * @param {Mixed} s The value being converted
14782 * @return {Number} The comparison value
14784 asInt : function(s) {
14785 var val = parseInt(String(s).replace(/,/g, ""));
14793 * Ext JS Library 1.1.1
14794 * Copyright(c) 2006-2007, Ext JS, LLC.
14796 * Originally Released Under LGPL - original licence link has changed is not relivant.
14799 * <script type="text/javascript">
14803 * @class Roo.data.Record
14804 * Instances of this class encapsulate both record <em>definition</em> information, and record
14805 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14806 * to access Records cached in an {@link Roo.data.Store} object.<br>
14808 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14809 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14812 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14814 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14815 * {@link #create}. The parameters are the same.
14816 * @param {Array} data An associative Array of data values keyed by the field name.
14817 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14818 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14819 * not specified an integer id is generated.
14821 Roo.data.Record = function(data, id){
14822 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14827 * Generate a constructor for a specific record layout.
14828 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14829 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14830 * Each field definition object may contain the following properties: <ul>
14831 * <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,
14832 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14833 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14834 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14835 * is being used, then this is a string containing the javascript expression to reference the data relative to
14836 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14837 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14838 * this may be omitted.</p></li>
14839 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14840 * <ul><li>auto (Default, implies no conversion)</li>
14845 * <li>date</li></ul></p></li>
14846 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14847 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14848 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14849 * by the Reader into an object that will be stored in the Record. It is passed the
14850 * following parameters:<ul>
14851 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14853 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14855 * <br>usage:<br><pre><code>
14856 var TopicRecord = Roo.data.Record.create(
14857 {name: 'title', mapping: 'topic_title'},
14858 {name: 'author', mapping: 'username'},
14859 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14860 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14861 {name: 'lastPoster', mapping: 'user2'},
14862 {name: 'excerpt', mapping: 'post_text'}
14865 var myNewRecord = new TopicRecord({
14866 title: 'Do my job please',
14869 lastPost: new Date(),
14870 lastPoster: 'Animal',
14871 excerpt: 'No way dude!'
14873 myStore.add(myNewRecord);
14878 Roo.data.Record.create = function(o){
14879 var f = function(){
14880 f.superclass.constructor.apply(this, arguments);
14882 Roo.extend(f, Roo.data.Record);
14883 var p = f.prototype;
14884 p.fields = new Roo.util.MixedCollection(false, function(field){
14887 for(var i = 0, len = o.length; i < len; i++){
14888 p.fields.add(new Roo.data.Field(o[i]));
14890 f.getField = function(name){
14891 return p.fields.get(name);
14896 Roo.data.Record.AUTO_ID = 1000;
14897 Roo.data.Record.EDIT = 'edit';
14898 Roo.data.Record.REJECT = 'reject';
14899 Roo.data.Record.COMMIT = 'commit';
14901 Roo.data.Record.prototype = {
14903 * Readonly flag - true if this record has been modified.
14912 join : function(store){
14913 this.store = store;
14917 * Set the named field to the specified value.
14918 * @param {String} name The name of the field to set.
14919 * @param {Object} value The value to set the field to.
14921 set : function(name, value){
14922 if(this.data[name] == value){
14926 if(!this.modified){
14927 this.modified = {};
14929 if(typeof this.modified[name] == 'undefined'){
14930 this.modified[name] = this.data[name];
14932 this.data[name] = value;
14933 if(!this.editing && this.store){
14934 this.store.afterEdit(this);
14939 * Get the value of the named field.
14940 * @param {String} name The name of the field to get the value of.
14941 * @return {Object} The value of the field.
14943 get : function(name){
14944 return this.data[name];
14948 beginEdit : function(){
14949 this.editing = true;
14950 this.modified = {};
14954 cancelEdit : function(){
14955 this.editing = false;
14956 delete this.modified;
14960 endEdit : function(){
14961 this.editing = false;
14962 if(this.dirty && this.store){
14963 this.store.afterEdit(this);
14968 * Usually called by the {@link Roo.data.Store} which owns the Record.
14969 * Rejects all changes made to the Record since either creation, or the last commit operation.
14970 * Modified fields are reverted to their original values.
14972 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14973 * of reject operations.
14975 reject : function(){
14976 var m = this.modified;
14978 if(typeof m[n] != "function"){
14979 this.data[n] = m[n];
14982 this.dirty = false;
14983 delete this.modified;
14984 this.editing = false;
14986 this.store.afterReject(this);
14991 * Usually called by the {@link Roo.data.Store} which owns the Record.
14992 * Commits all changes made to the Record since either creation, or the last commit operation.
14994 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14995 * of commit operations.
14997 commit : function(){
14998 this.dirty = false;
14999 delete this.modified;
15000 this.editing = false;
15002 this.store.afterCommit(this);
15007 hasError : function(){
15008 return this.error != null;
15012 clearError : function(){
15017 * Creates a copy of this record.
15018 * @param {String} id (optional) A new record id if you don't want to use this record's id
15021 copy : function(newId) {
15022 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15026 * Ext JS Library 1.1.1
15027 * Copyright(c) 2006-2007, Ext JS, LLC.
15029 * Originally Released Under LGPL - original licence link has changed is not relivant.
15032 * <script type="text/javascript">
15038 * @class Roo.data.Store
15039 * @extends Roo.util.Observable
15040 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15041 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15043 * 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
15044 * has no knowledge of the format of the data returned by the Proxy.<br>
15046 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15047 * instances from the data object. These records are cached and made available through accessor functions.
15049 * Creates a new Store.
15050 * @param {Object} config A config object containing the objects needed for the Store to access data,
15051 * and read the data into Records.
15053 Roo.data.Store = function(config){
15054 this.data = new Roo.util.MixedCollection(false);
15055 this.data.getKey = function(o){
15058 this.baseParams = {};
15060 this.paramNames = {
15065 "multisort" : "_multisort"
15068 if(config && config.data){
15069 this.inlineData = config.data;
15070 delete config.data;
15073 Roo.apply(this, config);
15075 if(this.reader){ // reader passed
15076 this.reader = Roo.factory(this.reader, Roo.data);
15077 this.reader.xmodule = this.xmodule || false;
15078 if(!this.recordType){
15079 this.recordType = this.reader.recordType;
15081 if(this.reader.onMetaChange){
15082 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15086 if(this.recordType){
15087 this.fields = this.recordType.prototype.fields;
15089 this.modified = [];
15093 * @event datachanged
15094 * Fires when the data cache has changed, and a widget which is using this Store
15095 * as a Record cache should refresh its view.
15096 * @param {Store} this
15098 datachanged : true,
15100 * @event metachange
15101 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15102 * @param {Store} this
15103 * @param {Object} meta The JSON metadata
15108 * Fires when Records have been added to the Store
15109 * @param {Store} this
15110 * @param {Roo.data.Record[]} records The array of Records added
15111 * @param {Number} index The index at which the record(s) were added
15116 * Fires when a Record has been removed from the Store
15117 * @param {Store} this
15118 * @param {Roo.data.Record} record The Record that was removed
15119 * @param {Number} index The index at which the record was removed
15124 * Fires when a Record has been updated
15125 * @param {Store} this
15126 * @param {Roo.data.Record} record The Record that was updated
15127 * @param {String} operation The update operation being performed. Value may be one of:
15129 Roo.data.Record.EDIT
15130 Roo.data.Record.REJECT
15131 Roo.data.Record.COMMIT
15137 * Fires when the data cache has been cleared.
15138 * @param {Store} this
15142 * @event beforeload
15143 * Fires before a request is made for a new data object. If the beforeload handler returns false
15144 * the load action will be canceled.
15145 * @param {Store} this
15146 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15150 * @event beforeloadadd
15151 * Fires after a new set of Records has been loaded.
15152 * @param {Store} this
15153 * @param {Roo.data.Record[]} records The Records that were loaded
15154 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15156 beforeloadadd : true,
15159 * Fires after a new set of Records has been loaded, before they are added to the store.
15160 * @param {Store} this
15161 * @param {Roo.data.Record[]} records The Records that were loaded
15162 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15163 * @params {Object} return from reader
15167 * @event loadexception
15168 * Fires if an exception occurs in the Proxy during loading.
15169 * Called with the signature of the Proxy's "loadexception" event.
15170 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15173 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15174 * @param {Object} load options
15175 * @param {Object} jsonData from your request (normally this contains the Exception)
15177 loadexception : true
15181 this.proxy = Roo.factory(this.proxy, Roo.data);
15182 this.proxy.xmodule = this.xmodule || false;
15183 this.relayEvents(this.proxy, ["loadexception"]);
15185 this.sortToggle = {};
15186 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15188 Roo.data.Store.superclass.constructor.call(this);
15190 if(this.inlineData){
15191 this.loadData(this.inlineData);
15192 delete this.inlineData;
15196 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15198 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15199 * without a remote query - used by combo/forms at present.
15203 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15206 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15209 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15210 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15213 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15214 * on any HTTP request
15217 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15220 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15224 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15225 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15227 remoteSort : false,
15230 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15231 * loaded or when a record is removed. (defaults to false).
15233 pruneModifiedRecords : false,
15236 lastOptions : null,
15239 * Add Records to the Store and fires the add event.
15240 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15242 add : function(records){
15243 records = [].concat(records);
15244 for(var i = 0, len = records.length; i < len; i++){
15245 records[i].join(this);
15247 var index = this.data.length;
15248 this.data.addAll(records);
15249 this.fireEvent("add", this, records, index);
15253 * Remove a Record from the Store and fires the remove event.
15254 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15256 remove : function(record){
15257 var index = this.data.indexOf(record);
15258 this.data.removeAt(index);
15260 if(this.pruneModifiedRecords){
15261 this.modified.remove(record);
15263 this.fireEvent("remove", this, record, index);
15267 * Remove all Records from the Store and fires the clear event.
15269 removeAll : function(){
15271 if(this.pruneModifiedRecords){
15272 this.modified = [];
15274 this.fireEvent("clear", this);
15278 * Inserts Records to the Store at the given index and fires the add event.
15279 * @param {Number} index The start index at which to insert the passed Records.
15280 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15282 insert : function(index, records){
15283 records = [].concat(records);
15284 for(var i = 0, len = records.length; i < len; i++){
15285 this.data.insert(index, records[i]);
15286 records[i].join(this);
15288 this.fireEvent("add", this, records, index);
15292 * Get the index within the cache of the passed Record.
15293 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15294 * @return {Number} The index of the passed Record. Returns -1 if not found.
15296 indexOf : function(record){
15297 return this.data.indexOf(record);
15301 * Get the index within the cache of the Record with the passed id.
15302 * @param {String} id The id of the Record to find.
15303 * @return {Number} The index of the Record. Returns -1 if not found.
15305 indexOfId : function(id){
15306 return this.data.indexOfKey(id);
15310 * Get the Record with the specified id.
15311 * @param {String} id The id of the Record to find.
15312 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15314 getById : function(id){
15315 return this.data.key(id);
15319 * Get the Record at the specified index.
15320 * @param {Number} index The index of the Record to find.
15321 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15323 getAt : function(index){
15324 return this.data.itemAt(index);
15328 * Returns a range of Records between specified indices.
15329 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15330 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15331 * @return {Roo.data.Record[]} An array of Records
15333 getRange : function(start, end){
15334 return this.data.getRange(start, end);
15338 storeOptions : function(o){
15339 o = Roo.apply({}, o);
15342 this.lastOptions = o;
15346 * Loads the Record cache from the configured Proxy using the configured Reader.
15348 * If using remote paging, then the first load call must specify the <em>start</em>
15349 * and <em>limit</em> properties in the options.params property to establish the initial
15350 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15352 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15353 * and this call will return before the new data has been loaded. Perform any post-processing
15354 * in a callback function, or in a "load" event handler.</strong>
15356 * @param {Object} options An object containing properties which control loading options:<ul>
15357 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15358 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15361 data : data, // array of key=>value data like JsonReader
15362 total : data.length,
15368 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15369 * passed the following arguments:<ul>
15370 * <li>r : Roo.data.Record[]</li>
15371 * <li>options: Options object from the load call</li>
15372 * <li>success: Boolean success indicator</li></ul></li>
15373 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15374 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15377 load : function(options){
15378 options = options || {};
15379 if(this.fireEvent("beforeload", this, options) !== false){
15380 this.storeOptions(options);
15381 var p = Roo.apply(options.params || {}, this.baseParams);
15382 // if meta was not loaded from remote source.. try requesting it.
15383 if (!this.reader.metaFromRemote) {
15384 p._requestMeta = 1;
15386 if(this.sortInfo && this.remoteSort){
15387 var pn = this.paramNames;
15388 p[pn["sort"]] = this.sortInfo.field;
15389 p[pn["dir"]] = this.sortInfo.direction;
15391 if (this.multiSort) {
15392 var pn = this.paramNames;
15393 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15396 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15401 * Reloads the Record cache from the configured Proxy using the configured Reader and
15402 * the options from the last load operation performed.
15403 * @param {Object} options (optional) An object containing properties which may override the options
15404 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15405 * the most recently used options are reused).
15407 reload : function(options){
15408 this.load(Roo.applyIf(options||{}, this.lastOptions));
15412 // Called as a callback by the Reader during a load operation.
15413 loadRecords : function(o, options, success){
15416 if(success !== false){
15417 this.fireEvent("load", this, [], options, o);
15419 if(options.callback){
15420 options.callback.call(options.scope || this, [], options, false);
15424 // if data returned failure - throw an exception.
15425 if (o.success === false) {
15426 // show a message if no listener is registered.
15427 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15428 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15430 // loadmask wil be hooked into this..
15431 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15434 var r = o.records, t = o.totalRecords || r.length;
15436 this.fireEvent("beforeloadadd", this, r, options, o);
15438 if(!options || options.add !== true){
15439 if(this.pruneModifiedRecords){
15440 this.modified = [];
15442 for(var i = 0, len = r.length; i < len; i++){
15446 this.data = this.snapshot;
15447 delete this.snapshot;
15450 this.data.addAll(r);
15451 this.totalLength = t;
15453 this.fireEvent("datachanged", this);
15455 this.totalLength = Math.max(t, this.data.length+r.length);
15459 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15461 var e = new Roo.data.Record({});
15463 e.set(this.parent.displayField, this.parent.emptyTitle);
15464 e.set(this.parent.valueField, '');
15469 this.fireEvent("load", this, r, options, o);
15470 if(options.callback){
15471 options.callback.call(options.scope || this, r, options, true);
15477 * Loads data from a passed data block. A Reader which understands the format of the data
15478 * must have been configured in the constructor.
15479 * @param {Object} data The data block from which to read the Records. The format of the data expected
15480 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15481 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15483 loadData : function(o, append){
15484 var r = this.reader.readRecords(o);
15485 this.loadRecords(r, {add: append}, true);
15489 * using 'cn' the nested child reader read the child array into it's child stores.
15490 * @param {Object} rec The record with a 'children array
15492 loadDataFromChildren : function(rec)
15494 this.loadData(this.reader.toLoadData(rec));
15499 * Gets the number of cached records.
15501 * <em>If using paging, this may not be the total size of the dataset. If the data object
15502 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15503 * the data set size</em>
15505 getCount : function(){
15506 return this.data.length || 0;
15510 * Gets the total number of records in the dataset as returned by the server.
15512 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15513 * the dataset size</em>
15515 getTotalCount : function(){
15516 return this.totalLength || 0;
15520 * Returns the sort state of the Store as an object with two properties:
15522 field {String} The name of the field by which the Records are sorted
15523 direction {String} The sort order, "ASC" or "DESC"
15526 getSortState : function(){
15527 return this.sortInfo;
15531 applySort : function(){
15532 if(this.sortInfo && !this.remoteSort){
15533 var s = this.sortInfo, f = s.field;
15534 var st = this.fields.get(f).sortType;
15535 var fn = function(r1, r2){
15536 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15537 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15539 this.data.sort(s.direction, fn);
15540 if(this.snapshot && this.snapshot != this.data){
15541 this.snapshot.sort(s.direction, fn);
15547 * Sets the default sort column and order to be used by the next load operation.
15548 * @param {String} fieldName The name of the field to sort by.
15549 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15551 setDefaultSort : function(field, dir){
15552 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15556 * Sort the Records.
15557 * If remote sorting is used, the sort is performed on the server, and the cache is
15558 * reloaded. If local sorting is used, the cache is sorted internally.
15559 * @param {String} fieldName The name of the field to sort by.
15560 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15562 sort : function(fieldName, dir){
15563 var f = this.fields.get(fieldName);
15565 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15567 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15568 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15573 this.sortToggle[f.name] = dir;
15574 this.sortInfo = {field: f.name, direction: dir};
15575 if(!this.remoteSort){
15577 this.fireEvent("datachanged", this);
15579 this.load(this.lastOptions);
15584 * Calls the specified function for each of the Records in the cache.
15585 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15586 * Returning <em>false</em> aborts and exits the iteration.
15587 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15589 each : function(fn, scope){
15590 this.data.each(fn, scope);
15594 * Gets all records modified since the last commit. Modified records are persisted across load operations
15595 * (e.g., during paging).
15596 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15598 getModifiedRecords : function(){
15599 return this.modified;
15603 createFilterFn : function(property, value, anyMatch){
15604 if(!value.exec){ // not a regex
15605 value = String(value);
15606 if(value.length == 0){
15609 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15611 return function(r){
15612 return value.test(r.data[property]);
15617 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15618 * @param {String} property A field on your records
15619 * @param {Number} start The record index to start at (defaults to 0)
15620 * @param {Number} end The last record index to include (defaults to length - 1)
15621 * @return {Number} The sum
15623 sum : function(property, start, end){
15624 var rs = this.data.items, v = 0;
15625 start = start || 0;
15626 end = (end || end === 0) ? end : rs.length-1;
15628 for(var i = start; i <= end; i++){
15629 v += (rs[i].data[property] || 0);
15635 * Filter the records by a specified property.
15636 * @param {String} field A field on your records
15637 * @param {String/RegExp} value Either a string that the field
15638 * should start with or a RegExp to test against the field
15639 * @param {Boolean} anyMatch True to match any part not just the beginning
15641 filter : function(property, value, anyMatch){
15642 var fn = this.createFilterFn(property, value, anyMatch);
15643 return fn ? this.filterBy(fn) : this.clearFilter();
15647 * Filter by a function. The specified function will be called with each
15648 * record in this data source. If the function returns true the record is included,
15649 * otherwise it is filtered.
15650 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15651 * @param {Object} scope (optional) The scope of the function (defaults to this)
15653 filterBy : function(fn, scope){
15654 this.snapshot = this.snapshot || this.data;
15655 this.data = this.queryBy(fn, scope||this);
15656 this.fireEvent("datachanged", this);
15660 * Query the records by a specified property.
15661 * @param {String} field A field on your records
15662 * @param {String/RegExp} value Either a string that the field
15663 * should start with or a RegExp to test against the field
15664 * @param {Boolean} anyMatch True to match any part not just the beginning
15665 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15667 query : function(property, value, anyMatch){
15668 var fn = this.createFilterFn(property, value, anyMatch);
15669 return fn ? this.queryBy(fn) : this.data.clone();
15673 * Query by a function. The specified function will be called with each
15674 * record in this data source. If the function returns true the record is included
15676 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15677 * @param {Object} scope (optional) The scope of the function (defaults to this)
15678 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15680 queryBy : function(fn, scope){
15681 var data = this.snapshot || this.data;
15682 return data.filterBy(fn, scope||this);
15686 * Collects unique values for a particular dataIndex from this store.
15687 * @param {String} dataIndex The property to collect
15688 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15689 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15690 * @return {Array} An array of the unique values
15692 collect : function(dataIndex, allowNull, bypassFilter){
15693 var d = (bypassFilter === true && this.snapshot) ?
15694 this.snapshot.items : this.data.items;
15695 var v, sv, r = [], l = {};
15696 for(var i = 0, len = d.length; i < len; i++){
15697 v = d[i].data[dataIndex];
15699 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15708 * Revert to a view of the Record cache with no filtering applied.
15709 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15711 clearFilter : function(suppressEvent){
15712 if(this.snapshot && this.snapshot != this.data){
15713 this.data = this.snapshot;
15714 delete this.snapshot;
15715 if(suppressEvent !== true){
15716 this.fireEvent("datachanged", this);
15722 afterEdit : function(record){
15723 if(this.modified.indexOf(record) == -1){
15724 this.modified.push(record);
15726 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15730 afterReject : function(record){
15731 this.modified.remove(record);
15732 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15736 afterCommit : function(record){
15737 this.modified.remove(record);
15738 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15742 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15743 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15745 commitChanges : function(){
15746 var m = this.modified.slice(0);
15747 this.modified = [];
15748 for(var i = 0, len = m.length; i < len; i++){
15754 * Cancel outstanding changes on all changed records.
15756 rejectChanges : function(){
15757 var m = this.modified.slice(0);
15758 this.modified = [];
15759 for(var i = 0, len = m.length; i < len; i++){
15764 onMetaChange : function(meta, rtype, o){
15765 this.recordType = rtype;
15766 this.fields = rtype.prototype.fields;
15767 delete this.snapshot;
15768 this.sortInfo = meta.sortInfo || this.sortInfo;
15769 this.modified = [];
15770 this.fireEvent('metachange', this, this.reader.meta);
15773 moveIndex : function(data, type)
15775 var index = this.indexOf(data);
15777 var newIndex = index + type;
15781 this.insert(newIndex, data);
15786 * Ext JS Library 1.1.1
15787 * Copyright(c) 2006-2007, Ext JS, LLC.
15789 * Originally Released Under LGPL - original licence link has changed is not relivant.
15792 * <script type="text/javascript">
15796 * @class Roo.data.SimpleStore
15797 * @extends Roo.data.Store
15798 * Small helper class to make creating Stores from Array data easier.
15799 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15800 * @cfg {Array} fields An array of field definition objects, or field name strings.
15801 * @cfg {Object} an existing reader (eg. copied from another store)
15802 * @cfg {Array} data The multi-dimensional array of data
15803 * @cfg {Roo.data.DataProxy} proxy [not-required]
15804 * @cfg {Roo.data.Reader} reader [not-required]
15806 * @param {Object} config
15808 Roo.data.SimpleStore = function(config)
15810 Roo.data.SimpleStore.superclass.constructor.call(this, {
15812 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15815 Roo.data.Record.create(config.fields)
15817 proxy : new Roo.data.MemoryProxy(config.data)
15821 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15823 * Ext JS Library 1.1.1
15824 * Copyright(c) 2006-2007, Ext JS, LLC.
15826 * Originally Released Under LGPL - original licence link has changed is not relivant.
15829 * <script type="text/javascript">
15834 * @extends Roo.data.Store
15835 * @class Roo.data.JsonStore
15836 * Small helper class to make creating Stores for JSON data easier. <br/>
15838 var store = new Roo.data.JsonStore({
15839 url: 'get-images.php',
15841 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15844 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15845 * JsonReader and HttpProxy (unless inline data is provided).</b>
15846 * @cfg {Array} fields An array of field definition objects, or field name strings.
15848 * @param {Object} config
15850 Roo.data.JsonStore = function(c){
15851 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15852 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15853 reader: new Roo.data.JsonReader(c, c.fields)
15856 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15858 * Ext JS Library 1.1.1
15859 * Copyright(c) 2006-2007, Ext JS, LLC.
15861 * Originally Released Under LGPL - original licence link has changed is not relivant.
15864 * <script type="text/javascript">
15868 Roo.data.Field = function(config){
15869 if(typeof config == "string"){
15870 config = {name: config};
15872 Roo.apply(this, config);
15875 this.type = "auto";
15878 var st = Roo.data.SortTypes;
15879 // named sortTypes are supported, here we look them up
15880 if(typeof this.sortType == "string"){
15881 this.sortType = st[this.sortType];
15884 // set default sortType for strings and dates
15885 if(!this.sortType){
15888 this.sortType = st.asUCString;
15891 this.sortType = st.asDate;
15894 this.sortType = st.none;
15899 var stripRe = /[\$,%]/g;
15901 // prebuilt conversion function for this field, instead of
15902 // switching every time we're reading a value
15904 var cv, dateFormat = this.dateFormat;
15909 cv = function(v){ return v; };
15912 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15916 return v !== undefined && v !== null && v !== '' ?
15917 parseInt(String(v).replace(stripRe, ""), 10) : '';
15922 return v !== undefined && v !== null && v !== '' ?
15923 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15928 cv = function(v){ return v === true || v === "true" || v == 1; };
15935 if(v instanceof Date){
15939 if(dateFormat == "timestamp"){
15940 return new Date(v*1000);
15942 return Date.parseDate(v, dateFormat);
15944 var parsed = Date.parse(v);
15945 return parsed ? new Date(parsed) : null;
15954 Roo.data.Field.prototype = {
15962 * Ext JS Library 1.1.1
15963 * Copyright(c) 2006-2007, Ext JS, LLC.
15965 * Originally Released Under LGPL - original licence link has changed is not relivant.
15968 * <script type="text/javascript">
15971 // Base class for reading structured data from a data source. This class is intended to be
15972 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15975 * @class Roo.data.DataReader
15977 * Base class for reading structured data from a data source. This class is intended to be
15978 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15981 Roo.data.DataReader = function(meta, recordType){
15985 this.recordType = recordType instanceof Array ?
15986 Roo.data.Record.create(recordType) : recordType;
15989 Roo.data.DataReader.prototype = {
15992 readerType : 'Data',
15994 * Create an empty record
15995 * @param {Object} data (optional) - overlay some values
15996 * @return {Roo.data.Record} record created.
15998 newRow : function(d) {
16000 this.recordType.prototype.fields.each(function(c) {
16002 case 'int' : da[c.name] = 0; break;
16003 case 'date' : da[c.name] = new Date(); break;
16004 case 'float' : da[c.name] = 0.0; break;
16005 case 'boolean' : da[c.name] = false; break;
16006 default : da[c.name] = ""; break;
16010 return new this.recordType(Roo.apply(da, d));
16016 * Ext JS Library 1.1.1
16017 * Copyright(c) 2006-2007, Ext JS, LLC.
16019 * Originally Released Under LGPL - original licence link has changed is not relivant.
16022 * <script type="text/javascript">
16026 * @class Roo.data.DataProxy
16027 * @extends Roo.util.Observable
16029 * This class is an abstract base class for implementations which provide retrieval of
16030 * unformatted data objects.<br>
16032 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16033 * (of the appropriate type which knows how to parse the data object) to provide a block of
16034 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16036 * Custom implementations must implement the load method as described in
16037 * {@link Roo.data.HttpProxy#load}.
16039 Roo.data.DataProxy = function(){
16042 * @event beforeload
16043 * Fires before a network request is made to retrieve a data object.
16044 * @param {Object} This DataProxy object.
16045 * @param {Object} params The params parameter to the load function.
16050 * Fires before the load method's callback is called.
16051 * @param {Object} This DataProxy object.
16052 * @param {Object} o The data object.
16053 * @param {Object} arg The callback argument object passed to the load function.
16057 * @event loadexception
16058 * Fires if an Exception occurs during data retrieval.
16059 * @param {Object} This DataProxy object.
16060 * @param {Object} o The data object.
16061 * @param {Object} arg The callback argument object passed to the load function.
16062 * @param {Object} e The Exception.
16064 loadexception : true
16066 Roo.data.DataProxy.superclass.constructor.call(this);
16069 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16072 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16076 * Ext JS Library 1.1.1
16077 * Copyright(c) 2006-2007, Ext JS, LLC.
16079 * Originally Released Under LGPL - original licence link has changed is not relivant.
16082 * <script type="text/javascript">
16085 * @class Roo.data.MemoryProxy
16086 * @extends Roo.data.DataProxy
16087 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16088 * to the Reader when its load method is called.
16090 * @param {Object} config A config object containing the objects needed for the Store to access data,
16092 Roo.data.MemoryProxy = function(config){
16094 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16095 data = config.data;
16097 Roo.data.MemoryProxy.superclass.constructor.call(this);
16101 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16104 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16107 * Load data from the requested source (in this case an in-memory
16108 * data object passed to the constructor), read the data object into
16109 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16110 * process that block using the passed callback.
16111 * @param {Object} params This parameter is not used by the MemoryProxy class.
16112 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16113 * object into a block of Roo.data.Records.
16114 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16115 * The function must be passed <ul>
16116 * <li>The Record block object</li>
16117 * <li>The "arg" argument from the load function</li>
16118 * <li>A boolean success indicator</li>
16120 * @param {Object} scope The scope in which to call the callback
16121 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16123 load : function(params, reader, callback, scope, arg){
16124 params = params || {};
16127 result = reader.readRecords(params.data ? params.data :this.data);
16129 this.fireEvent("loadexception", this, arg, null, e);
16130 callback.call(scope, null, arg, false);
16133 callback.call(scope, result, arg, true);
16137 update : function(params, records){
16142 * Ext JS Library 1.1.1
16143 * Copyright(c) 2006-2007, Ext JS, LLC.
16145 * Originally Released Under LGPL - original licence link has changed is not relivant.
16148 * <script type="text/javascript">
16151 * @class Roo.data.HttpProxy
16152 * @extends Roo.data.DataProxy
16153 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16154 * configured to reference a certain URL.<br><br>
16156 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16157 * from which the running page was served.<br><br>
16159 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16161 * Be aware that to enable the browser to parse an XML document, the server must set
16162 * the Content-Type header in the HTTP response to "text/xml".
16164 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16165 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16166 * will be used to make the request.
16168 Roo.data.HttpProxy = function(conn){
16169 Roo.data.HttpProxy.superclass.constructor.call(this);
16170 // is conn a conn config or a real conn?
16172 this.useAjax = !conn || !conn.events;
16176 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16177 // thse are take from connection...
16180 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16183 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16184 * extra parameters to each request made by this object. (defaults to undefined)
16187 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16188 * to each request made by this object. (defaults to undefined)
16191 * @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)
16194 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16197 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16203 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16207 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16208 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16209 * a finer-grained basis than the DataProxy events.
16211 getConnection : function(){
16212 return this.useAjax ? Roo.Ajax : this.conn;
16216 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16217 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16218 * process that block using the passed callback.
16219 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16220 * for the request to the remote server.
16221 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16222 * object into a block of Roo.data.Records.
16223 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16224 * The function must be passed <ul>
16225 * <li>The Record block object</li>
16226 * <li>The "arg" argument from the load function</li>
16227 * <li>A boolean success indicator</li>
16229 * @param {Object} scope The scope in which to call the callback
16230 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16232 load : function(params, reader, callback, scope, arg){
16233 if(this.fireEvent("beforeload", this, params) !== false){
16235 params : params || {},
16237 callback : callback,
16242 callback : this.loadResponse,
16246 Roo.applyIf(o, this.conn);
16247 if(this.activeRequest){
16248 Roo.Ajax.abort(this.activeRequest);
16250 this.activeRequest = Roo.Ajax.request(o);
16252 this.conn.request(o);
16255 callback.call(scope||this, null, arg, false);
16260 loadResponse : function(o, success, response){
16261 delete this.activeRequest;
16263 this.fireEvent("loadexception", this, o, response);
16264 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16269 result = o.reader.read(response);
16272 o.raw = { errorMsg : response.responseText };
16273 this.fireEvent("loadexception", this, o, response, e);
16274 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16278 this.fireEvent("load", this, o, o.request.arg);
16279 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16283 update : function(dataSet){
16288 updateResponse : function(dataSet){
16293 * Ext JS Library 1.1.1
16294 * Copyright(c) 2006-2007, Ext JS, LLC.
16296 * Originally Released Under LGPL - original licence link has changed is not relivant.
16299 * <script type="text/javascript">
16303 * @class Roo.data.ScriptTagProxy
16304 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16305 * other than the originating domain of the running page.<br><br>
16307 * <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
16308 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16310 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16311 * source code that is used as the source inside a <script> tag.<br><br>
16313 * In order for the browser to process the returned data, the server must wrap the data object
16314 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16315 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16316 * depending on whether the callback name was passed:
16319 boolean scriptTag = false;
16320 String cb = request.getParameter("callback");
16323 response.setContentType("text/javascript");
16325 response.setContentType("application/x-json");
16327 Writer out = response.getWriter();
16329 out.write(cb + "(");
16331 out.print(dataBlock.toJsonString());
16338 * @param {Object} config A configuration object.
16340 Roo.data.ScriptTagProxy = function(config){
16341 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16342 Roo.apply(this, config);
16343 this.head = document.getElementsByTagName("head")[0];
16346 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16348 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16350 * @cfg {String} url The URL from which to request the data object.
16353 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16357 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16358 * the server the name of the callback function set up by the load call to process the returned data object.
16359 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16360 * javascript output which calls this named function passing the data object as its only parameter.
16362 callbackParam : "callback",
16364 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16365 * name to the request.
16370 * Load data from the configured URL, read the data object into
16371 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16372 * process that block using the passed callback.
16373 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16374 * for the request to the remote server.
16375 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16376 * object into a block of Roo.data.Records.
16377 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16378 * The function must be passed <ul>
16379 * <li>The Record block object</li>
16380 * <li>The "arg" argument from the load function</li>
16381 * <li>A boolean success indicator</li>
16383 * @param {Object} scope The scope in which to call the callback
16384 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16386 load : function(params, reader, callback, scope, arg){
16387 if(this.fireEvent("beforeload", this, params) !== false){
16389 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16391 var url = this.url;
16392 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16394 url += "&_dc=" + (new Date().getTime());
16396 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16399 cb : "stcCallback"+transId,
16400 scriptId : "stcScript"+transId,
16404 callback : callback,
16410 window[trans.cb] = function(o){
16411 conn.handleResponse(o, trans);
16414 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16416 if(this.autoAbort !== false){
16420 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16422 var script = document.createElement("script");
16423 script.setAttribute("src", url);
16424 script.setAttribute("type", "text/javascript");
16425 script.setAttribute("id", trans.scriptId);
16426 this.head.appendChild(script);
16428 this.trans = trans;
16430 callback.call(scope||this, null, arg, false);
16435 isLoading : function(){
16436 return this.trans ? true : false;
16440 * Abort the current server request.
16442 abort : function(){
16443 if(this.isLoading()){
16444 this.destroyTrans(this.trans);
16449 destroyTrans : function(trans, isLoaded){
16450 this.head.removeChild(document.getElementById(trans.scriptId));
16451 clearTimeout(trans.timeoutId);
16453 window[trans.cb] = undefined;
16455 delete window[trans.cb];
16458 // if hasn't been loaded, wait for load to remove it to prevent script error
16459 window[trans.cb] = function(){
16460 window[trans.cb] = undefined;
16462 delete window[trans.cb];
16469 handleResponse : function(o, trans){
16470 this.trans = false;
16471 this.destroyTrans(trans, true);
16474 result = trans.reader.readRecords(o);
16476 this.fireEvent("loadexception", this, o, trans.arg, e);
16477 trans.callback.call(trans.scope||window, null, trans.arg, false);
16480 this.fireEvent("load", this, o, trans.arg);
16481 trans.callback.call(trans.scope||window, result, trans.arg, true);
16485 handleFailure : function(trans){
16486 this.trans = false;
16487 this.destroyTrans(trans, false);
16488 this.fireEvent("loadexception", this, null, trans.arg);
16489 trans.callback.call(trans.scope||window, null, trans.arg, false);
16493 * Ext JS Library 1.1.1
16494 * Copyright(c) 2006-2007, Ext JS, LLC.
16496 * Originally Released Under LGPL - original licence link has changed is not relivant.
16499 * <script type="text/javascript">
16503 * @class Roo.data.JsonReader
16504 * @extends Roo.data.DataReader
16505 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16506 * based on mappings in a provided Roo.data.Record constructor.
16508 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16509 * in the reply previously.
16514 var RecordDef = Roo.data.Record.create([
16515 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16516 {name: 'occupation'} // This field will use "occupation" as the mapping.
16518 var myReader = new Roo.data.JsonReader({
16519 totalProperty: "results", // The property which contains the total dataset size (optional)
16520 root: "rows", // The property which contains an Array of row objects
16521 id: "id" // The property within each row object that provides an ID for the record (optional)
16525 * This would consume a JSON file like this:
16527 { 'results': 2, 'rows': [
16528 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16529 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16532 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16533 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16534 * paged from the remote server.
16535 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16536 * @cfg {String} root name of the property which contains the Array of row objects.
16537 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16538 * @cfg {Array} fields Array of field definition objects
16540 * Create a new JsonReader
16541 * @param {Object} meta Metadata configuration options
16542 * @param {Object} recordType Either an Array of field definition objects,
16543 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16545 Roo.data.JsonReader = function(meta, recordType){
16548 // set some defaults:
16549 Roo.applyIf(meta, {
16550 totalProperty: 'total',
16551 successProperty : 'success',
16556 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16558 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16560 readerType : 'Json',
16563 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16564 * Used by Store query builder to append _requestMeta to params.
16567 metaFromRemote : false,
16569 * This method is only used by a DataProxy which has retrieved data from a remote server.
16570 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16571 * @return {Object} data A data block which is used by an Roo.data.Store object as
16572 * a cache of Roo.data.Records.
16574 read : function(response){
16575 var json = response.responseText;
16577 var o = /* eval:var:o */ eval("("+json+")");
16579 throw {message: "JsonReader.read: Json object not found"};
16585 this.metaFromRemote = true;
16586 this.meta = o.metaData;
16587 this.recordType = Roo.data.Record.create(o.metaData.fields);
16588 this.onMetaChange(this.meta, this.recordType, o);
16590 return this.readRecords(o);
16593 // private function a store will implement
16594 onMetaChange : function(meta, recordType, o){
16601 simpleAccess: function(obj, subsc) {
16608 getJsonAccessor: function(){
16610 return function(expr) {
16612 return(re.test(expr))
16613 ? new Function("obj", "return obj." + expr)
16618 return Roo.emptyFn;
16623 * Create a data block containing Roo.data.Records from an XML document.
16624 * @param {Object} o An object which contains an Array of row objects in the property specified
16625 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16626 * which contains the total size of the dataset.
16627 * @return {Object} data A data block which is used by an Roo.data.Store object as
16628 * a cache of Roo.data.Records.
16630 readRecords : function(o){
16632 * After any data loads, the raw JSON data is available for further custom processing.
16636 var s = this.meta, Record = this.recordType,
16637 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16639 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16641 if(s.totalProperty) {
16642 this.getTotal = this.getJsonAccessor(s.totalProperty);
16644 if(s.successProperty) {
16645 this.getSuccess = this.getJsonAccessor(s.successProperty);
16647 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16649 var g = this.getJsonAccessor(s.id);
16650 this.getId = function(rec) {
16652 return (r === undefined || r === "") ? null : r;
16655 this.getId = function(){return null;};
16658 for(var jj = 0; jj < fl; jj++){
16660 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16661 this.ef[jj] = this.getJsonAccessor(map);
16665 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16666 if(s.totalProperty){
16667 var vt = parseInt(this.getTotal(o), 10);
16672 if(s.successProperty){
16673 var vs = this.getSuccess(o);
16674 if(vs === false || vs === 'false'){
16679 for(var i = 0; i < c; i++){
16682 var id = this.getId(n);
16683 for(var j = 0; j < fl; j++){
16685 var v = this.ef[j](n);
16687 Roo.log('missing convert for ' + f.name);
16691 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16695 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16701 var record = new Record(values, id);
16703 records[i] = record;
16709 totalRecords : totalRecords
16712 // used when loading children.. @see loadDataFromChildren
16713 toLoadData: function(rec)
16715 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16716 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16717 return { data : data, total : data.length };
16722 * Ext JS Library 1.1.1
16723 * Copyright(c) 2006-2007, Ext JS, LLC.
16725 * Originally Released Under LGPL - original licence link has changed is not relivant.
16728 * <script type="text/javascript">
16732 * @class Roo.data.ArrayReader
16733 * @extends Roo.data.DataReader
16734 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16735 * Each element of that Array represents a row of data fields. The
16736 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16737 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16741 var RecordDef = Roo.data.Record.create([
16742 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16743 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16745 var myReader = new Roo.data.ArrayReader({
16746 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16750 * This would consume an Array like this:
16752 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16756 * Create a new JsonReader
16757 * @param {Object} meta Metadata configuration options.
16758 * @param {Object|Array} recordType Either an Array of field definition objects
16760 * @cfg {Array} fields Array of field definition objects
16761 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16762 * as specified to {@link Roo.data.Record#create},
16763 * or an {@link Roo.data.Record} object
16766 * created using {@link Roo.data.Record#create}.
16768 Roo.data.ArrayReader = function(meta, recordType)
16770 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16773 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16776 * Create a data block containing Roo.data.Records from an XML document.
16777 * @param {Object} o An Array of row objects which represents the dataset.
16778 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16779 * a cache of Roo.data.Records.
16781 readRecords : function(o)
16783 var sid = this.meta ? this.meta.id : null;
16784 var recordType = this.recordType, fields = recordType.prototype.fields;
16787 for(var i = 0; i < root.length; i++){
16790 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16791 for(var j = 0, jlen = fields.length; j < jlen; j++){
16792 var f = fields.items[j];
16793 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16794 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16796 values[f.name] = v;
16798 var record = new recordType(values, id);
16800 records[records.length] = record;
16804 totalRecords : records.length
16807 // used when loading children.. @see loadDataFromChildren
16808 toLoadData: function(rec)
16810 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16811 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16822 * @class Roo.bootstrap.form.ComboBox
16823 * @extends Roo.bootstrap.form.TriggerField
16824 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16825 * @cfg {Boolean} append (true|false) default false
16826 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16827 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16828 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16829 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16830 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16831 * @cfg {Boolean} animate default true
16832 * @cfg {Boolean} emptyResultText only for touch device
16833 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16834 * @cfg {String} emptyTitle default ''
16835 * @cfg {Number} width fixed with? experimental
16837 * Create a new ComboBox.
16838 * @param {Object} config Configuration options
16840 Roo.bootstrap.form.ComboBox = function(config){
16841 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16845 * Fires when the dropdown list is expanded
16846 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16851 * Fires when the dropdown list is collapsed
16852 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16856 * @event beforeselect
16857 * Fires before a list item is selected. Return false to cancel the selection.
16858 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859 * @param {Roo.data.Record} record The data record returned from the underlying store
16860 * @param {Number} index The index of the selected item in the dropdown list
16862 'beforeselect' : true,
16865 * Fires when a list item is selected
16866 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16867 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16868 * @param {Number} index The index of the selected item in the dropdown list
16872 * @event beforequery
16873 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16874 * The event object passed has these properties:
16875 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16876 * @param {String} query The query
16877 * @param {Boolean} forceAll true to force "all" query
16878 * @param {Boolean} cancel true to cancel the query
16879 * @param {Object} e The query event object
16881 'beforequery': true,
16884 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16885 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16890 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16891 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16892 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16897 * Fires when the remove value from the combobox array
16898 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16902 * @event afterremove
16903 * Fires when the remove value from the combobox array
16904 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16906 'afterremove' : true,
16908 * @event specialfilter
16909 * Fires when specialfilter
16910 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16912 'specialfilter' : true,
16915 * Fires when tick the element
16916 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16920 * @event touchviewdisplay
16921 * Fires when touch view require special display (default is using displayField)
16922 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16923 * @param {Object} cfg set html .
16925 'touchviewdisplay' : true
16930 this.tickItems = [];
16932 this.selectedIndex = -1;
16933 if(this.mode == 'local'){
16934 if(config.queryDelay === undefined){
16935 this.queryDelay = 10;
16937 if(config.minChars === undefined){
16943 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16946 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16947 * rendering into an Roo.Editor, defaults to false)
16950 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16951 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16954 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16957 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16958 * the dropdown list (defaults to undefined, with no header element)
16962 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16966 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16968 listWidth: undefined,
16970 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16971 * mode = 'remote' or 'text' if mode = 'local')
16973 displayField: undefined,
16976 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16977 * mode = 'remote' or 'value' if mode = 'local').
16978 * Note: use of a valueField requires the user make a selection
16979 * in order for a value to be mapped.
16981 valueField: undefined,
16983 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16988 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16989 * field's data value (defaults to the underlying DOM element's name)
16991 hiddenName: undefined,
16993 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16997 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16999 selectedClass: 'active',
17002 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17006 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17007 * anchor positions (defaults to 'tl-bl')
17009 listAlign: 'tl-bl?',
17011 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17015 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17016 * query specified by the allQuery config option (defaults to 'query')
17018 triggerAction: 'query',
17020 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17021 * (defaults to 4, does not apply if editable = false)
17025 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17026 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17030 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17031 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17035 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17036 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17040 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17041 * when editable = true (defaults to false)
17043 selectOnFocus:false,
17045 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17047 queryParam: 'query',
17049 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17050 * when mode = 'remote' (defaults to 'Loading...')
17052 loadingText: 'Loading...',
17054 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17058 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17062 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17063 * traditional select (defaults to true)
17067 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17071 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17075 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17076 * listWidth has a higher value)
17080 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17081 * allow the user to set arbitrary text into the field (defaults to false)
17083 forceSelection:false,
17085 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17086 * if typeAhead = true (defaults to 250)
17088 typeAheadDelay : 250,
17090 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17091 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17093 valueNotFoundText : undefined,
17095 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17097 blockFocus : false,
17100 * @cfg {Boolean} disableClear Disable showing of clear button.
17102 disableClear : false,
17104 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17106 alwaysQuery : false,
17109 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17114 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17116 invalidClass : "has-warning",
17119 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17121 validClass : "has-success",
17124 * @cfg {Boolean} specialFilter (true|false) special filter default false
17126 specialFilter : false,
17129 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17131 mobileTouchView : true,
17134 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17136 useNativeIOS : false,
17139 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17141 mobile_restrict_height : false,
17143 ios_options : false,
17155 btnPosition : 'right',
17156 triggerList : true,
17157 showToggleBtn : true,
17159 emptyResultText: 'Empty',
17160 triggerText : 'Select',
17164 // element that contains real text value.. (when hidden is used..)
17166 getAutoCreate : function()
17171 * Render classic select for iso
17174 if(Roo.isIOS && this.useNativeIOS){
17175 cfg = this.getAutoCreateNativeIOS();
17183 if(Roo.isTouch && this.mobileTouchView){
17184 cfg = this.getAutoCreateTouchView();
17191 if(!this.tickable){
17192 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17197 * ComboBox with tickable selections
17200 var align = this.labelAlign || this.parentLabelAlign();
17203 cls : 'form-group roo-combobox-tickable' //input-group
17206 var btn_text_select = '';
17207 var btn_text_done = '';
17208 var btn_text_cancel = '';
17210 if (this.btn_text_show) {
17211 btn_text_select = 'Select';
17212 btn_text_done = 'Done';
17213 btn_text_cancel = 'Cancel';
17218 cls : 'tickable-buttons',
17223 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17224 //html : this.triggerText
17225 html: btn_text_select
17231 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17233 html: btn_text_done
17239 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17241 html: btn_text_cancel
17247 buttons.cn.unshift({
17249 cls: 'roo-select2-search-field-input'
17255 Roo.each(buttons.cn, function(c){
17257 c.cls += ' btn-' + _this.size;
17260 if (_this.disabled) {
17267 style : 'display: contents',
17272 cls: 'form-hidden-field'
17276 cls: 'roo-select2-choices',
17280 cls: 'roo-select2-search-field',
17291 cls: 'roo-select2-container input-group roo-select2-container-multi',
17297 // cls: 'typeahead typeahead-long dropdown-menu',
17298 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17303 if(this.hasFeedback && !this.allowBlank){
17307 cls: 'glyphicon form-control-feedback'
17310 combobox.cn.push(feedback);
17317 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17318 tooltip : 'This field is required'
17320 if (Roo.bootstrap.version == 4) {
17323 style : 'display:none'
17326 if (align ==='left' && this.fieldLabel.length) {
17328 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17335 cls : 'control-label col-form-label',
17336 html : this.fieldLabel
17348 var labelCfg = cfg.cn[1];
17349 var contentCfg = cfg.cn[2];
17352 if(this.indicatorpos == 'right'){
17358 cls : 'control-label col-form-label',
17362 html : this.fieldLabel
17378 labelCfg = cfg.cn[0];
17379 contentCfg = cfg.cn[1];
17383 if(this.labelWidth > 12){
17384 labelCfg.style = "width: " + this.labelWidth + 'px';
17386 if(this.width * 1 > 0){
17387 contentCfg.style = "width: " + this.width + 'px';
17389 if(this.labelWidth < 13 && this.labelmd == 0){
17390 this.labelmd = this.labelWidth;
17393 if(this.labellg > 0){
17394 labelCfg.cls += ' col-lg-' + this.labellg;
17395 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17398 if(this.labelmd > 0){
17399 labelCfg.cls += ' col-md-' + this.labelmd;
17400 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17403 if(this.labelsm > 0){
17404 labelCfg.cls += ' col-sm-' + this.labelsm;
17405 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17408 if(this.labelxs > 0){
17409 labelCfg.cls += ' col-xs-' + this.labelxs;
17410 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17414 } else if ( this.fieldLabel.length) {
17415 // Roo.log(" label");
17420 //cls : 'input-group-addon',
17421 html : this.fieldLabel
17426 if(this.indicatorpos == 'right'){
17430 //cls : 'input-group-addon',
17431 html : this.fieldLabel
17441 // Roo.log(" no label && no align");
17448 ['xs','sm','md','lg'].map(function(size){
17449 if (settings[size]) {
17450 cfg.cls += ' col-' + size + '-' + settings[size];
17458 _initEventsCalled : false,
17461 initEvents: function()
17463 if (this._initEventsCalled) { // as we call render... prevent looping...
17466 this._initEventsCalled = true;
17469 throw "can not find store for combo";
17472 this.indicator = this.indicatorEl();
17474 this.store = Roo.factory(this.store, Roo.data);
17475 this.store.parent = this;
17477 // if we are building from html. then this element is so complex, that we can not really
17478 // use the rendered HTML.
17479 // so we have to trash and replace the previous code.
17480 if (Roo.XComponent.build_from_html) {
17481 // remove this element....
17482 var e = this.el.dom, k=0;
17483 while (e ) { e = e.previousSibling; ++k;}
17488 this.rendered = false;
17490 this.render(this.parent().getChildContainer(true), k);
17493 if(Roo.isIOS && this.useNativeIOS){
17494 this.initIOSView();
17502 if(Roo.isTouch && this.mobileTouchView){
17503 this.initTouchView();
17508 this.initTickableEvents();
17512 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17514 if(this.hiddenName){
17516 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17518 this.hiddenField.dom.value =
17519 this.hiddenValue !== undefined ? this.hiddenValue :
17520 this.value !== undefined ? this.value : '';
17522 // prevent input submission
17523 this.el.dom.removeAttribute('name');
17524 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17529 // this.el.dom.setAttribute('autocomplete', 'off');
17532 var cls = 'x-combo-list';
17534 //this.list = new Roo.Layer({
17535 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17541 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17542 _this.list.setWidth(lw);
17545 this.list.on('mouseover', this.onViewOver, this);
17546 this.list.on('mousemove', this.onViewMove, this);
17547 this.list.on('scroll', this.onViewScroll, this);
17550 this.list.swallowEvent('mousewheel');
17551 this.assetHeight = 0;
17554 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17555 this.assetHeight += this.header.getHeight();
17558 this.innerList = this.list.createChild({cls:cls+'-inner'});
17559 this.innerList.on('mouseover', this.onViewOver, this);
17560 this.innerList.on('mousemove', this.onViewMove, this);
17561 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17563 if(this.allowBlank && !this.pageSize && !this.disableClear){
17564 this.footer = this.list.createChild({cls:cls+'-ft'});
17565 this.pageTb = new Roo.Toolbar(this.footer);
17569 this.footer = this.list.createChild({cls:cls+'-ft'});
17570 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17571 {pageSize: this.pageSize});
17575 if (this.pageTb && this.allowBlank && !this.disableClear) {
17577 this.pageTb.add(new Roo.Toolbar.Fill(), {
17578 cls: 'x-btn-icon x-btn-clear',
17580 handler: function()
17583 _this.clearValue();
17584 _this.onSelect(false, -1);
17589 this.assetHeight += this.footer.getHeight();
17594 this.tpl = Roo.bootstrap.version == 4 ?
17595 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17596 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17599 this.view = new Roo.View(this.list, this.tpl, {
17600 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17602 //this.view.wrapEl.setDisplayed(false);
17603 this.view.on('click', this.onViewClick, this);
17606 this.store.on('beforeload', this.onBeforeLoad, this);
17607 this.store.on('load', this.onLoad, this);
17608 this.store.on('loadexception', this.onLoadException, this);
17610 if(this.resizable){
17611 this.resizer = new Roo.Resizable(this.list, {
17612 pinned:true, handles:'se'
17614 this.resizer.on('resize', function(r, w, h){
17615 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17616 this.listWidth = w;
17617 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17618 this.restrictHeight();
17620 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17623 if(!this.editable){
17624 this.editable = true;
17625 this.setEditable(false);
17630 if (typeof(this.events.add.listeners) != 'undefined') {
17632 this.addicon = this.wrap.createChild(
17633 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17635 this.addicon.on('click', function(e) {
17636 this.fireEvent('add', this);
17639 if (typeof(this.events.edit.listeners) != 'undefined') {
17641 this.editicon = this.wrap.createChild(
17642 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17643 if (this.addicon) {
17644 this.editicon.setStyle('margin-left', '40px');
17646 this.editicon.on('click', function(e) {
17648 // we fire even if inothing is selected..
17649 this.fireEvent('edit', this, this.lastData );
17655 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17656 "up" : function(e){
17657 this.inKeyMode = true;
17661 "down" : function(e){
17662 if(!this.isExpanded()){
17663 this.onTriggerClick();
17665 this.inKeyMode = true;
17670 "enter" : function(e){
17671 // this.onViewClick();
17675 if(this.fireEvent("specialkey", this, e)){
17676 this.onViewClick(false);
17682 "esc" : function(e){
17686 "tab" : function(e){
17689 if(this.fireEvent("specialkey", this, e)){
17690 this.onViewClick(false);
17698 doRelay : function(foo, bar, hname){
17699 if(hname == 'down' || this.scope.isExpanded()){
17700 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17709 this.queryDelay = Math.max(this.queryDelay || 10,
17710 this.mode == 'local' ? 10 : 250);
17713 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17715 if(this.typeAhead){
17716 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17718 if(this.editable !== false){
17719 this.inputEl().on("keyup", this.onKeyUp, this);
17721 if(this.forceSelection){
17722 this.inputEl().on('blur', this.doForce, this);
17726 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17727 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17731 initTickableEvents: function()
17735 if(this.hiddenName){
17737 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17739 this.hiddenField.dom.value =
17740 this.hiddenValue !== undefined ? this.hiddenValue :
17741 this.value !== undefined ? this.value : '';
17743 // prevent input submission
17744 this.el.dom.removeAttribute('name');
17745 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17750 // this.list = this.el.select('ul.dropdown-menu',true).first();
17752 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17753 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17754 if(this.triggerList){
17755 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17758 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17759 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17761 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17762 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17764 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17765 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17767 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17768 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17769 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17772 this.cancelBtn.hide();
17777 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17778 _this.list.setWidth(lw);
17781 this.list.on('mouseover', this.onViewOver, this);
17782 this.list.on('mousemove', this.onViewMove, this);
17784 this.list.on('scroll', this.onViewScroll, this);
17787 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17788 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17791 this.view = new Roo.View(this.list, this.tpl, {
17796 selectedClass: this.selectedClass
17799 //this.view.wrapEl.setDisplayed(false);
17800 this.view.on('click', this.onViewClick, this);
17804 this.store.on('beforeload', this.onBeforeLoad, this);
17805 this.store.on('load', this.onLoad, this);
17806 this.store.on('loadexception', this.onLoadException, this);
17809 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17810 "up" : function(e){
17811 this.inKeyMode = true;
17815 "down" : function(e){
17816 this.inKeyMode = true;
17820 "enter" : function(e){
17821 if(this.fireEvent("specialkey", this, e)){
17822 this.onViewClick(false);
17828 "esc" : function(e){
17829 this.onTickableFooterButtonClick(e, false, false);
17832 "tab" : function(e){
17833 this.fireEvent("specialkey", this, e);
17835 this.onTickableFooterButtonClick(e, false, false);
17842 doRelay : function(e, fn, key){
17843 if(this.scope.isExpanded()){
17844 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17853 this.queryDelay = Math.max(this.queryDelay || 10,
17854 this.mode == 'local' ? 10 : 250);
17857 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17859 if(this.typeAhead){
17860 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17863 if(this.editable !== false){
17864 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17867 this.indicator = this.indicatorEl();
17869 if(this.indicator){
17870 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17871 this.indicator.hide();
17876 onDestroy : function(){
17878 this.view.setStore(null);
17879 this.view.el.removeAllListeners();
17880 this.view.el.remove();
17881 this.view.purgeListeners();
17884 this.list.dom.innerHTML = '';
17888 this.store.un('beforeload', this.onBeforeLoad, this);
17889 this.store.un('load', this.onLoad, this);
17890 this.store.un('loadexception', this.onLoadException, this);
17892 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17896 fireKey : function(e){
17897 if(e.isNavKeyPress() && !this.list.isVisible()){
17898 this.fireEvent("specialkey", this, e);
17903 onResize: function(w, h)
17907 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17909 // if(typeof w != 'number'){
17910 // // we do not handle it!?!?
17913 // var tw = this.trigger.getWidth();
17914 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17915 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17917 // this.inputEl().setWidth( this.adjustWidth('input', x));
17919 // //this.trigger.setStyle('left', x+'px');
17921 // if(this.list && this.listWidth === undefined){
17922 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17923 // this.list.setWidth(lw);
17924 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17932 * Allow or prevent the user from directly editing the field text. If false is passed,
17933 * the user will only be able to select from the items defined in the dropdown list. This method
17934 * is the runtime equivalent of setting the 'editable' config option at config time.
17935 * @param {Boolean} value True to allow the user to directly edit the field text
17937 setEditable : function(value){
17938 if(value == this.editable){
17941 this.editable = value;
17943 this.inputEl().dom.setAttribute('readOnly', true);
17944 this.inputEl().on('mousedown', this.onTriggerClick, this);
17945 this.inputEl().addClass('x-combo-noedit');
17947 this.inputEl().dom.removeAttribute('readOnly');
17948 this.inputEl().un('mousedown', this.onTriggerClick, this);
17949 this.inputEl().removeClass('x-combo-noedit');
17955 onBeforeLoad : function(combo,opts){
17956 if(!this.hasFocus){
17960 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17962 this.restrictHeight();
17963 this.selectedIndex = -1;
17967 onLoad : function(){
17969 this.hasQuery = false;
17971 if(!this.hasFocus){
17975 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17976 this.loading.hide();
17979 if(this.store.getCount() > 0){
17982 this.restrictHeight();
17983 if(this.lastQuery == this.allQuery){
17984 if(this.editable && !this.tickable){
17985 this.inputEl().dom.select();
17989 !this.selectByValue(this.value, true) &&
17992 !this.store.lastOptions ||
17993 typeof(this.store.lastOptions.add) == 'undefined' ||
17994 this.store.lastOptions.add != true
17997 this.select(0, true);
18000 if(this.autoFocus){
18003 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18004 this.taTask.delay(this.typeAheadDelay);
18008 this.onEmptyResults();
18014 onLoadException : function()
18016 this.hasQuery = false;
18018 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18019 this.loading.hide();
18022 if(this.tickable && this.editable){
18027 // only causes errors at present
18028 //Roo.log(this.store.reader.jsonData);
18029 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18031 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18037 onTypeAhead : function(){
18038 if(this.store.getCount() > 0){
18039 var r = this.store.getAt(0);
18040 var newValue = r.data[this.displayField];
18041 var len = newValue.length;
18042 var selStart = this.getRawValue().length;
18044 if(selStart != len){
18045 this.setRawValue(newValue);
18046 this.selectText(selStart, newValue.length);
18052 onSelect : function(record, index){
18054 if(this.fireEvent('beforeselect', this, record, index) !== false){
18056 this.setFromData(index > -1 ? record.data : false);
18059 this.fireEvent('select', this, record, index);
18064 * Returns the currently selected field value or empty string if no value is set.
18065 * @return {String} value The selected value
18067 getValue : function()
18069 if(Roo.isIOS && this.useNativeIOS){
18070 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18074 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18077 if(this.valueField){
18078 return typeof this.value != 'undefined' ? this.value : '';
18080 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18084 getRawValue : function()
18086 if(Roo.isIOS && this.useNativeIOS){
18087 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18090 var v = this.inputEl().getValue();
18096 * Clears any text/value currently set in the field
18098 clearValue : function(){
18100 if(this.hiddenField){
18101 this.hiddenField.dom.value = '';
18104 this.setRawValue('');
18105 this.lastSelectionText = '';
18106 this.lastData = false;
18108 var close = this.closeTriggerEl();
18119 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18120 * will be displayed in the field. If the value does not match the data value of an existing item,
18121 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18122 * Otherwise the field will be blank (although the value will still be set).
18123 * @param {String} value The value to match
18125 setValue : function(v)
18127 if(Roo.isIOS && this.useNativeIOS){
18128 this.setIOSValue(v);
18138 if(this.valueField){
18139 var r = this.findRecord(this.valueField, v);
18141 text = r.data[this.displayField];
18142 }else if(this.valueNotFoundText !== undefined){
18143 text = this.valueNotFoundText;
18146 this.lastSelectionText = text;
18147 if(this.hiddenField){
18148 this.hiddenField.dom.value = v;
18150 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18153 var close = this.closeTriggerEl();
18156 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18162 * @property {Object} the last set data for the element
18167 * Sets the value of the field based on a object which is related to the record format for the store.
18168 * @param {Object} value the value to set as. or false on reset?
18170 setFromData : function(o){
18177 var dv = ''; // display value
18178 var vv = ''; // value value..
18180 if (this.displayField) {
18181 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18183 // this is an error condition!!!
18184 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18187 if(this.valueField){
18188 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18191 var close = this.closeTriggerEl();
18194 if(dv.length || vv * 1 > 0){
18196 this.blockFocus=true;
18202 if(this.hiddenField){
18203 this.hiddenField.dom.value = vv;
18205 this.lastSelectionText = dv;
18206 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18210 // no hidden field.. - we store the value in 'value', but still display
18211 // display field!!!!
18212 this.lastSelectionText = dv;
18213 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18220 reset : function(){
18221 // overridden so that last data is reset..
18228 this.setValue(this.originalValue);
18229 //this.clearInvalid();
18230 this.lastData = false;
18232 this.view.clearSelections();
18238 findRecord : function(prop, value){
18240 if(this.store.getCount() > 0){
18241 this.store.each(function(r){
18242 if(r.data[prop] == value){
18252 getName: function()
18254 // returns hidden if it's set..
18255 if (!this.rendered) {return ''};
18256 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18260 onViewMove : function(e, t){
18261 this.inKeyMode = false;
18265 onViewOver : function(e, t){
18266 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18269 var item = this.view.findItemFromChild(t);
18272 var index = this.view.indexOf(item);
18273 this.select(index, false);
18278 onViewClick : function(view, doFocus, el, e)
18280 var index = this.view.getSelectedIndexes()[0];
18282 var r = this.store.getAt(index);
18286 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18293 Roo.each(this.tickItems, function(v,k){
18295 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18297 _this.tickItems.splice(k, 1);
18299 if(typeof(e) == 'undefined' && view == false){
18300 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18312 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18313 this.tickItems.push(r.data);
18316 if(typeof(e) == 'undefined' && view == false){
18317 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18324 this.onSelect(r, index);
18326 if(doFocus !== false && !this.blockFocus){
18327 this.inputEl().focus();
18332 restrictHeight : function(){
18333 //this.innerList.dom.style.height = '';
18334 //var inner = this.innerList.dom;
18335 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18336 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18337 //this.list.beginUpdate();
18338 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18339 this.list.alignTo(this.inputEl(), this.listAlign);
18340 this.list.alignTo(this.inputEl(), this.listAlign);
18341 //this.list.endUpdate();
18345 onEmptyResults : function(){
18347 if(this.tickable && this.editable){
18348 this.hasFocus = false;
18349 this.restrictHeight();
18357 * Returns true if the dropdown list is expanded, else false.
18359 isExpanded : function(){
18360 return this.list.isVisible();
18364 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18365 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18366 * @param {String} value The data value of the item to select
18367 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18368 * selected item if it is not currently in view (defaults to true)
18369 * @return {Boolean} True if the value matched an item in the list, else false
18371 selectByValue : function(v, scrollIntoView){
18372 if(v !== undefined && v !== null){
18373 var r = this.findRecord(this.valueField || this.displayField, v);
18375 this.select(this.store.indexOf(r), scrollIntoView);
18383 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18384 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18385 * @param {Number} index The zero-based index of the list item to select
18386 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18387 * selected item if it is not currently in view (defaults to true)
18389 select : function(index, scrollIntoView){
18390 this.selectedIndex = index;
18391 this.view.select(index);
18392 if(scrollIntoView !== false){
18393 var el = this.view.getNode(index);
18395 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18398 this.list.scrollChildIntoView(el, false);
18404 selectNext : function(){
18405 var ct = this.store.getCount();
18407 if(this.selectedIndex == -1){
18409 }else if(this.selectedIndex < ct-1){
18410 this.select(this.selectedIndex+1);
18416 selectPrev : function(){
18417 var ct = this.store.getCount();
18419 if(this.selectedIndex == -1){
18421 }else if(this.selectedIndex != 0){
18422 this.select(this.selectedIndex-1);
18428 onKeyUp : function(e){
18429 if(this.editable !== false && !e.isSpecialKey()){
18430 this.lastKey = e.getKey();
18431 this.dqTask.delay(this.queryDelay);
18436 validateBlur : function(){
18437 return !this.list || !this.list.isVisible();
18441 initQuery : function(){
18443 var v = this.getRawValue();
18445 if(this.tickable && this.editable){
18446 v = this.tickableInputEl().getValue();
18453 doForce : function(){
18454 if(this.inputEl().dom.value.length > 0){
18455 this.inputEl().dom.value =
18456 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18462 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18463 * query allowing the query action to be canceled if needed.
18464 * @param {String} query The SQL query to execute
18465 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18466 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18467 * saved in the current store (defaults to false)
18469 doQuery : function(q, forceAll){
18471 if(q === undefined || q === null){
18476 forceAll: forceAll,
18480 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18485 forceAll = qe.forceAll;
18486 if(forceAll === true || (q.length >= this.minChars)){
18488 this.hasQuery = true;
18490 if(this.lastQuery != q || this.alwaysQuery){
18491 this.lastQuery = q;
18492 if(this.mode == 'local'){
18493 this.selectedIndex = -1;
18495 this.store.clearFilter();
18498 if(this.specialFilter){
18499 this.fireEvent('specialfilter', this);
18504 this.store.filter(this.displayField, q);
18507 this.store.fireEvent("datachanged", this.store);
18514 this.store.baseParams[this.queryParam] = q;
18516 var options = {params : this.getParams(q)};
18519 options.add = true;
18520 options.params.start = this.page * this.pageSize;
18523 this.store.load(options);
18526 * this code will make the page width larger, at the beginning, the list not align correctly,
18527 * we should expand the list on onLoad
18528 * so command out it
18533 this.selectedIndex = -1;
18538 this.loadNext = false;
18542 getParams : function(q){
18544 //p[this.queryParam] = q;
18548 p.limit = this.pageSize;
18554 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18556 collapse : function(){
18557 if(!this.isExpanded()){
18563 this.hasFocus = false;
18567 this.cancelBtn.hide();
18568 this.trigger.show();
18571 this.tickableInputEl().dom.value = '';
18572 this.tickableInputEl().blur();
18577 Roo.get(document).un('mousedown', this.collapseIf, this);
18578 Roo.get(document).un('mousewheel', this.collapseIf, this);
18579 if (!this.editable) {
18580 Roo.get(document).un('keydown', this.listKeyPress, this);
18582 this.fireEvent('collapse', this);
18588 collapseIf : function(e){
18589 var in_combo = e.within(this.el);
18590 var in_list = e.within(this.list);
18591 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18593 if (in_combo || in_list || is_list) {
18594 //e.stopPropagation();
18599 this.onTickableFooterButtonClick(e, false, false);
18607 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18609 expand : function(){
18611 if(this.isExpanded() || !this.hasFocus){
18615 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18616 this.list.setWidth(lw);
18622 this.restrictHeight();
18626 this.tickItems = Roo.apply([], this.item);
18629 this.cancelBtn.show();
18630 this.trigger.hide();
18633 this.tickableInputEl().focus();
18638 Roo.get(document).on('mousedown', this.collapseIf, this);
18639 Roo.get(document).on('mousewheel', this.collapseIf, this);
18640 if (!this.editable) {
18641 Roo.get(document).on('keydown', this.listKeyPress, this);
18644 this.fireEvent('expand', this);
18648 // Implements the default empty TriggerField.onTriggerClick function
18649 onTriggerClick : function(e)
18651 Roo.log('trigger click');
18653 if(this.disabled || !this.triggerList){
18658 this.loadNext = false;
18660 if(this.isExpanded()){
18662 if (!this.blockFocus) {
18663 this.inputEl().focus();
18667 this.hasFocus = true;
18668 if(this.triggerAction == 'all') {
18669 this.doQuery(this.allQuery, true);
18671 this.doQuery(this.getRawValue());
18673 if (!this.blockFocus) {
18674 this.inputEl().focus();
18679 onTickableTriggerClick : function(e)
18686 this.loadNext = false;
18687 this.hasFocus = true;
18689 if(this.triggerAction == 'all') {
18690 this.doQuery(this.allQuery, true);
18692 this.doQuery(this.getRawValue());
18696 onSearchFieldClick : function(e)
18698 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18699 this.onTickableFooterButtonClick(e, false, false);
18703 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18708 this.loadNext = false;
18709 this.hasFocus = true;
18711 if(this.triggerAction == 'all') {
18712 this.doQuery(this.allQuery, true);
18714 this.doQuery(this.getRawValue());
18718 listKeyPress : function(e)
18720 //Roo.log('listkeypress');
18721 // scroll to first matching element based on key pres..
18722 if (e.isSpecialKey()) {
18725 var k = String.fromCharCode(e.getKey()).toUpperCase();
18728 var csel = this.view.getSelectedNodes();
18729 var cselitem = false;
18731 var ix = this.view.indexOf(csel[0]);
18732 cselitem = this.store.getAt(ix);
18733 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18739 this.store.each(function(v) {
18741 // start at existing selection.
18742 if (cselitem.id == v.id) {
18748 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18749 match = this.store.indexOf(v);
18755 if (match === false) {
18756 return true; // no more action?
18759 this.view.select(match);
18760 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18761 sn.scrollIntoView(sn.dom.parentNode, false);
18764 onViewScroll : function(e, t){
18766 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){
18770 this.hasQuery = true;
18772 this.loading = this.list.select('.loading', true).first();
18774 if(this.loading === null){
18775 this.list.createChild({
18777 cls: 'loading roo-select2-more-results roo-select2-active',
18778 html: 'Loading more results...'
18781 this.loading = this.list.select('.loading', true).first();
18783 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18785 this.loading.hide();
18788 this.loading.show();
18793 this.loadNext = true;
18795 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18800 addItem : function(o)
18802 var dv = ''; // display value
18804 if (this.displayField) {
18805 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18807 // this is an error condition!!!
18808 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18815 var choice = this.choices.createChild({
18817 cls: 'roo-select2-search-choice',
18826 cls: 'roo-select2-search-choice-close fa fa-times',
18831 }, this.searchField);
18833 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18835 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18843 this.inputEl().dom.value = '';
18848 onRemoveItem : function(e, _self, o)
18850 e.preventDefault();
18852 this.lastItem = Roo.apply([], this.item);
18854 var index = this.item.indexOf(o.data) * 1;
18857 Roo.log('not this item?!');
18861 this.item.splice(index, 1);
18866 this.fireEvent('remove', this, e);
18872 syncValue : function()
18874 if(!this.item.length){
18881 Roo.each(this.item, function(i){
18882 if(_this.valueField){
18883 value.push(i[_this.valueField]);
18890 this.value = value.join(',');
18892 if(this.hiddenField){
18893 this.hiddenField.dom.value = this.value;
18896 this.store.fireEvent("datachanged", this.store);
18901 clearItem : function()
18903 if(!this.multiple){
18909 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18917 if(this.tickable && !Roo.isTouch){
18918 this.view.refresh();
18922 inputEl: function ()
18924 if(Roo.isIOS && this.useNativeIOS){
18925 return this.el.select('select.roo-ios-select', true).first();
18928 if(Roo.isTouch && this.mobileTouchView){
18929 return this.el.select('input.form-control',true).first();
18933 return this.searchField;
18936 return this.el.select('input.form-control',true).first();
18939 onTickableFooterButtonClick : function(e, btn, el)
18941 e.preventDefault();
18943 this.lastItem = Roo.apply([], this.item);
18945 if(btn && btn.name == 'cancel'){
18946 this.tickItems = Roo.apply([], this.item);
18955 Roo.each(this.tickItems, function(o){
18963 validate : function()
18965 if(this.getVisibilityEl().hasClass('hidden')){
18969 var v = this.getRawValue();
18972 v = this.getValue();
18975 if(this.disabled || this.allowBlank || v.length){
18980 this.markInvalid();
18984 tickableInputEl : function()
18986 if(!this.tickable || !this.editable){
18987 return this.inputEl();
18990 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18994 getAutoCreateTouchView : function()
18999 cls: 'form-group' //input-group
19005 type : this.inputType,
19006 cls : 'form-control x-combo-noedit',
19007 autocomplete: 'new-password',
19008 placeholder : this.placeholder || '',
19013 input.name = this.name;
19017 input.cls += ' input-' + this.size;
19020 if (this.disabled) {
19021 input.disabled = true;
19025 cls : 'roo-combobox-wrap',
19032 inputblock.cls += ' input-group';
19034 inputblock.cn.unshift({
19036 cls : 'input-group-addon input-group-prepend input-group-text',
19041 if(this.removable && !this.multiple){
19042 inputblock.cls += ' roo-removable';
19044 inputblock.cn.push({
19047 cls : 'roo-combo-removable-btn close'
19051 if(this.hasFeedback && !this.allowBlank){
19053 inputblock.cls += ' has-feedback';
19055 inputblock.cn.push({
19057 cls: 'glyphicon form-control-feedback'
19064 inputblock.cls += (this.before) ? '' : ' input-group';
19066 inputblock.cn.push({
19068 cls : 'input-group-addon input-group-append input-group-text',
19074 var ibwrap = inputblock;
19079 cls: 'roo-select2-choices',
19083 cls: 'roo-select2-search-field',
19096 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19101 cls: 'form-hidden-field'
19107 if(!this.multiple && this.showToggleBtn){
19113 if (this.caret != false) {
19116 cls: 'fa fa-' + this.caret
19123 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19125 Roo.bootstrap.version == 3 ? caret : '',
19128 cls: 'combobox-clear',
19142 combobox.cls += ' roo-select2-container-multi';
19145 var required = this.allowBlank ? {
19147 style: 'display: none'
19150 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19151 tooltip : 'This field is required'
19154 var align = this.labelAlign || this.parentLabelAlign();
19156 if (align ==='left' && this.fieldLabel.length) {
19162 cls : 'control-label col-form-label',
19163 html : this.fieldLabel
19167 cls : 'roo-combobox-wrap ',
19174 var labelCfg = cfg.cn[1];
19175 var contentCfg = cfg.cn[2];
19178 if(this.indicatorpos == 'right'){
19183 cls : 'control-label col-form-label',
19187 html : this.fieldLabel
19193 cls : "roo-combobox-wrap ",
19201 labelCfg = cfg.cn[0];
19202 contentCfg = cfg.cn[1];
19207 if(this.labelWidth > 12){
19208 labelCfg.style = "width: " + this.labelWidth + 'px';
19211 if(this.labelWidth < 13 && this.labelmd == 0){
19212 this.labelmd = this.labelWidth;
19215 if(this.labellg > 0){
19216 labelCfg.cls += ' col-lg-' + this.labellg;
19217 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19220 if(this.labelmd > 0){
19221 labelCfg.cls += ' col-md-' + this.labelmd;
19222 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19225 if(this.labelsm > 0){
19226 labelCfg.cls += ' col-sm-' + this.labelsm;
19227 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19230 if(this.labelxs > 0){
19231 labelCfg.cls += ' col-xs-' + this.labelxs;
19232 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19236 } else if ( this.fieldLabel.length) {
19241 cls : 'control-label',
19242 html : this.fieldLabel
19253 if(this.indicatorpos == 'right'){
19257 cls : 'control-label',
19258 html : this.fieldLabel,
19276 var settings = this;
19278 ['xs','sm','md','lg'].map(function(size){
19279 if (settings[size]) {
19280 cfg.cls += ' col-' + size + '-' + settings[size];
19287 initTouchView : function()
19289 this.renderTouchView();
19291 this.touchViewEl.on('scroll', function(){
19292 this.el.dom.scrollTop = 0;
19295 this.originalValue = this.getValue();
19297 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19299 this.inputEl().on("click", this.showTouchView, this);
19300 if (this.triggerEl) {
19301 this.triggerEl.on("click", this.showTouchView, this);
19305 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19306 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19308 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19310 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19311 this.store.on('load', this.onTouchViewLoad, this);
19312 this.store.on('loadexception', this.onTouchViewLoadException, this);
19314 if(this.hiddenName){
19316 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19318 this.hiddenField.dom.value =
19319 this.hiddenValue !== undefined ? this.hiddenValue :
19320 this.value !== undefined ? this.value : '';
19322 this.el.dom.removeAttribute('name');
19323 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19327 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19328 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19331 if(this.removable && !this.multiple){
19332 var close = this.closeTriggerEl();
19334 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19335 close.on('click', this.removeBtnClick, this, close);
19339 * fix the bug in Safari iOS8
19341 this.inputEl().on("focus", function(e){
19342 document.activeElement.blur();
19345 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19352 renderTouchView : function()
19354 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19355 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19357 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19358 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19360 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19361 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19362 this.touchViewBodyEl.setStyle('overflow', 'auto');
19364 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19365 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19367 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19368 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19372 showTouchView : function()
19378 this.touchViewHeaderEl.hide();
19380 if(this.modalTitle.length){
19381 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19382 this.touchViewHeaderEl.show();
19385 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19386 this.touchViewEl.show();
19388 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19390 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19391 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19393 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19395 if(this.modalTitle.length){
19396 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19399 this.touchViewBodyEl.setHeight(bodyHeight);
19403 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19405 this.touchViewEl.addClass(['in','show']);
19408 if(this._touchViewMask){
19409 Roo.get(document.body).addClass("x-body-masked");
19410 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19411 this._touchViewMask.setStyle('z-index', 10000);
19412 this._touchViewMask.addClass('show');
19415 this.doTouchViewQuery();
19419 hideTouchView : function()
19421 this.touchViewEl.removeClass(['in','show']);
19425 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19427 this.touchViewEl.setStyle('display', 'none');
19430 if(this._touchViewMask){
19431 this._touchViewMask.removeClass('show');
19432 Roo.get(document.body).removeClass("x-body-masked");
19436 setTouchViewValue : function()
19443 Roo.each(this.tickItems, function(o){
19448 this.hideTouchView();
19451 doTouchViewQuery : function()
19460 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19464 if(!this.alwaysQuery || this.mode == 'local'){
19465 this.onTouchViewLoad();
19472 onTouchViewBeforeLoad : function(combo,opts)
19478 onTouchViewLoad : function()
19480 if(this.store.getCount() < 1){
19481 this.onTouchViewEmptyResults();
19485 this.clearTouchView();
19487 var rawValue = this.getRawValue();
19489 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19491 this.tickItems = [];
19493 this.store.data.each(function(d, rowIndex){
19494 var row = this.touchViewListGroup.createChild(template);
19496 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19497 row.addClass(d.data.cls);
19500 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19503 html : d.data[this.displayField]
19506 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19507 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19510 row.removeClass('selected');
19511 if(!this.multiple && this.valueField &&
19512 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19515 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19516 row.addClass('selected');
19519 if(this.multiple && this.valueField &&
19520 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19524 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19525 this.tickItems.push(d.data);
19528 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19532 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19534 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19536 if(this.modalTitle.length){
19537 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19540 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19542 if(this.mobile_restrict_height && listHeight < bodyHeight){
19543 this.touchViewBodyEl.setHeight(listHeight);
19548 if(firstChecked && listHeight > bodyHeight){
19549 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19554 onTouchViewLoadException : function()
19556 this.hideTouchView();
19559 onTouchViewEmptyResults : function()
19561 this.clearTouchView();
19563 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19565 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19569 clearTouchView : function()
19571 this.touchViewListGroup.dom.innerHTML = '';
19574 onTouchViewClick : function(e, el, o)
19576 e.preventDefault();
19579 var rowIndex = o.rowIndex;
19581 var r = this.store.getAt(rowIndex);
19583 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19585 if(!this.multiple){
19586 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19587 c.dom.removeAttribute('checked');
19590 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19592 this.setFromData(r.data);
19594 var close = this.closeTriggerEl();
19600 this.hideTouchView();
19602 this.fireEvent('select', this, r, rowIndex);
19607 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19608 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19609 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19613 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19614 this.addItem(r.data);
19615 this.tickItems.push(r.data);
19619 getAutoCreateNativeIOS : function()
19622 cls: 'form-group' //input-group,
19627 cls : 'roo-ios-select'
19631 combobox.name = this.name;
19634 if (this.disabled) {
19635 combobox.disabled = true;
19638 var settings = this;
19640 ['xs','sm','md','lg'].map(function(size){
19641 if (settings[size]) {
19642 cfg.cls += ' col-' + size + '-' + settings[size];
19652 initIOSView : function()
19654 this.store.on('load', this.onIOSViewLoad, this);
19659 onIOSViewLoad : function()
19661 if(this.store.getCount() < 1){
19665 this.clearIOSView();
19667 if(this.allowBlank) {
19669 var default_text = '-- SELECT --';
19671 if(this.placeholder.length){
19672 default_text = this.placeholder;
19675 if(this.emptyTitle.length){
19676 default_text += ' - ' + this.emptyTitle + ' -';
19679 var opt = this.inputEl().createChild({
19682 html : default_text
19686 o[this.valueField] = 0;
19687 o[this.displayField] = default_text;
19689 this.ios_options.push({
19696 this.store.data.each(function(d, rowIndex){
19700 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19701 html = d.data[this.displayField];
19706 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19707 value = d.data[this.valueField];
19716 if(this.value == d.data[this.valueField]){
19717 option['selected'] = true;
19720 var opt = this.inputEl().createChild(option);
19722 this.ios_options.push({
19729 this.inputEl().on('change', function(){
19730 this.fireEvent('select', this);
19735 clearIOSView: function()
19737 this.inputEl().dom.innerHTML = '';
19739 this.ios_options = [];
19742 setIOSValue: function(v)
19746 if(!this.ios_options){
19750 Roo.each(this.ios_options, function(opts){
19752 opts.el.dom.removeAttribute('selected');
19754 if(opts.data[this.valueField] != v){
19758 opts.el.dom.setAttribute('selected', true);
19764 * @cfg {Boolean} grow
19768 * @cfg {Number} growMin
19772 * @cfg {Number} growMax
19781 Roo.apply(Roo.bootstrap.form.ComboBox, {
19785 cls: 'modal-header',
19807 cls: 'list-group-item',
19811 cls: 'roo-combobox-list-group-item-value'
19815 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19829 listItemCheckbox : {
19831 cls: 'list-group-item',
19835 cls: 'roo-combobox-list-group-item-value'
19839 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19855 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19860 cls: 'modal-footer',
19868 cls: 'col-xs-6 text-left',
19871 cls: 'btn btn-danger roo-touch-view-cancel',
19877 cls: 'col-xs-6 text-right',
19880 cls: 'btn btn-success roo-touch-view-ok',
19891 Roo.apply(Roo.bootstrap.form.ComboBox, {
19893 touchViewTemplate : {
19895 cls: 'modal fade roo-combobox-touch-view',
19899 cls: 'modal-dialog',
19900 style : 'position:fixed', // we have to fix position....
19904 cls: 'modal-content',
19906 Roo.bootstrap.form.ComboBox.header,
19907 Roo.bootstrap.form.ComboBox.body,
19908 Roo.bootstrap.form.ComboBox.footer
19917 * Ext JS Library 1.1.1
19918 * Copyright(c) 2006-2007, Ext JS, LLC.
19920 * Originally Released Under LGPL - original licence link has changed is not relivant.
19923 * <script type="text/javascript">
19928 * @extends Roo.util.Observable
19929 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19930 * This class also supports single and multi selection modes. <br>
19931 * Create a data model bound view:
19933 var store = new Roo.data.Store(...);
19935 var view = new Roo.View({
19937 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19939 singleSelect: true,
19940 selectedClass: "ydataview-selected",
19944 // listen for node click?
19945 view.on("click", function(vw, index, node, e){
19946 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19950 dataModel.load("foobar.xml");
19952 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19954 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19955 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19957 * Note: old style constructor is still suported (container, template, config)
19960 * Create a new View
19961 * @param {Object} config The config object
19964 Roo.View = function(config, depreciated_tpl, depreciated_config){
19966 this.parent = false;
19968 if (typeof(depreciated_tpl) == 'undefined') {
19969 // new way.. - universal constructor.
19970 Roo.apply(this, config);
19971 this.el = Roo.get(this.el);
19974 this.el = Roo.get(config);
19975 this.tpl = depreciated_tpl;
19976 Roo.apply(this, depreciated_config);
19978 this.wrapEl = this.el.wrap().wrap();
19979 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19982 if(typeof(this.tpl) == "string"){
19983 this.tpl = new Roo.Template(this.tpl);
19985 // support xtype ctors..
19986 this.tpl = new Roo.factory(this.tpl, Roo);
19990 this.tpl.compile();
19995 * @event beforeclick
19996 * Fires before a click is processed. Returns false to cancel the default action.
19997 * @param {Roo.View} this
19998 * @param {Number} index The index of the target node
19999 * @param {HTMLElement} node The target node
20000 * @param {Roo.EventObject} e The raw event object
20002 "beforeclick" : true,
20005 * Fires when a template node is clicked.
20006 * @param {Roo.View} this
20007 * @param {Number} index The index of the target node
20008 * @param {HTMLElement} node The target node
20009 * @param {Roo.EventObject} e The raw event object
20014 * Fires when a template node is double clicked.
20015 * @param {Roo.View} this
20016 * @param {Number} index The index of the target node
20017 * @param {HTMLElement} node The target node
20018 * @param {Roo.EventObject} e The raw event object
20022 * @event contextmenu
20023 * Fires when a template node is right clicked.
20024 * @param {Roo.View} this
20025 * @param {Number} index The index of the target node
20026 * @param {HTMLElement} node The target node
20027 * @param {Roo.EventObject} e The raw event object
20029 "contextmenu" : true,
20031 * @event selectionchange
20032 * Fires when the selected nodes change.
20033 * @param {Roo.View} this
20034 * @param {Array} selections Array of the selected nodes
20036 "selectionchange" : true,
20039 * @event beforeselect
20040 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20041 * @param {Roo.View} this
20042 * @param {HTMLElement} node The node to be selected
20043 * @param {Array} selections Array of currently selected nodes
20045 "beforeselect" : true,
20047 * @event preparedata
20048 * Fires on every row to render, to allow you to change the data.
20049 * @param {Roo.View} this
20050 * @param {Object} data to be rendered (change this)
20052 "preparedata" : true
20060 "click": this.onClick,
20061 "dblclick": this.onDblClick,
20062 "contextmenu": this.onContextMenu,
20066 this.selections = [];
20068 this.cmp = new Roo.CompositeElementLite([]);
20070 this.store = Roo.factory(this.store, Roo.data);
20071 this.setStore(this.store, true);
20074 if ( this.footer && this.footer.xtype) {
20076 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20078 this.footer.dataSource = this.store;
20079 this.footer.container = fctr;
20080 this.footer = Roo.factory(this.footer, Roo);
20081 fctr.insertFirst(this.el);
20083 // this is a bit insane - as the paging toolbar seems to detach the el..
20084 // dom.parentNode.parentNode.parentNode
20085 // they get detached?
20089 Roo.View.superclass.constructor.call(this);
20094 Roo.extend(Roo.View, Roo.util.Observable, {
20097 * @cfg {Roo.data.Store} store Data store to load data from.
20102 * @cfg {String|Roo.Element} el The container element.
20107 * @cfg {String|Roo.Template} tpl The template used by this View
20111 * @cfg {String} dataName the named area of the template to use as the data area
20112 * Works with domtemplates roo-name="name"
20116 * @cfg {String} selectedClass The css class to add to selected nodes
20118 selectedClass : "x-view-selected",
20120 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20125 * @cfg {String} text to display on mask (default Loading)
20129 * @cfg {Boolean} multiSelect Allow multiple selection
20131 multiSelect : false,
20133 * @cfg {Boolean} singleSelect Allow single selection
20135 singleSelect: false,
20138 * @cfg {Boolean} toggleSelect - selecting
20140 toggleSelect : false,
20143 * @cfg {Boolean} tickable - selecting
20148 * Returns the element this view is bound to.
20149 * @return {Roo.Element}
20151 getEl : function(){
20152 return this.wrapEl;
20158 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20160 refresh : function(){
20161 //Roo.log('refresh');
20164 // if we are using something like 'domtemplate', then
20165 // the what gets used is:
20166 // t.applySubtemplate(NAME, data, wrapping data..)
20167 // the outer template then get' applied with
20168 // the store 'extra data'
20169 // and the body get's added to the
20170 // roo-name="data" node?
20171 // <span class='roo-tpl-{name}'></span> ?????
20175 this.clearSelections();
20176 this.el.update("");
20178 var records = this.store.getRange();
20179 if(records.length < 1) {
20181 // is this valid?? = should it render a template??
20183 this.el.update(this.emptyText);
20187 if (this.dataName) {
20188 this.el.update(t.apply(this.store.meta)); //????
20189 el = this.el.child('.roo-tpl-' + this.dataName);
20192 for(var i = 0, len = records.length; i < len; i++){
20193 var data = this.prepareData(records[i].data, i, records[i]);
20194 this.fireEvent("preparedata", this, data, i, records[i]);
20196 var d = Roo.apply({}, data);
20199 Roo.apply(d, {'roo-id' : Roo.id()});
20203 Roo.each(this.parent.item, function(item){
20204 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20207 Roo.apply(d, {'roo-data-checked' : 'checked'});
20211 html[html.length] = Roo.util.Format.trim(
20213 t.applySubtemplate(this.dataName, d, this.store.meta) :
20220 el.update(html.join(""));
20221 this.nodes = el.dom.childNodes;
20222 this.updateIndexes(0);
20227 * Function to override to reformat the data that is sent to
20228 * the template for each node.
20229 * DEPRICATED - use the preparedata event handler.
20230 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20231 * a JSON object for an UpdateManager bound view).
20233 prepareData : function(data, index, record)
20235 this.fireEvent("preparedata", this, data, index, record);
20239 onUpdate : function(ds, record){
20240 // Roo.log('on update');
20241 this.clearSelections();
20242 var index = this.store.indexOf(record);
20243 var n = this.nodes[index];
20244 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20245 n.parentNode.removeChild(n);
20246 this.updateIndexes(index, index);
20252 onAdd : function(ds, records, index)
20254 //Roo.log(['on Add', ds, records, index] );
20255 this.clearSelections();
20256 if(this.nodes.length == 0){
20260 var n = this.nodes[index];
20261 for(var i = 0, len = records.length; i < len; i++){
20262 var d = this.prepareData(records[i].data, i, records[i]);
20264 this.tpl.insertBefore(n, d);
20267 this.tpl.append(this.el, d);
20270 this.updateIndexes(index);
20273 onRemove : function(ds, record, index){
20274 // Roo.log('onRemove');
20275 this.clearSelections();
20276 var el = this.dataName ?
20277 this.el.child('.roo-tpl-' + this.dataName) :
20280 el.dom.removeChild(this.nodes[index]);
20281 this.updateIndexes(index);
20285 * Refresh an individual node.
20286 * @param {Number} index
20288 refreshNode : function(index){
20289 this.onUpdate(this.store, this.store.getAt(index));
20292 updateIndexes : function(startIndex, endIndex){
20293 var ns = this.nodes;
20294 startIndex = startIndex || 0;
20295 endIndex = endIndex || ns.length - 1;
20296 for(var i = startIndex; i <= endIndex; i++){
20297 ns[i].nodeIndex = i;
20302 * Changes the data store this view uses and refresh the view.
20303 * @param {Store} store
20305 setStore : function(store, initial){
20306 if(!initial && this.store){
20307 this.store.un("datachanged", this.refresh);
20308 this.store.un("add", this.onAdd);
20309 this.store.un("remove", this.onRemove);
20310 this.store.un("update", this.onUpdate);
20311 this.store.un("clear", this.refresh);
20312 this.store.un("beforeload", this.onBeforeLoad);
20313 this.store.un("load", this.onLoad);
20314 this.store.un("loadexception", this.onLoad);
20318 store.on("datachanged", this.refresh, this);
20319 store.on("add", this.onAdd, this);
20320 store.on("remove", this.onRemove, this);
20321 store.on("update", this.onUpdate, this);
20322 store.on("clear", this.refresh, this);
20323 store.on("beforeload", this.onBeforeLoad, this);
20324 store.on("load", this.onLoad, this);
20325 store.on("loadexception", this.onLoad, this);
20333 * onbeforeLoad - masks the loading area.
20336 onBeforeLoad : function(store,opts)
20338 //Roo.log('onBeforeLoad');
20340 this.el.update("");
20342 this.el.mask(this.mask ? this.mask : "Loading" );
20344 onLoad : function ()
20351 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20352 * @param {HTMLElement} node
20353 * @return {HTMLElement} The template node
20355 findItemFromChild : function(node){
20356 var el = this.dataName ?
20357 this.el.child('.roo-tpl-' + this.dataName,true) :
20360 if(!node || node.parentNode == el){
20363 var p = node.parentNode;
20364 while(p && p != el){
20365 if(p.parentNode == el){
20374 onClick : function(e){
20375 var item = this.findItemFromChild(e.getTarget());
20377 var index = this.indexOf(item);
20378 if(this.onItemClick(item, index, e) !== false){
20379 this.fireEvent("click", this, index, item, e);
20382 this.clearSelections();
20387 onContextMenu : function(e){
20388 var item = this.findItemFromChild(e.getTarget());
20390 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20395 onDblClick : function(e){
20396 var item = this.findItemFromChild(e.getTarget());
20398 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20402 onItemClick : function(item, index, e)
20404 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20407 if (this.toggleSelect) {
20408 var m = this.isSelected(item) ? 'unselect' : 'select';
20411 _t[m](item, true, false);
20414 if(this.multiSelect || this.singleSelect){
20415 if(this.multiSelect && e.shiftKey && this.lastSelection){
20416 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20418 this.select(item, this.multiSelect && e.ctrlKey);
20419 this.lastSelection = item;
20422 if(!this.tickable){
20423 e.preventDefault();
20431 * Get the number of selected nodes.
20434 getSelectionCount : function(){
20435 return this.selections.length;
20439 * Get the currently selected nodes.
20440 * @return {Array} An array of HTMLElements
20442 getSelectedNodes : function(){
20443 return this.selections;
20447 * Get the indexes of the selected nodes.
20450 getSelectedIndexes : function(){
20451 var indexes = [], s = this.selections;
20452 for(var i = 0, len = s.length; i < len; i++){
20453 indexes.push(s[i].nodeIndex);
20459 * Clear all selections
20460 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20462 clearSelections : function(suppressEvent){
20463 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20464 this.cmp.elements = this.selections;
20465 this.cmp.removeClass(this.selectedClass);
20466 this.selections = [];
20467 if(!suppressEvent){
20468 this.fireEvent("selectionchange", this, this.selections);
20474 * Returns true if the passed node is selected
20475 * @param {HTMLElement/Number} node The node or node index
20476 * @return {Boolean}
20478 isSelected : function(node){
20479 var s = this.selections;
20483 node = this.getNode(node);
20484 return s.indexOf(node) !== -1;
20489 * @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
20490 * @param {Boolean} keepExisting (optional) true to keep existing selections
20491 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20493 select : function(nodeInfo, keepExisting, suppressEvent){
20494 if(nodeInfo instanceof Array){
20496 this.clearSelections(true);
20498 for(var i = 0, len = nodeInfo.length; i < len; i++){
20499 this.select(nodeInfo[i], true, true);
20503 var node = this.getNode(nodeInfo);
20504 if(!node || this.isSelected(node)){
20505 return; // already selected.
20508 this.clearSelections(true);
20511 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20512 Roo.fly(node).addClass(this.selectedClass);
20513 this.selections.push(node);
20514 if(!suppressEvent){
20515 this.fireEvent("selectionchange", this, this.selections);
20523 * @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
20524 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20525 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20527 unselect : function(nodeInfo, keepExisting, suppressEvent)
20529 if(nodeInfo instanceof Array){
20530 Roo.each(this.selections, function(s) {
20531 this.unselect(s, nodeInfo);
20535 var node = this.getNode(nodeInfo);
20536 if(!node || !this.isSelected(node)){
20537 //Roo.log("not selected");
20538 return; // not selected.
20542 Roo.each(this.selections, function(s) {
20544 Roo.fly(node).removeClass(this.selectedClass);
20551 this.selections= ns;
20552 this.fireEvent("selectionchange", this, this.selections);
20556 * Gets a template node.
20557 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20558 * @return {HTMLElement} The node or null if it wasn't found
20560 getNode : function(nodeInfo){
20561 if(typeof nodeInfo == "string"){
20562 return document.getElementById(nodeInfo);
20563 }else if(typeof nodeInfo == "number"){
20564 return this.nodes[nodeInfo];
20570 * Gets a range template nodes.
20571 * @param {Number} startIndex
20572 * @param {Number} endIndex
20573 * @return {Array} An array of nodes
20575 getNodes : function(start, end){
20576 var ns = this.nodes;
20577 start = start || 0;
20578 end = typeof end == "undefined" ? ns.length - 1 : end;
20581 for(var i = start; i <= end; i++){
20585 for(var i = start; i >= end; i--){
20593 * Finds the index of the passed node
20594 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20595 * @return {Number} The index of the node or -1
20597 indexOf : function(node){
20598 node = this.getNode(node);
20599 if(typeof node.nodeIndex == "number"){
20600 return node.nodeIndex;
20602 var ns = this.nodes;
20603 for(var i = 0, len = ns.length; i < len; i++){
20614 * based on jquery fullcalendar
20618 Roo.bootstrap = Roo.bootstrap || {};
20620 * @class Roo.bootstrap.Calendar
20621 * @extends Roo.bootstrap.Component
20622 * Bootstrap Calendar class
20623 * @cfg {Boolean} loadMask (true|false) default false
20624 * @cfg {Object} header generate the user specific header of the calendar, default false
20627 * Create a new Container
20628 * @param {Object} config The config object
20633 Roo.bootstrap.Calendar = function(config){
20634 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20638 * Fires when a date is selected
20639 * @param {DatePicker} this
20640 * @param {Date} date The selected date
20644 * @event monthchange
20645 * Fires when the displayed month changes
20646 * @param {DatePicker} this
20647 * @param {Date} date The selected month
20649 'monthchange': true,
20651 * @event evententer
20652 * Fires when mouse over an event
20653 * @param {Calendar} this
20654 * @param {event} Event
20656 'evententer': true,
20658 * @event eventleave
20659 * Fires when the mouse leaves an
20660 * @param {Calendar} this
20663 'eventleave': true,
20665 * @event eventclick
20666 * Fires when the mouse click an
20667 * @param {Calendar} this
20676 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20679 * @cfg {Roo.data.Store} store
20680 * The data source for the calendar
20684 * @cfg {Number} startDay
20685 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20693 getAutoCreate : function(){
20696 var fc_button = function(name, corner, style, content ) {
20697 return Roo.apply({},{
20699 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20701 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20704 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20715 style : 'width:100%',
20722 cls : 'fc-header-left',
20724 fc_button('prev', 'left', 'arrow', '‹' ),
20725 fc_button('next', 'right', 'arrow', '›' ),
20726 { tag: 'span', cls: 'fc-header-space' },
20727 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20735 cls : 'fc-header-center',
20739 cls: 'fc-header-title',
20742 html : 'month / year'
20750 cls : 'fc-header-right',
20752 /* fc_button('month', 'left', '', 'month' ),
20753 fc_button('week', '', '', 'week' ),
20754 fc_button('day', 'right', '', 'day' )
20766 header = this.header;
20769 var cal_heads = function() {
20771 // fixme - handle this.
20773 for (var i =0; i < Date.dayNames.length; i++) {
20774 var d = Date.dayNames[i];
20777 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20778 html : d.substring(0,3)
20782 ret[0].cls += ' fc-first';
20783 ret[6].cls += ' fc-last';
20786 var cal_cell = function(n) {
20789 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20794 cls: 'fc-day-number',
20798 cls: 'fc-day-content',
20802 style: 'position: relative;' // height: 17px;
20814 var cal_rows = function() {
20817 for (var r = 0; r < 6; r++) {
20824 for (var i =0; i < Date.dayNames.length; i++) {
20825 var d = Date.dayNames[i];
20826 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20829 row.cn[0].cls+=' fc-first';
20830 row.cn[0].cn[0].style = 'min-height:90px';
20831 row.cn[6].cls+=' fc-last';
20835 ret[0].cls += ' fc-first';
20836 ret[4].cls += ' fc-prev-last';
20837 ret[5].cls += ' fc-last';
20844 cls: 'fc-border-separate',
20845 style : 'width:100%',
20853 cls : 'fc-first fc-last',
20871 cls : 'fc-content',
20872 style : "position: relative;",
20875 cls : 'fc-view fc-view-month fc-grid',
20876 style : 'position: relative',
20877 unselectable : 'on',
20880 cls : 'fc-event-container',
20881 style : 'position:absolute;z-index:8;top:0;left:0;'
20899 initEvents : function()
20902 throw "can not find store for calendar";
20908 style: "text-align:center",
20912 style: "background-color:white;width:50%;margin:250 auto",
20916 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20927 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20929 var size = this.el.select('.fc-content', true).first().getSize();
20930 this.maskEl.setSize(size.width, size.height);
20931 this.maskEl.enableDisplayMode("block");
20932 if(!this.loadMask){
20933 this.maskEl.hide();
20936 this.store = Roo.factory(this.store, Roo.data);
20937 this.store.on('load', this.onLoad, this);
20938 this.store.on('beforeload', this.onBeforeLoad, this);
20942 this.cells = this.el.select('.fc-day',true);
20943 //Roo.log(this.cells);
20944 this.textNodes = this.el.query('.fc-day-number');
20945 this.cells.addClassOnOver('fc-state-hover');
20947 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20948 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20949 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20950 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20952 this.on('monthchange', this.onMonthChange, this);
20954 this.update(new Date().clearTime());
20957 resize : function() {
20958 var sz = this.el.getSize();
20960 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20961 this.el.select('.fc-day-content div',true).setHeight(34);
20966 showPrevMonth : function(e){
20967 this.update(this.activeDate.add("mo", -1));
20969 showToday : function(e){
20970 this.update(new Date().clearTime());
20973 showNextMonth : function(e){
20974 this.update(this.activeDate.add("mo", 1));
20978 showPrevYear : function(){
20979 this.update(this.activeDate.add("y", -1));
20983 showNextYear : function(){
20984 this.update(this.activeDate.add("y", 1));
20989 update : function(date)
20991 var vd = this.activeDate;
20992 this.activeDate = date;
20993 // if(vd && this.el){
20994 // var t = date.getTime();
20995 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20996 // Roo.log('using add remove');
20998 // this.fireEvent('monthchange', this, date);
21000 // this.cells.removeClass("fc-state-highlight");
21001 // this.cells.each(function(c){
21002 // if(c.dateValue == t){
21003 // c.addClass("fc-state-highlight");
21004 // setTimeout(function(){
21005 // try{c.dom.firstChild.focus();}catch(e){}
21015 var days = date.getDaysInMonth();
21017 var firstOfMonth = date.getFirstDateOfMonth();
21018 var startingPos = firstOfMonth.getDay()-this.startDay;
21020 if(startingPos < this.startDay){
21024 var pm = date.add(Date.MONTH, -1);
21025 var prevStart = pm.getDaysInMonth()-startingPos;
21027 this.cells = this.el.select('.fc-day',true);
21028 this.textNodes = this.el.query('.fc-day-number');
21029 this.cells.addClassOnOver('fc-state-hover');
21031 var cells = this.cells.elements;
21032 var textEls = this.textNodes;
21034 Roo.each(cells, function(cell){
21035 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21038 days += startingPos;
21040 // convert everything to numbers so it's fast
21041 var day = 86400000;
21042 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21045 //Roo.log(prevStart);
21047 var today = new Date().clearTime().getTime();
21048 var sel = date.clearTime().getTime();
21049 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21050 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21051 var ddMatch = this.disabledDatesRE;
21052 var ddText = this.disabledDatesText;
21053 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21054 var ddaysText = this.disabledDaysText;
21055 var format = this.format;
21057 var setCellClass = function(cal, cell){
21061 //Roo.log('set Cell Class');
21063 var t = d.getTime();
21067 cell.dateValue = t;
21069 cell.className += " fc-today";
21070 cell.className += " fc-state-highlight";
21071 cell.title = cal.todayText;
21074 // disable highlight in other month..
21075 //cell.className += " fc-state-highlight";
21080 cell.className = " fc-state-disabled";
21081 cell.title = cal.minText;
21085 cell.className = " fc-state-disabled";
21086 cell.title = cal.maxText;
21090 if(ddays.indexOf(d.getDay()) != -1){
21091 cell.title = ddaysText;
21092 cell.className = " fc-state-disabled";
21095 if(ddMatch && format){
21096 var fvalue = d.dateFormat(format);
21097 if(ddMatch.test(fvalue)){
21098 cell.title = ddText.replace("%0", fvalue);
21099 cell.className = " fc-state-disabled";
21103 if (!cell.initialClassName) {
21104 cell.initialClassName = cell.dom.className;
21107 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21112 for(; i < startingPos; i++) {
21113 textEls[i].innerHTML = (++prevStart);
21114 d.setDate(d.getDate()+1);
21116 cells[i].className = "fc-past fc-other-month";
21117 setCellClass(this, cells[i]);
21122 for(; i < days; i++){
21123 intDay = i - startingPos + 1;
21124 textEls[i].innerHTML = (intDay);
21125 d.setDate(d.getDate()+1);
21127 cells[i].className = ''; // "x-date-active";
21128 setCellClass(this, cells[i]);
21132 for(; i < 42; i++) {
21133 textEls[i].innerHTML = (++extraDays);
21134 d.setDate(d.getDate()+1);
21136 cells[i].className = "fc-future fc-other-month";
21137 setCellClass(this, cells[i]);
21140 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21142 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21144 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21145 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21147 if(totalRows != 6){
21148 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21149 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21152 this.fireEvent('monthchange', this, date);
21156 if(!this.internalRender){
21157 var main = this.el.dom.firstChild;
21158 var w = main.offsetWidth;
21159 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21160 Roo.fly(main).setWidth(w);
21161 this.internalRender = true;
21162 // opera does not respect the auto grow header center column
21163 // then, after it gets a width opera refuses to recalculate
21164 // without a second pass
21165 if(Roo.isOpera && !this.secondPass){
21166 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21167 this.secondPass = true;
21168 this.update.defer(10, this, [date]);
21175 findCell : function(dt) {
21176 dt = dt.clearTime().getTime();
21178 this.cells.each(function(c){
21179 //Roo.log("check " +c.dateValue + '?=' + dt);
21180 if(c.dateValue == dt){
21190 findCells : function(ev) {
21191 var s = ev.start.clone().clearTime().getTime();
21193 var e= ev.end.clone().clearTime().getTime();
21196 this.cells.each(function(c){
21197 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21199 if(c.dateValue > e){
21202 if(c.dateValue < s){
21211 // findBestRow: function(cells)
21215 // for (var i =0 ; i < cells.length;i++) {
21216 // ret = Math.max(cells[i].rows || 0,ret);
21223 addItem : function(ev)
21225 // look for vertical location slot in
21226 var cells = this.findCells(ev);
21228 // ev.row = this.findBestRow(cells);
21230 // work out the location.
21234 for(var i =0; i < cells.length; i++) {
21236 cells[i].row = cells[0].row;
21239 cells[i].row = cells[i].row + 1;
21249 if (crow.start.getY() == cells[i].getY()) {
21251 crow.end = cells[i];
21268 cells[0].events.push(ev);
21270 this.calevents.push(ev);
21273 clearEvents: function() {
21275 if(!this.calevents){
21279 Roo.each(this.cells.elements, function(c){
21285 Roo.each(this.calevents, function(e) {
21286 Roo.each(e.els, function(el) {
21287 el.un('mouseenter' ,this.onEventEnter, this);
21288 el.un('mouseleave' ,this.onEventLeave, this);
21293 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21299 renderEvents: function()
21303 this.cells.each(function(c) {
21312 if(c.row != c.events.length){
21313 r = 4 - (4 - (c.row - c.events.length));
21316 c.events = ev.slice(0, r);
21317 c.more = ev.slice(r);
21319 if(c.more.length && c.more.length == 1){
21320 c.events.push(c.more.pop());
21323 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21327 this.cells.each(function(c) {
21329 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21332 for (var e = 0; e < c.events.length; e++){
21333 var ev = c.events[e];
21334 var rows = ev.rows;
21336 for(var i = 0; i < rows.length; i++) {
21338 // how many rows should it span..
21341 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21342 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21344 unselectable : "on",
21347 cls: 'fc-event-inner',
21351 // cls: 'fc-event-time',
21352 // html : cells.length > 1 ? '' : ev.time
21356 cls: 'fc-event-title',
21357 html : String.format('{0}', ev.title)
21364 cls: 'ui-resizable-handle ui-resizable-e',
21365 html : '  '
21372 cfg.cls += ' fc-event-start';
21374 if ((i+1) == rows.length) {
21375 cfg.cls += ' fc-event-end';
21378 var ctr = _this.el.select('.fc-event-container',true).first();
21379 var cg = ctr.createChild(cfg);
21381 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21382 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21384 var r = (c.more.length) ? 1 : 0;
21385 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21386 cg.setWidth(ebox.right - sbox.x -2);
21388 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21389 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21390 cg.on('click', _this.onEventClick, _this, ev);
21401 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21402 style : 'position: absolute',
21403 unselectable : "on",
21406 cls: 'fc-event-inner',
21410 cls: 'fc-event-title',
21418 cls: 'ui-resizable-handle ui-resizable-e',
21419 html : '  '
21425 var ctr = _this.el.select('.fc-event-container',true).first();
21426 var cg = ctr.createChild(cfg);
21428 var sbox = c.select('.fc-day-content',true).first().getBox();
21429 var ebox = c.select('.fc-day-content',true).first().getBox();
21431 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21432 cg.setWidth(ebox.right - sbox.x -2);
21434 cg.on('click', _this.onMoreEventClick, _this, c.more);
21444 onEventEnter: function (e, el,event,d) {
21445 this.fireEvent('evententer', this, el, event);
21448 onEventLeave: function (e, el,event,d) {
21449 this.fireEvent('eventleave', this, el, event);
21452 onEventClick: function (e, el,event,d) {
21453 this.fireEvent('eventclick', this, el, event);
21456 onMonthChange: function () {
21460 onMoreEventClick: function(e, el, more)
21464 this.calpopover.placement = 'right';
21465 this.calpopover.setTitle('More');
21467 this.calpopover.setContent('');
21469 var ctr = this.calpopover.el.select('.popover-content', true).first();
21471 Roo.each(more, function(m){
21473 cls : 'fc-event-hori fc-event-draggable',
21476 var cg = ctr.createChild(cfg);
21478 cg.on('click', _this.onEventClick, _this, m);
21481 this.calpopover.show(el);
21486 onLoad: function ()
21488 this.calevents = [];
21491 if(this.store.getCount() > 0){
21492 this.store.data.each(function(d){
21495 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21496 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21497 time : d.data.start_time,
21498 title : d.data.title,
21499 description : d.data.description,
21500 venue : d.data.venue
21505 this.renderEvents();
21507 if(this.calevents.length && this.loadMask){
21508 this.maskEl.hide();
21512 onBeforeLoad: function()
21514 this.clearEvents();
21516 this.maskEl.show();
21530 * @class Roo.bootstrap.Popover
21531 * @extends Roo.bootstrap.Component
21532 * @parent none builder
21533 * @children Roo.bootstrap.Component
21534 * Bootstrap Popover class
21535 * @cfg {String} html contents of the popover (or false to use children..)
21536 * @cfg {String} title of popover (or false to hide)
21537 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21538 * @cfg {String} trigger click || hover (or false to trigger manually)
21539 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21540 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21541 * - if false and it has a 'parent' then it will be automatically added to that element
21542 * - if string - Roo.get will be called
21543 * @cfg {Number} delay - delay before showing
21546 * Create a new Popover
21547 * @param {Object} config The config object
21550 Roo.bootstrap.Popover = function(config){
21551 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21557 * After the popover show
21559 * @param {Roo.bootstrap.Popover} this
21564 * After the popover hide
21566 * @param {Roo.bootstrap.Popover} this
21572 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21577 placement : 'right',
21578 trigger : 'hover', // hover
21584 can_build_overlaid : false,
21586 maskEl : false, // the mask element
21589 alignEl : false, // when show is called with an element - this get's stored.
21591 getChildContainer : function()
21593 return this.contentEl;
21596 getPopoverHeader : function()
21598 this.title = true; // flag not to hide it..
21599 this.headerEl.addClass('p-0');
21600 return this.headerEl
21604 getAutoCreate : function(){
21607 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21608 style: 'display:block',
21614 cls : 'popover-inner ',
21618 cls: 'popover-title popover-header',
21619 html : this.title === false ? '' : this.title
21622 cls : 'popover-content popover-body ' + (this.cls || ''),
21623 html : this.html || ''
21634 * @param {string} the title
21636 setTitle: function(str)
21640 this.headerEl.dom.innerHTML = str;
21645 * @param {string} the body content
21647 setContent: function(str)
21650 if (this.contentEl) {
21651 this.contentEl.dom.innerHTML = str;
21655 // as it get's added to the bottom of the page.
21656 onRender : function(ct, position)
21658 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21663 var cfg = Roo.apply({}, this.getAutoCreate());
21667 cfg.cls += ' ' + this.cls;
21670 cfg.style = this.style;
21672 //Roo.log("adding to ");
21673 this.el = Roo.get(document.body).createChild(cfg, position);
21674 // Roo.log(this.el);
21677 this.contentEl = this.el.select('.popover-content',true).first();
21678 this.headerEl = this.el.select('.popover-title',true).first();
21681 if(typeof(this.items) != 'undefined'){
21682 var items = this.items;
21685 for(var i =0;i < items.length;i++) {
21686 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21690 this.items = nitems;
21692 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21693 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21700 resizeMask : function()
21702 this.maskEl.setSize(
21703 Roo.lib.Dom.getViewWidth(true),
21704 Roo.lib.Dom.getViewHeight(true)
21708 initEvents : function()
21712 Roo.bootstrap.Popover.register(this);
21715 this.arrowEl = this.el.select('.arrow',true).first();
21716 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21717 this.el.enableDisplayMode('block');
21721 if (this.over === false && !this.parent()) {
21724 if (this.triggers === false) {
21729 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21730 var triggers = this.trigger ? this.trigger.split(' ') : [];
21731 Roo.each(triggers, function(trigger) {
21733 if (trigger == 'click') {
21734 on_el.on('click', this.toggle, this);
21735 } else if (trigger != 'manual') {
21736 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21737 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21739 on_el.on(eventIn ,this.enter, this);
21740 on_el.on(eventOut, this.leave, this);
21750 toggle : function () {
21751 this.hoverState == 'in' ? this.leave() : this.enter();
21754 enter : function () {
21756 clearTimeout(this.timeout);
21758 this.hoverState = 'in';
21760 if (!this.delay || !this.delay.show) {
21765 this.timeout = setTimeout(function () {
21766 if (_t.hoverState == 'in') {
21769 }, this.delay.show)
21772 leave : function() {
21773 clearTimeout(this.timeout);
21775 this.hoverState = 'out';
21777 if (!this.delay || !this.delay.hide) {
21782 this.timeout = setTimeout(function () {
21783 if (_t.hoverState == 'out') {
21786 }, this.delay.hide)
21790 * update the position of the dialog
21791 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21796 doAlign : function()
21799 if (this.alignEl) {
21800 this.updatePosition(this.placement, true);
21803 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21804 var es = this.el.getSize();
21805 var x = Roo.lib.Dom.getViewWidth()/2;
21806 var y = Roo.lib.Dom.getViewHeight()/2;
21807 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21819 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21820 * @param {string} (left|right|top|bottom) position
21822 show : function (on_el, placement)
21824 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21825 on_el = on_el || false; // default to false
21828 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21829 on_el = this.parent().el;
21830 } else if (this.over) {
21831 on_el = Roo.get(this.over);
21836 this.alignEl = Roo.get( on_el );
21839 this.render(document.body);
21845 if (this.title === false) {
21846 this.headerEl.hide();
21851 this.el.dom.style.display = 'block';
21855 //var arrow = this.el.select('.arrow',true).first();
21856 //arrow.set(align[2],
21858 this.el.addClass('in');
21862 this.hoverState = 'in';
21865 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21866 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21867 this.maskEl.dom.style.display = 'block';
21868 this.maskEl.addClass('show');
21870 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21872 this.fireEvent('show', this);
21876 * fire this manually after loading a grid in the table for example
21877 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21878 * @param {Boolean} try and move it if we cant get right position.
21880 updatePosition : function(placement, try_move)
21882 // allow for calling with no parameters
21883 placement = placement ? placement : this.placement;
21884 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21886 this.el.removeClass([
21887 'fade','top','bottom', 'left', 'right','in',
21888 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21890 this.el.addClass(placement + ' bs-popover-' + placement);
21892 if (!this.alignEl ) {
21896 switch (placement) {
21898 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21899 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21900 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21901 //normal display... or moved up/down.
21902 this.el.setXY(offset);
21903 var xy = this.alignEl.getAnchorXY('tr', false);
21905 this.arrowEl.setXY(xy);
21908 // continue through...
21909 return this.updatePosition('left', false);
21913 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21914 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21915 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21916 //normal display... or moved up/down.
21917 this.el.setXY(offset);
21918 var xy = this.alignEl.getAnchorXY('tl', false);
21919 xy[0]-=10;xy[1]+=5; // << fix me
21920 this.arrowEl.setXY(xy);
21924 return this.updatePosition('right', false);
21927 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21928 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21929 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21930 //normal display... or moved up/down.
21931 this.el.setXY(offset);
21932 var xy = this.alignEl.getAnchorXY('t', false);
21933 xy[1]-=10; // << fix me
21934 this.arrowEl.setXY(xy);
21938 return this.updatePosition('bottom', false);
21941 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21942 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21943 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21944 //normal display... or moved up/down.
21945 this.el.setXY(offset);
21946 var xy = this.alignEl.getAnchorXY('b', false);
21947 xy[1]+=2; // << fix me
21948 this.arrowEl.setXY(xy);
21952 return this.updatePosition('top', false);
21963 this.el.setXY([0,0]);
21964 this.el.removeClass('in');
21966 this.hoverState = null;
21967 this.maskEl.hide(); // always..
21968 this.fireEvent('hide', this);
21974 Roo.apply(Roo.bootstrap.Popover, {
21977 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21978 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21979 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21980 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21985 clickHander : false,
21989 onMouseDown : function(e)
21991 if (this.popups.length && !e.getTarget(".roo-popover")) {
21992 /// what is nothing is showing..
22001 register : function(popup)
22003 if (!Roo.bootstrap.Popover.clickHandler) {
22004 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22006 // hide other popups.
22007 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22008 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22009 this.hideAll(); //<< why?
22010 //this.popups.push(popup);
22012 hideAll : function()
22014 this.popups.forEach(function(p) {
22018 onShow : function() {
22019 Roo.bootstrap.Popover.popups.push(this);
22021 onHide : function() {
22022 Roo.bootstrap.Popover.popups.remove(this);
22027 * @class Roo.bootstrap.PopoverNav
22028 * @extends Roo.bootstrap.nav.Simplebar
22029 * @parent Roo.bootstrap.Popover
22030 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22032 * Bootstrap Popover header navigation class
22033 * FIXME? should this go under nav?
22037 * Create a new Popover Header Navigation
22038 * @param {Object} config The config object
22041 Roo.bootstrap.PopoverNav = function(config){
22042 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22045 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22048 container_method : 'getPopoverHeader'
22066 * @class Roo.bootstrap.Progress
22067 * @extends Roo.bootstrap.Component
22068 * @children Roo.bootstrap.ProgressBar
22069 * Bootstrap Progress class
22070 * @cfg {Boolean} striped striped of the progress bar
22071 * @cfg {Boolean} active animated of the progress bar
22075 * Create a new Progress
22076 * @param {Object} config The config object
22079 Roo.bootstrap.Progress = function(config){
22080 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22083 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22088 getAutoCreate : function(){
22096 cfg.cls += ' progress-striped';
22100 cfg.cls += ' active';
22119 * @class Roo.bootstrap.ProgressBar
22120 * @extends Roo.bootstrap.Component
22121 * Bootstrap ProgressBar class
22122 * @cfg {Number} aria_valuenow aria-value now
22123 * @cfg {Number} aria_valuemin aria-value min
22124 * @cfg {Number} aria_valuemax aria-value max
22125 * @cfg {String} label label for the progress bar
22126 * @cfg {String} panel (success | info | warning | danger )
22127 * @cfg {String} role role of the progress bar
22128 * @cfg {String} sr_only text
22132 * Create a new ProgressBar
22133 * @param {Object} config The config object
22136 Roo.bootstrap.ProgressBar = function(config){
22137 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22140 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22144 aria_valuemax : 100,
22150 getAutoCreate : function()
22155 cls: 'progress-bar',
22156 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22168 cfg.role = this.role;
22171 if(this.aria_valuenow){
22172 cfg['aria-valuenow'] = this.aria_valuenow;
22175 if(this.aria_valuemin){
22176 cfg['aria-valuemin'] = this.aria_valuemin;
22179 if(this.aria_valuemax){
22180 cfg['aria-valuemax'] = this.aria_valuemax;
22183 if(this.label && !this.sr_only){
22184 cfg.html = this.label;
22188 cfg.cls += ' progress-bar-' + this.panel;
22194 update : function(aria_valuenow)
22196 this.aria_valuenow = aria_valuenow;
22198 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22206 * @class Roo.bootstrap.TabGroup
22207 * @extends Roo.bootstrap.Column
22208 * @children Roo.bootstrap.TabPanel
22209 * Bootstrap Column class
22210 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22211 * @cfg {Boolean} carousel true to make the group behave like a carousel
22212 * @cfg {Boolean} bullets show bullets for the panels
22213 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22214 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22215 * @cfg {Boolean} showarrow (true|false) show arrow default true
22218 * Create a new TabGroup
22219 * @param {Object} config The config object
22222 Roo.bootstrap.TabGroup = function(config){
22223 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22225 this.navId = Roo.id();
22228 Roo.bootstrap.TabGroup.register(this);
22232 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22235 transition : false,
22240 slideOnTouch : false,
22243 getAutoCreate : function()
22245 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22247 cfg.cls += ' tab-content';
22249 if (this.carousel) {
22250 cfg.cls += ' carousel slide';
22253 cls : 'carousel-inner',
22257 if(this.bullets && !Roo.isTouch){
22260 cls : 'carousel-bullets',
22264 if(this.bullets_cls){
22265 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22272 cfg.cn[0].cn.push(bullets);
22275 if(this.showarrow){
22276 cfg.cn[0].cn.push({
22278 class : 'carousel-arrow',
22282 class : 'carousel-prev',
22286 class : 'fa fa-chevron-left'
22292 class : 'carousel-next',
22296 class : 'fa fa-chevron-right'
22309 initEvents: function()
22311 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22312 // this.el.on("touchstart", this.onTouchStart, this);
22315 if(this.autoslide){
22318 this.slideFn = window.setInterval(function() {
22319 _this.showPanelNext();
22323 if(this.showarrow){
22324 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22325 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22331 // onTouchStart : function(e, el, o)
22333 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22337 // this.showPanelNext();
22341 getChildContainer : function()
22343 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22347 * register a Navigation item
22348 * @param {Roo.bootstrap.nav.Item} the navitem to add
22350 register : function(item)
22352 this.tabs.push( item);
22353 item.navId = this.navId; // not really needed..
22358 getActivePanel : function()
22361 Roo.each(this.tabs, function(t) {
22371 getPanelByName : function(n)
22374 Roo.each(this.tabs, function(t) {
22375 if (t.tabId == n) {
22383 indexOfPanel : function(p)
22386 Roo.each(this.tabs, function(t,i) {
22387 if (t.tabId == p.tabId) {
22396 * show a specific panel
22397 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22398 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22400 showPanel : function (pan)
22402 if(this.transition || typeof(pan) == 'undefined'){
22403 Roo.log("waiting for the transitionend");
22407 if (typeof(pan) == 'number') {
22408 pan = this.tabs[pan];
22411 if (typeof(pan) == 'string') {
22412 pan = this.getPanelByName(pan);
22415 var cur = this.getActivePanel();
22418 Roo.log('pan or acitve pan is undefined');
22422 if (pan.tabId == this.getActivePanel().tabId) {
22426 if (false === cur.fireEvent('beforedeactivate')) {
22430 if(this.bullets > 0 && !Roo.isTouch){
22431 this.setActiveBullet(this.indexOfPanel(pan));
22434 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22436 //class="carousel-item carousel-item-next carousel-item-left"
22438 this.transition = true;
22439 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22440 var lr = dir == 'next' ? 'left' : 'right';
22441 pan.el.addClass(dir); // or prev
22442 pan.el.addClass('carousel-item-' + dir); // or prev
22443 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22444 cur.el.addClass(lr); // or right
22445 pan.el.addClass(lr);
22446 cur.el.addClass('carousel-item-' +lr); // or right
22447 pan.el.addClass('carousel-item-' +lr);
22451 cur.el.on('transitionend', function() {
22452 Roo.log("trans end?");
22454 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22455 pan.setActive(true);
22457 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22458 cur.setActive(false);
22460 _this.transition = false;
22462 }, this, { single: true } );
22467 cur.setActive(false);
22468 pan.setActive(true);
22473 showPanelNext : function()
22475 var i = this.indexOfPanel(this.getActivePanel());
22477 if (i >= this.tabs.length - 1 && !this.autoslide) {
22481 if (i >= this.tabs.length - 1 && this.autoslide) {
22485 this.showPanel(this.tabs[i+1]);
22488 showPanelPrev : function()
22490 var i = this.indexOfPanel(this.getActivePanel());
22492 if (i < 1 && !this.autoslide) {
22496 if (i < 1 && this.autoslide) {
22497 i = this.tabs.length;
22500 this.showPanel(this.tabs[i-1]);
22504 addBullet: function()
22506 if(!this.bullets || Roo.isTouch){
22509 var ctr = this.el.select('.carousel-bullets',true).first();
22510 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22511 var bullet = ctr.createChild({
22512 cls : 'bullet bullet-' + i
22513 },ctr.dom.lastChild);
22518 bullet.on('click', (function(e, el, o, ii, t){
22520 e.preventDefault();
22522 this.showPanel(ii);
22524 if(this.autoslide && this.slideFn){
22525 clearInterval(this.slideFn);
22526 this.slideFn = window.setInterval(function() {
22527 _this.showPanelNext();
22531 }).createDelegate(this, [i, bullet], true));
22536 setActiveBullet : function(i)
22542 Roo.each(this.el.select('.bullet', true).elements, function(el){
22543 el.removeClass('selected');
22546 var bullet = this.el.select('.bullet-' + i, true).first();
22552 bullet.addClass('selected');
22563 Roo.apply(Roo.bootstrap.TabGroup, {
22567 * register a Navigation Group
22568 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22570 register : function(navgrp)
22572 this.groups[navgrp.navId] = navgrp;
22576 * fetch a Navigation Group based on the navigation ID
22577 * if one does not exist , it will get created.
22578 * @param {string} the navgroup to add
22579 * @returns {Roo.bootstrap.nav.Group} the navgroup
22581 get: function(navId) {
22582 if (typeof(this.groups[navId]) == 'undefined') {
22583 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22585 return this.groups[navId] ;
22600 * @class Roo.bootstrap.TabPanel
22601 * @extends Roo.bootstrap.Component
22602 * @children Roo.bootstrap.Component
22603 * Bootstrap TabPanel class
22604 * @cfg {Boolean} active panel active
22605 * @cfg {String} html panel content
22606 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22607 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22608 * @cfg {String} href click to link..
22609 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22613 * Create a new TabPanel
22614 * @param {Object} config The config object
22617 Roo.bootstrap.TabPanel = function(config){
22618 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22622 * Fires when the active status changes
22623 * @param {Roo.bootstrap.TabPanel} this
22624 * @param {Boolean} state the new state
22629 * @event beforedeactivate
22630 * Fires before a tab is de-activated - can be used to do validation on a form.
22631 * @param {Roo.bootstrap.TabPanel} this
22632 * @return {Boolean} false if there is an error
22635 'beforedeactivate': true
22638 this.tabId = this.tabId || Roo.id();
22642 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22649 touchSlide : false,
22650 getAutoCreate : function(){
22655 // item is needed for carousel - not sure if it has any effect otherwise
22656 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22657 html: this.html || ''
22661 cfg.cls += ' active';
22665 cfg.tabId = this.tabId;
22673 initEvents: function()
22675 var p = this.parent();
22677 this.navId = this.navId || p.navId;
22679 if (typeof(this.navId) != 'undefined') {
22680 // not really needed.. but just in case.. parent should be a NavGroup.
22681 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22685 var i = tg.tabs.length - 1;
22687 if(this.active && tg.bullets > 0 && i < tg.bullets){
22688 tg.setActiveBullet(i);
22692 this.el.on('click', this.onClick, this);
22694 if(Roo.isTouch && this.touchSlide){
22695 this.el.on("touchstart", this.onTouchStart, this);
22696 this.el.on("touchmove", this.onTouchMove, this);
22697 this.el.on("touchend", this.onTouchEnd, this);
22702 onRender : function(ct, position)
22704 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22707 setActive : function(state)
22709 Roo.log("panel - set active " + this.tabId + "=" + state);
22711 this.active = state;
22713 this.el.removeClass('active');
22715 } else if (!this.el.hasClass('active')) {
22716 this.el.addClass('active');
22719 this.fireEvent('changed', this, state);
22722 onClick : function(e)
22724 e.preventDefault();
22726 if(!this.href.length){
22730 window.location.href = this.href;
22739 onTouchStart : function(e)
22741 this.swiping = false;
22743 this.startX = e.browserEvent.touches[0].clientX;
22744 this.startY = e.browserEvent.touches[0].clientY;
22747 onTouchMove : function(e)
22749 this.swiping = true;
22751 this.endX = e.browserEvent.touches[0].clientX;
22752 this.endY = e.browserEvent.touches[0].clientY;
22755 onTouchEnd : function(e)
22762 var tabGroup = this.parent();
22764 if(this.endX > this.startX){ // swiping right
22765 tabGroup.showPanelPrev();
22769 if(this.startX > this.endX){ // swiping left
22770 tabGroup.showPanelNext();
22789 * @class Roo.bootstrap.form.DateField
22790 * @extends Roo.bootstrap.form.Input
22791 * Bootstrap DateField class
22792 * @cfg {Number} weekStart default 0
22793 * @cfg {String} viewMode default empty, (months|years)
22794 * @cfg {String} minViewMode default empty, (months|years)
22795 * @cfg {Number} startDate default -Infinity
22796 * @cfg {Number} endDate default Infinity
22797 * @cfg {Boolean} todayHighlight default false
22798 * @cfg {Boolean} todayBtn default false
22799 * @cfg {Boolean} calendarWeeks default false
22800 * @cfg {Object} daysOfWeekDisabled default empty
22801 * @cfg {Boolean} singleMode default false (true | false)
22803 * @cfg {Boolean} keyboardNavigation default true
22804 * @cfg {String} language default en
22807 * Create a new DateField
22808 * @param {Object} config The config object
22811 Roo.bootstrap.form.DateField = function(config){
22812 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22816 * Fires when this field show.
22817 * @param {Roo.bootstrap.form.DateField} this
22818 * @param {Mixed} date The date value
22823 * Fires when this field hide.
22824 * @param {Roo.bootstrap.form.DateField} this
22825 * @param {Mixed} date The date value
22830 * Fires when select a date.
22831 * @param {Roo.bootstrap.form.DateField} this
22832 * @param {Mixed} date The date value
22836 * @event beforeselect
22837 * Fires when before select a date.
22838 * @param {Roo.bootstrap.form.DateField} this
22839 * @param {Mixed} date The date value
22841 beforeselect : true
22845 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22848 * @cfg {String} format
22849 * The default date format string which can be overriden for localization support. The format must be
22850 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22854 * @cfg {String} altFormats
22855 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22856 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22858 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22866 todayHighlight : false,
22872 keyboardNavigation: true,
22874 calendarWeeks: false,
22876 startDate: -Infinity,
22880 daysOfWeekDisabled: [],
22884 singleMode : false,
22886 UTCDate: function()
22888 return new Date(Date.UTC.apply(Date, arguments));
22891 UTCToday: function()
22893 var today = new Date();
22894 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22897 getDate: function() {
22898 var d = this.getUTCDate();
22899 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22902 getUTCDate: function() {
22906 setDate: function(d) {
22907 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22910 setUTCDate: function(d) {
22912 this.setValue(this.formatDate(this.date));
22915 onRender: function(ct, position)
22918 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22920 this.language = this.language || 'en';
22921 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22922 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22924 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22925 this.format = this.format || 'm/d/y';
22926 this.isInline = false;
22927 this.isInput = true;
22928 this.component = this.el.select('.add-on', true).first() || false;
22929 this.component = (this.component && this.component.length === 0) ? false : this.component;
22930 this.hasInput = this.component && this.inputEl().length;
22932 if (typeof(this.minViewMode === 'string')) {
22933 switch (this.minViewMode) {
22935 this.minViewMode = 1;
22938 this.minViewMode = 2;
22941 this.minViewMode = 0;
22946 if (typeof(this.viewMode === 'string')) {
22947 switch (this.viewMode) {
22960 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22962 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22964 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22966 this.picker().on('mousedown', this.onMousedown, this);
22967 this.picker().on('click', this.onClick, this);
22969 this.picker().addClass('datepicker-dropdown');
22971 this.startViewMode = this.viewMode;
22973 if(this.singleMode){
22974 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22975 v.setVisibilityMode(Roo.Element.DISPLAY);
22979 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22980 v.setStyle('width', '189px');
22984 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22985 if(!this.calendarWeeks){
22990 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22991 v.attr('colspan', function(i, val){
22992 return parseInt(val) + 1;
22997 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22999 this.setStartDate(this.startDate);
23000 this.setEndDate(this.endDate);
23002 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23009 if(this.isInline) {
23014 picker : function()
23016 return this.pickerEl;
23017 // return this.el.select('.datepicker', true).first();
23020 fillDow: function()
23022 var dowCnt = this.weekStart;
23031 if(this.calendarWeeks){
23039 while (dowCnt < this.weekStart + 7) {
23043 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23047 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23050 fillMonths: function()
23053 var months = this.picker().select('>.datepicker-months td', true).first();
23055 months.dom.innerHTML = '';
23061 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23064 months.createChild(month);
23071 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;
23073 if (this.date < this.startDate) {
23074 this.viewDate = new Date(this.startDate);
23075 } else if (this.date > this.endDate) {
23076 this.viewDate = new Date(this.endDate);
23078 this.viewDate = new Date(this.date);
23086 var d = new Date(this.viewDate),
23087 year = d.getUTCFullYear(),
23088 month = d.getUTCMonth(),
23089 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23090 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23091 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23092 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23093 currentDate = this.date && this.date.valueOf(),
23094 today = this.UTCToday();
23096 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23098 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23100 // this.picker.select('>tfoot th.today').
23101 // .text(dates[this.language].today)
23102 // .toggle(this.todayBtn !== false);
23104 this.updateNavArrows();
23107 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23109 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23111 prevMonth.setUTCDate(day);
23113 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23115 var nextMonth = new Date(prevMonth);
23117 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23119 nextMonth = nextMonth.valueOf();
23121 var fillMonths = false;
23123 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23125 while(prevMonth.valueOf() <= nextMonth) {
23128 if (prevMonth.getUTCDay() === this.weekStart) {
23130 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23138 if(this.calendarWeeks){
23139 // ISO 8601: First week contains first thursday.
23140 // ISO also states week starts on Monday, but we can be more abstract here.
23142 // Start of current week: based on weekstart/current date
23143 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23144 // Thursday of this week
23145 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23146 // First Thursday of year, year from thursday
23147 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23148 // Calendar week: ms between thursdays, div ms per day, div 7 days
23149 calWeek = (th - yth) / 864e5 / 7 + 1;
23151 fillMonths.cn.push({
23159 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23161 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23164 if (this.todayHighlight &&
23165 prevMonth.getUTCFullYear() == today.getFullYear() &&
23166 prevMonth.getUTCMonth() == today.getMonth() &&
23167 prevMonth.getUTCDate() == today.getDate()) {
23168 clsName += ' today';
23171 if (currentDate && prevMonth.valueOf() === currentDate) {
23172 clsName += ' active';
23175 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23176 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23177 clsName += ' disabled';
23180 fillMonths.cn.push({
23182 cls: 'day ' + clsName,
23183 html: prevMonth.getDate()
23186 prevMonth.setDate(prevMonth.getDate()+1);
23189 var currentYear = this.date && this.date.getUTCFullYear();
23190 var currentMonth = this.date && this.date.getUTCMonth();
23192 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23194 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23195 v.removeClass('active');
23197 if(currentYear === year && k === currentMonth){
23198 v.addClass('active');
23201 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23202 v.addClass('disabled');
23208 year = parseInt(year/10, 10) * 10;
23210 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23212 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23215 for (var i = -1; i < 11; i++) {
23216 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23218 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23226 showMode: function(dir)
23229 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23232 Roo.each(this.picker().select('>div',true).elements, function(v){
23233 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23236 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23241 if(this.isInline) {
23245 this.picker().removeClass(['bottom', 'top']);
23247 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23249 * place to the top of element!
23253 this.picker().addClass('top');
23254 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23259 this.picker().addClass('bottom');
23261 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23264 parseDate : function(value)
23266 if(!value || value instanceof Date){
23269 var v = Date.parseDate(value, this.format);
23270 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23271 v = Date.parseDate(value, 'Y-m-d');
23273 if(!v && this.altFormats){
23274 if(!this.altFormatsArray){
23275 this.altFormatsArray = this.altFormats.split("|");
23277 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23278 v = Date.parseDate(value, this.altFormatsArray[i]);
23284 formatDate : function(date, fmt)
23286 return (!date || !(date instanceof Date)) ?
23287 date : date.dateFormat(fmt || this.format);
23290 onFocus : function()
23292 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23296 onBlur : function()
23298 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23300 var d = this.inputEl().getValue();
23307 showPopup : function()
23309 this.picker().show();
23313 this.fireEvent('showpopup', this, this.date);
23316 hidePopup : function()
23318 if(this.isInline) {
23321 this.picker().hide();
23322 this.viewMode = this.startViewMode;
23325 this.fireEvent('hidepopup', this, this.date);
23329 onMousedown: function(e)
23331 e.stopPropagation();
23332 e.preventDefault();
23337 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23341 setValue: function(v)
23343 if(this.fireEvent('beforeselect', this, v) !== false){
23344 var d = new Date(this.parseDate(v) ).clearTime();
23346 if(isNaN(d.getTime())){
23347 this.date = this.viewDate = '';
23348 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23352 v = this.formatDate(d);
23354 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23356 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23360 this.fireEvent('select', this, this.date);
23364 getValue: function()
23366 return this.formatDate(this.date);
23369 fireKey: function(e)
23371 if (!this.picker().isVisible()){
23372 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23378 var dateChanged = false,
23380 newDate, newViewDate;
23385 e.preventDefault();
23389 if (!this.keyboardNavigation) {
23392 dir = e.keyCode == 37 ? -1 : 1;
23395 newDate = this.moveYear(this.date, dir);
23396 newViewDate = this.moveYear(this.viewDate, dir);
23397 } else if (e.shiftKey){
23398 newDate = this.moveMonth(this.date, dir);
23399 newViewDate = this.moveMonth(this.viewDate, dir);
23401 newDate = new Date(this.date);
23402 newDate.setUTCDate(this.date.getUTCDate() + dir);
23403 newViewDate = new Date(this.viewDate);
23404 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23406 if (this.dateWithinRange(newDate)){
23407 this.date = newDate;
23408 this.viewDate = newViewDate;
23409 this.setValue(this.formatDate(this.date));
23411 e.preventDefault();
23412 dateChanged = true;
23417 if (!this.keyboardNavigation) {
23420 dir = e.keyCode == 38 ? -1 : 1;
23422 newDate = this.moveYear(this.date, dir);
23423 newViewDate = this.moveYear(this.viewDate, dir);
23424 } else if (e.shiftKey){
23425 newDate = this.moveMonth(this.date, dir);
23426 newViewDate = this.moveMonth(this.viewDate, dir);
23428 newDate = new Date(this.date);
23429 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23430 newViewDate = new Date(this.viewDate);
23431 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23433 if (this.dateWithinRange(newDate)){
23434 this.date = newDate;
23435 this.viewDate = newViewDate;
23436 this.setValue(this.formatDate(this.date));
23438 e.preventDefault();
23439 dateChanged = true;
23443 this.setValue(this.formatDate(this.date));
23445 e.preventDefault();
23448 this.setValue(this.formatDate(this.date));
23462 onClick: function(e)
23464 e.stopPropagation();
23465 e.preventDefault();
23467 var target = e.getTarget();
23469 if(target.nodeName.toLowerCase() === 'i'){
23470 target = Roo.get(target).dom.parentNode;
23473 var nodeName = target.nodeName;
23474 var className = target.className;
23475 var html = target.innerHTML;
23476 //Roo.log(nodeName);
23478 switch(nodeName.toLowerCase()) {
23480 switch(className) {
23486 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23487 switch(this.viewMode){
23489 this.viewDate = this.moveMonth(this.viewDate, dir);
23493 this.viewDate = this.moveYear(this.viewDate, dir);
23499 var date = new Date();
23500 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23502 this.setValue(this.formatDate(this.date));
23509 if (className.indexOf('disabled') < 0) {
23510 if (!this.viewDate) {
23511 this.viewDate = new Date();
23513 this.viewDate.setUTCDate(1);
23514 if (className.indexOf('month') > -1) {
23515 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23517 var year = parseInt(html, 10) || 0;
23518 this.viewDate.setUTCFullYear(year);
23522 if(this.singleMode){
23523 this.setValue(this.formatDate(this.viewDate));
23534 //Roo.log(className);
23535 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23536 var day = parseInt(html, 10) || 1;
23537 var year = (this.viewDate || new Date()).getUTCFullYear(),
23538 month = (this.viewDate || new Date()).getUTCMonth();
23540 if (className.indexOf('old') > -1) {
23547 } else if (className.indexOf('new') > -1) {
23555 //Roo.log([year,month,day]);
23556 this.date = this.UTCDate(year, month, day,0,0,0,0);
23557 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23559 //Roo.log(this.formatDate(this.date));
23560 this.setValue(this.formatDate(this.date));
23567 setStartDate: function(startDate)
23569 this.startDate = startDate || -Infinity;
23570 if (this.startDate !== -Infinity) {
23571 this.startDate = this.parseDate(this.startDate);
23574 this.updateNavArrows();
23577 setEndDate: function(endDate)
23579 this.endDate = endDate || Infinity;
23580 if (this.endDate !== Infinity) {
23581 this.endDate = this.parseDate(this.endDate);
23584 this.updateNavArrows();
23587 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23589 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23590 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23591 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23593 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23594 return parseInt(d, 10);
23597 this.updateNavArrows();
23600 updateNavArrows: function()
23602 if(this.singleMode){
23606 var d = new Date(this.viewDate),
23607 year = d.getUTCFullYear(),
23608 month = d.getUTCMonth();
23610 Roo.each(this.picker().select('.prev', true).elements, function(v){
23612 switch (this.viewMode) {
23615 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23621 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23628 Roo.each(this.picker().select('.next', true).elements, function(v){
23630 switch (this.viewMode) {
23633 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23639 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23647 moveMonth: function(date, dir)
23652 var new_date = new Date(date.valueOf()),
23653 day = new_date.getUTCDate(),
23654 month = new_date.getUTCMonth(),
23655 mag = Math.abs(dir),
23657 dir = dir > 0 ? 1 : -1;
23660 // If going back one month, make sure month is not current month
23661 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23663 return new_date.getUTCMonth() == month;
23665 // If going forward one month, make sure month is as expected
23666 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23668 return new_date.getUTCMonth() != new_month;
23670 new_month = month + dir;
23671 new_date.setUTCMonth(new_month);
23672 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23673 if (new_month < 0 || new_month > 11) {
23674 new_month = (new_month + 12) % 12;
23677 // For magnitudes >1, move one month at a time...
23678 for (var i=0; i<mag; i++) {
23679 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23680 new_date = this.moveMonth(new_date, dir);
23682 // ...then reset the day, keeping it in the new month
23683 new_month = new_date.getUTCMonth();
23684 new_date.setUTCDate(day);
23686 return new_month != new_date.getUTCMonth();
23689 // Common date-resetting loop -- if date is beyond end of month, make it
23692 new_date.setUTCDate(--day);
23693 new_date.setUTCMonth(new_month);
23698 moveYear: function(date, dir)
23700 return this.moveMonth(date, dir*12);
23703 dateWithinRange: function(date)
23705 return date >= this.startDate && date <= this.endDate;
23711 this.picker().remove();
23714 validateValue : function(value)
23716 if(this.getVisibilityEl().hasClass('hidden')){
23720 if(value.length < 1) {
23721 if(this.allowBlank){
23727 if(value.length < this.minLength){
23730 if(value.length > this.maxLength){
23734 var vt = Roo.form.VTypes;
23735 if(!vt[this.vtype](value, this)){
23739 if(typeof this.validator == "function"){
23740 var msg = this.validator(value);
23746 if(this.regex && !this.regex.test(value)){
23750 if(typeof(this.parseDate(value)) == 'undefined'){
23754 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23758 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23768 this.date = this.viewDate = '';
23770 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23775 Roo.apply(Roo.bootstrap.form.DateField, {
23786 html: '<i class="fa fa-arrow-left"/>'
23796 html: '<i class="fa fa-arrow-right"/>'
23838 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23839 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23840 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23841 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23842 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23855 navFnc: 'FullYear',
23860 navFnc: 'FullYear',
23865 Roo.apply(Roo.bootstrap.form.DateField, {
23869 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23873 cls: 'datepicker-days',
23877 cls: 'table-condensed',
23879 Roo.bootstrap.form.DateField.head,
23883 Roo.bootstrap.form.DateField.footer
23890 cls: 'datepicker-months',
23894 cls: 'table-condensed',
23896 Roo.bootstrap.form.DateField.head,
23897 Roo.bootstrap.form.DateField.content,
23898 Roo.bootstrap.form.DateField.footer
23905 cls: 'datepicker-years',
23909 cls: 'table-condensed',
23911 Roo.bootstrap.form.DateField.head,
23912 Roo.bootstrap.form.DateField.content,
23913 Roo.bootstrap.form.DateField.footer
23932 * @class Roo.bootstrap.form.TimeField
23933 * @extends Roo.bootstrap.form.Input
23934 * Bootstrap DateField class
23935 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23939 * Create a new TimeField
23940 * @param {Object} config The config object
23943 Roo.bootstrap.form.TimeField = function(config){
23944 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23948 * Fires when this field show.
23949 * @param {Roo.bootstrap.form.DateField} thisthis
23950 * @param {Mixed} date The date value
23955 * Fires when this field hide.
23956 * @param {Roo.bootstrap.form.DateField} this
23957 * @param {Mixed} date The date value
23962 * Fires when select a date.
23963 * @param {Roo.bootstrap.form.DateField} this
23964 * @param {Mixed} date The date value
23970 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23973 * @cfg {String} format
23974 * The default time format string which can be overriden for localization support. The format must be
23975 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23980 getAutoCreate : function()
23982 this.after = '<i class="fa far fa-clock"></i>';
23983 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23987 onRender: function(ct, position)
23990 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23992 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23994 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23996 this.pop = this.picker().select('>.datepicker-time',true).first();
23997 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23999 this.picker().on('mousedown', this.onMousedown, this);
24000 this.picker().on('click', this.onClick, this);
24002 this.picker().addClass('datepicker-dropdown');
24007 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24008 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24009 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24010 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24011 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24012 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24016 fireKey: function(e){
24017 if (!this.picker().isVisible()){
24018 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24024 e.preventDefault();
24032 this.onTogglePeriod();
24035 this.onIncrementMinutes();
24038 this.onDecrementMinutes();
24047 onClick: function(e) {
24048 e.stopPropagation();
24049 e.preventDefault();
24052 picker : function()
24054 return this.pickerEl;
24057 fillTime: function()
24059 var time = this.pop.select('tbody', true).first();
24061 time.dom.innerHTML = '';
24076 cls: 'hours-up fa fas fa-chevron-up'
24096 cls: 'minutes-up fa fas fa-chevron-up'
24117 cls: 'timepicker-hour',
24132 cls: 'timepicker-minute',
24147 cls: 'btn btn-primary period',
24169 cls: 'hours-down fa fas fa-chevron-down'
24189 cls: 'minutes-down fa fas fa-chevron-down'
24207 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24214 var hours = this.time.getHours();
24215 var minutes = this.time.getMinutes();
24228 hours = hours - 12;
24232 hours = '0' + hours;
24236 minutes = '0' + minutes;
24239 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24240 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24241 this.pop.select('button', true).first().dom.innerHTML = period;
24247 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24249 var cls = ['bottom'];
24251 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24258 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24262 //this.picker().setXY(20000,20000);
24263 this.picker().addClass(cls.join('-'));
24267 Roo.each(cls, function(c){
24272 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24273 //_this.picker().setTop(_this.inputEl().getHeight());
24277 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24279 //_this.picker().setTop(0 - _this.picker().getHeight());
24284 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24288 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24296 onFocus : function()
24298 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24302 onBlur : function()
24304 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24310 this.picker().show();
24315 this.fireEvent('show', this, this.date);
24320 this.picker().hide();
24323 this.fireEvent('hide', this, this.date);
24326 setTime : function()
24329 this.setValue(this.time.format(this.format));
24331 this.fireEvent('select', this, this.date);
24336 onMousedown: function(e){
24337 e.stopPropagation();
24338 e.preventDefault();
24341 onIncrementHours: function()
24343 Roo.log('onIncrementHours');
24344 this.time = this.time.add(Date.HOUR, 1);
24349 onDecrementHours: function()
24351 Roo.log('onDecrementHours');
24352 this.time = this.time.add(Date.HOUR, -1);
24356 onIncrementMinutes: function()
24358 Roo.log('onIncrementMinutes');
24359 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24360 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24364 onDecrementMinutes: function()
24366 Roo.log('onDecrementMinutes');
24367 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24368 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24372 onTogglePeriod: function()
24374 Roo.log('onTogglePeriod');
24375 this.time = this.time.add(Date.HOUR, 12);
24383 Roo.apply(Roo.bootstrap.form.TimeField, {
24387 cls: 'datepicker dropdown-menu',
24391 cls: 'datepicker-time',
24395 cls: 'table-condensed',
24424 cls: 'btn btn-info ok',
24452 * @class Roo.bootstrap.form.MonthField
24453 * @extends Roo.bootstrap.form.Input
24454 * Bootstrap MonthField class
24456 * @cfg {String} language default en
24459 * Create a new MonthField
24460 * @param {Object} config The config object
24463 Roo.bootstrap.form.MonthField = function(config){
24464 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24469 * Fires when this field show.
24470 * @param {Roo.bootstrap.form.MonthField} this
24471 * @param {Mixed} date The date value
24476 * Fires when this field hide.
24477 * @param {Roo.bootstrap.form.MonthField} this
24478 * @param {Mixed} date The date value
24483 * Fires when select a date.
24484 * @param {Roo.bootstrap.form.MonthField} this
24485 * @param {String} oldvalue The old value
24486 * @param {String} newvalue The new value
24492 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24494 onRender: function(ct, position)
24497 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24499 this.language = this.language || 'en';
24500 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24501 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24503 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24504 this.isInline = false;
24505 this.isInput = true;
24506 this.component = this.el.select('.add-on', true).first() || false;
24507 this.component = (this.component && this.component.length === 0) ? false : this.component;
24508 this.hasInput = this.component && this.inputEL().length;
24510 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24512 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24514 this.picker().on('mousedown', this.onMousedown, this);
24515 this.picker().on('click', this.onClick, this);
24517 this.picker().addClass('datepicker-dropdown');
24519 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24520 v.setStyle('width', '189px');
24527 if(this.isInline) {
24533 setValue: function(v, suppressEvent)
24535 var o = this.getValue();
24537 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24541 if(suppressEvent !== true){
24542 this.fireEvent('select', this, o, v);
24547 getValue: function()
24552 onClick: function(e)
24554 e.stopPropagation();
24555 e.preventDefault();
24557 var target = e.getTarget();
24559 if(target.nodeName.toLowerCase() === 'i'){
24560 target = Roo.get(target).dom.parentNode;
24563 var nodeName = target.nodeName;
24564 var className = target.className;
24565 var html = target.innerHTML;
24567 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24571 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24573 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24579 picker : function()
24581 return this.pickerEl;
24584 fillMonths: function()
24587 var months = this.picker().select('>.datepicker-months td', true).first();
24589 months.dom.innerHTML = '';
24595 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24598 months.createChild(month);
24607 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24608 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24611 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24612 e.removeClass('active');
24614 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24615 e.addClass('active');
24622 if(this.isInline) {
24626 this.picker().removeClass(['bottom', 'top']);
24628 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24630 * place to the top of element!
24634 this.picker().addClass('top');
24635 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24640 this.picker().addClass('bottom');
24642 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24645 onFocus : function()
24647 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24651 onBlur : function()
24653 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24655 var d = this.inputEl().getValue();
24664 this.picker().show();
24665 this.picker().select('>.datepicker-months', true).first().show();
24669 this.fireEvent('show', this, this.date);
24674 if(this.isInline) {
24677 this.picker().hide();
24678 this.fireEvent('hide', this, this.date);
24682 onMousedown: function(e)
24684 e.stopPropagation();
24685 e.preventDefault();
24690 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24694 fireKey: function(e)
24696 if (!this.picker().isVisible()){
24697 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24708 e.preventDefault();
24712 dir = e.keyCode == 37 ? -1 : 1;
24714 this.vIndex = this.vIndex + dir;
24716 if(this.vIndex < 0){
24720 if(this.vIndex > 11){
24724 if(isNaN(this.vIndex)){
24728 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734 dir = e.keyCode == 38 ? -1 : 1;
24736 this.vIndex = this.vIndex + dir * 4;
24738 if(this.vIndex < 0){
24742 if(this.vIndex > 11){
24746 if(isNaN(this.vIndex)){
24750 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24755 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24756 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24760 e.preventDefault();
24763 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24764 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24780 this.picker().remove();
24785 Roo.apply(Roo.bootstrap.form.MonthField, {
24804 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24805 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24810 Roo.apply(Roo.bootstrap.form.MonthField, {
24814 cls: 'datepicker dropdown-menu roo-dynamic',
24818 cls: 'datepicker-months',
24822 cls: 'table-condensed',
24824 Roo.bootstrap.form.DateField.content
24844 * @class Roo.bootstrap.form.CheckBox
24845 * @extends Roo.bootstrap.form.Input
24846 * Bootstrap CheckBox class
24848 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24849 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24850 * @cfg {String} boxLabel The text that appears beside the checkbox
24851 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24852 * @cfg {Boolean} checked initnal the element
24853 * @cfg {Boolean} inline inline the element (default false)
24854 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24855 * @cfg {String} tooltip label tooltip
24858 * Create a new CheckBox
24859 * @param {Object} config The config object
24862 Roo.bootstrap.form.CheckBox = function(config){
24863 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24868 * Fires when the element is checked or unchecked.
24869 * @param {Roo.bootstrap.form.CheckBox} this This input
24870 * @param {Boolean} checked The new checked value
24875 * Fires when the element is click.
24876 * @param {Roo.bootstrap.form.CheckBox} this This input
24883 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24885 inputType: 'checkbox',
24894 // checkbox success does not make any sense really..
24899 getAutoCreate : function()
24901 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24907 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24910 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24916 type : this.inputType,
24917 value : this.inputValue,
24918 cls : 'roo-' + this.inputType, //'form-box',
24919 placeholder : this.placeholder || ''
24923 if(this.inputType != 'radio'){
24927 cls : 'roo-hidden-value',
24928 value : this.checked ? this.inputValue : this.valueOff
24933 if (this.weight) { // Validity check?
24934 cfg.cls += " " + this.inputType + "-" + this.weight;
24937 if (this.disabled) {
24938 input.disabled=true;
24942 input.checked = this.checked;
24947 input.name = this.name;
24949 if(this.inputType != 'radio'){
24950 hidden.name = this.name;
24951 input.name = '_hidden_' + this.name;
24956 input.cls += ' input-' + this.size;
24961 ['xs','sm','md','lg'].map(function(size){
24962 if (settings[size]) {
24963 cfg.cls += ' col-' + size + '-' + settings[size];
24967 var inputblock = input;
24969 if (this.before || this.after) {
24972 cls : 'input-group',
24977 inputblock.cn.push({
24979 cls : 'input-group-addon',
24984 inputblock.cn.push(input);
24986 if(this.inputType != 'radio'){
24987 inputblock.cn.push(hidden);
24991 inputblock.cn.push({
24993 cls : 'input-group-addon',
24999 var boxLabelCfg = false;
25005 //'for': id, // box label is handled by onclick - so no for...
25007 html: this.boxLabel
25010 boxLabelCfg.tooltip = this.tooltip;
25016 if (align ==='left' && this.fieldLabel.length) {
25017 // Roo.log("left and has label");
25022 cls : 'control-label',
25023 html : this.fieldLabel
25034 cfg.cn[1].cn.push(boxLabelCfg);
25037 if(this.labelWidth > 12){
25038 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25041 if(this.labelWidth < 13 && this.labelmd == 0){
25042 this.labelmd = this.labelWidth;
25045 if(this.labellg > 0){
25046 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25047 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25050 if(this.labelmd > 0){
25051 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25052 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25055 if(this.labelsm > 0){
25056 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25057 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25060 if(this.labelxs > 0){
25061 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25062 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25065 } else if ( this.fieldLabel.length) {
25066 // Roo.log(" label");
25070 tag: this.boxLabel ? 'span' : 'label',
25072 cls: 'control-label box-input-label',
25073 //cls : 'input-group-addon',
25074 html : this.fieldLabel
25081 cfg.cn.push(boxLabelCfg);
25086 // Roo.log(" no label && no align");
25087 cfg.cn = [ inputblock ] ;
25089 cfg.cn.push(boxLabelCfg);
25097 if(this.inputType != 'radio'){
25098 cfg.cn.push(hidden);
25106 * return the real input element.
25108 inputEl: function ()
25110 return this.el.select('input.roo-' + this.inputType,true).first();
25112 hiddenEl: function ()
25114 return this.el.select('input.roo-hidden-value',true).first();
25117 labelEl: function()
25119 return this.el.select('label.control-label',true).first();
25121 /* depricated... */
25125 return this.labelEl();
25128 boxLabelEl: function()
25130 return this.el.select('label.box-label',true).first();
25133 initEvents : function()
25135 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25137 this.inputEl().on('click', this.onClick, this);
25139 if (this.boxLabel) {
25140 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25143 this.startValue = this.getValue();
25146 Roo.bootstrap.form.CheckBox.register(this);
25150 onClick : function(e)
25152 if(this.fireEvent('click', this, e) !== false){
25153 this.setChecked(!this.checked);
25158 setChecked : function(state,suppressEvent)
25160 this.startValue = this.getValue();
25162 if(this.inputType == 'radio'){
25164 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25165 e.dom.checked = false;
25168 this.inputEl().dom.checked = true;
25170 this.inputEl().dom.value = this.inputValue;
25172 if(suppressEvent !== true){
25173 this.fireEvent('check', this, true);
25181 this.checked = state;
25183 this.inputEl().dom.checked = state;
25186 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25188 if(suppressEvent !== true){
25189 this.fireEvent('check', this, state);
25195 getValue : function()
25197 if(this.inputType == 'radio'){
25198 return this.getGroupValue();
25201 return this.hiddenEl().dom.value;
25205 getGroupValue : function()
25207 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25211 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25214 setValue : function(v,suppressEvent)
25216 if(this.inputType == 'radio'){
25217 this.setGroupValue(v, suppressEvent);
25221 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25226 setGroupValue : function(v, suppressEvent)
25228 this.startValue = this.getValue();
25230 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25231 e.dom.checked = false;
25233 if(e.dom.value == v){
25234 e.dom.checked = true;
25238 if(suppressEvent !== true){
25239 this.fireEvent('check', this, true);
25247 validate : function()
25249 if(this.getVisibilityEl().hasClass('hidden')){
25255 (this.inputType == 'radio' && this.validateRadio()) ||
25256 (this.inputType == 'checkbox' && this.validateCheckbox())
25262 this.markInvalid();
25266 validateRadio : function()
25268 if(this.getVisibilityEl().hasClass('hidden')){
25272 if(this.allowBlank){
25278 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25279 if(!e.dom.checked){
25291 validateCheckbox : function()
25294 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25295 //return (this.getValue() == this.inputValue) ? true : false;
25298 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25306 for(var i in group){
25307 if(group[i].el.isVisible(true)){
25315 for(var i in group){
25320 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327 * Mark this field as valid
25329 markValid : function()
25333 this.fireEvent('valid', this);
25335 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25338 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345 if(this.inputType == 'radio'){
25346 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25347 var fg = e.findParent('.form-group', false, true);
25348 if (Roo.bootstrap.version == 3) {
25349 fg.removeClass([_this.invalidClass, _this.validClass]);
25350 fg.addClass(_this.validClass);
25352 fg.removeClass(['is-valid', 'is-invalid']);
25353 fg.addClass('is-valid');
25361 var fg = this.el.findParent('.form-group', false, true);
25362 if (Roo.bootstrap.version == 3) {
25363 fg.removeClass([this.invalidClass, this.validClass]);
25364 fg.addClass(this.validClass);
25366 fg.removeClass(['is-valid', 'is-invalid']);
25367 fg.addClass('is-valid');
25372 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25378 for(var i in group){
25379 var fg = group[i].el.findParent('.form-group', false, true);
25380 if (Roo.bootstrap.version == 3) {
25381 fg.removeClass([this.invalidClass, this.validClass]);
25382 fg.addClass(this.validClass);
25384 fg.removeClass(['is-valid', 'is-invalid']);
25385 fg.addClass('is-valid');
25391 * Mark this field as invalid
25392 * @param {String} msg The validation message
25394 markInvalid : function(msg)
25396 if(this.allowBlank){
25402 this.fireEvent('invalid', this, msg);
25404 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25407 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25411 label.markInvalid();
25414 if(this.inputType == 'radio'){
25416 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25417 var fg = e.findParent('.form-group', false, true);
25418 if (Roo.bootstrap.version == 3) {
25419 fg.removeClass([_this.invalidClass, _this.validClass]);
25420 fg.addClass(_this.invalidClass);
25422 fg.removeClass(['is-invalid', 'is-valid']);
25423 fg.addClass('is-invalid');
25431 var fg = this.el.findParent('.form-group', false, true);
25432 if (Roo.bootstrap.version == 3) {
25433 fg.removeClass([_this.invalidClass, _this.validClass]);
25434 fg.addClass(_this.invalidClass);
25436 fg.removeClass(['is-invalid', 'is-valid']);
25437 fg.addClass('is-invalid');
25442 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25448 for(var i in group){
25449 var fg = group[i].el.findParent('.form-group', false, true);
25450 if (Roo.bootstrap.version == 3) {
25451 fg.removeClass([_this.invalidClass, _this.validClass]);
25452 fg.addClass(_this.invalidClass);
25454 fg.removeClass(['is-invalid', 'is-valid']);
25455 fg.addClass('is-invalid');
25461 clearInvalid : function()
25463 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25465 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25467 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25469 if (label && label.iconEl) {
25470 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25471 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25475 disable : function()
25477 if(this.inputType != 'radio'){
25478 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25486 _this.getActionEl().addClass(this.disabledClass);
25487 e.dom.disabled = true;
25491 this.disabled = true;
25492 this.fireEvent("disable", this);
25496 enable : function()
25498 if(this.inputType != 'radio'){
25499 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25507 _this.getActionEl().removeClass(this.disabledClass);
25508 e.dom.disabled = false;
25512 this.disabled = false;
25513 this.fireEvent("enable", this);
25517 setBoxLabel : function(v)
25522 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25528 Roo.apply(Roo.bootstrap.form.CheckBox, {
25533 * register a CheckBox Group
25534 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25536 register : function(checkbox)
25538 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25539 this.groups[checkbox.groupId] = {};
25542 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25546 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25550 * fetch a CheckBox Group based on the group ID
25551 * @param {string} the group ID
25552 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25554 get: function(groupId) {
25555 if (typeof(this.groups[groupId]) == 'undefined') {
25559 return this.groups[groupId] ;
25572 * @class Roo.bootstrap.form.Radio
25573 * @extends Roo.bootstrap.Component
25574 * Bootstrap Radio class
25575 * @cfg {String} boxLabel - the label associated
25576 * @cfg {String} value - the value of radio
25579 * Create a new Radio
25580 * @param {Object} config The config object
25582 Roo.bootstrap.form.Radio = function(config){
25583 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25587 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25593 getAutoCreate : function()
25597 cls : 'form-group radio',
25602 html : this.boxLabel
25610 initEvents : function()
25612 this.parent().register(this);
25614 this.el.on('click', this.onClick, this);
25618 onClick : function(e)
25620 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25621 this.setChecked(true);
25625 setChecked : function(state, suppressEvent)
25627 this.parent().setValue(this.value, suppressEvent);
25631 setBoxLabel : function(v)
25636 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25651 * @class Roo.bootstrap.form.SecurePass
25652 * @extends Roo.bootstrap.form.Input
25653 * Bootstrap SecurePass class
25657 * Create a new SecurePass
25658 * @param {Object} config The config object
25661 Roo.bootstrap.form.SecurePass = function (config) {
25662 // these go here, so the translation tool can replace them..
25664 PwdEmpty: "Please type a password, and then retype it to confirm.",
25665 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25666 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25667 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25668 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25669 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25670 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25671 TooWeak: "Your password is Too Weak."
25673 this.meterLabel = "Password strength:";
25674 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25675 this.meterClass = [
25676 "roo-password-meter-tooweak",
25677 "roo-password-meter-weak",
25678 "roo-password-meter-medium",
25679 "roo-password-meter-strong",
25680 "roo-password-meter-grey"
25685 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25688 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25690 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25692 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25693 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25694 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25695 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25696 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25697 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25698 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25708 * @cfg {String/Object} Label for the strength meter (defaults to
25709 * 'Password strength:')
25714 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25715 * ['Weak', 'Medium', 'Strong'])
25718 pwdStrengths: false,
25731 initEvents: function ()
25733 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25735 if (this.el.is('input[type=password]') && Roo.isSafari) {
25736 this.el.on('keydown', this.SafariOnKeyDown, this);
25739 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25742 onRender: function (ct, position)
25744 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25745 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25746 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25748 this.trigger.createChild({
25753 cls: 'roo-password-meter-grey col-xs-12',
25756 //width: this.meterWidth + 'px'
25760 cls: 'roo-password-meter-text'
25766 if (this.hideTrigger) {
25767 this.trigger.setDisplayed(false);
25769 this.setSize(this.width || '', this.height || '');
25772 onDestroy: function ()
25774 if (this.trigger) {
25775 this.trigger.removeAllListeners();
25776 this.trigger.remove();
25779 this.wrap.remove();
25781 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25784 checkStrength: function ()
25786 var pwd = this.inputEl().getValue();
25787 if (pwd == this._lastPwd) {
25792 if (this.ClientSideStrongPassword(pwd)) {
25794 } else if (this.ClientSideMediumPassword(pwd)) {
25796 } else if (this.ClientSideWeakPassword(pwd)) {
25802 Roo.log('strength1: ' + strength);
25804 //var pm = this.trigger.child('div/div/div').dom;
25805 var pm = this.trigger.child('div/div');
25806 pm.removeClass(this.meterClass);
25807 pm.addClass(this.meterClass[strength]);
25810 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25812 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25814 this._lastPwd = pwd;
25818 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25820 this._lastPwd = '';
25822 var pm = this.trigger.child('div/div');
25823 pm.removeClass(this.meterClass);
25824 pm.addClass('roo-password-meter-grey');
25827 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25830 this.inputEl().dom.type='password';
25833 validateValue: function (value)
25835 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25838 if (value.length == 0) {
25839 if (this.allowBlank) {
25840 this.clearInvalid();
25844 this.markInvalid(this.errors.PwdEmpty);
25845 this.errorMsg = this.errors.PwdEmpty;
25853 if (!value.match(/[\x21-\x7e]+/)) {
25854 this.markInvalid(this.errors.PwdBadChar);
25855 this.errorMsg = this.errors.PwdBadChar;
25858 if (value.length < 6) {
25859 this.markInvalid(this.errors.PwdShort);
25860 this.errorMsg = this.errors.PwdShort;
25863 if (value.length > 16) {
25864 this.markInvalid(this.errors.PwdLong);
25865 this.errorMsg = this.errors.PwdLong;
25869 if (this.ClientSideStrongPassword(value)) {
25871 } else if (this.ClientSideMediumPassword(value)) {
25873 } else if (this.ClientSideWeakPassword(value)) {
25880 if (strength < 2) {
25881 //this.markInvalid(this.errors.TooWeak);
25882 this.errorMsg = this.errors.TooWeak;
25887 console.log('strength2: ' + strength);
25889 //var pm = this.trigger.child('div/div/div').dom;
25891 var pm = this.trigger.child('div/div');
25892 pm.removeClass(this.meterClass);
25893 pm.addClass(this.meterClass[strength]);
25895 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25897 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25899 this.errorMsg = '';
25903 CharacterSetChecks: function (type)
25906 this.fResult = false;
25909 isctype: function (character, type)
25912 case this.kCapitalLetter:
25913 if (character >= 'A' && character <= 'Z') {
25918 case this.kSmallLetter:
25919 if (character >= 'a' && character <= 'z') {
25925 if (character >= '0' && character <= '9') {
25930 case this.kPunctuation:
25931 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25942 IsLongEnough: function (pwd, size)
25944 return !(pwd == null || isNaN(size) || pwd.length < size);
25947 SpansEnoughCharacterSets: function (word, nb)
25949 if (!this.IsLongEnough(word, nb))
25954 var characterSetChecks = new Array(
25955 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25956 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25959 for (var index = 0; index < word.length; ++index) {
25960 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25961 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25962 characterSetChecks[nCharSet].fResult = true;
25969 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25970 if (characterSetChecks[nCharSet].fResult) {
25975 if (nCharSets < nb) {
25981 ClientSideStrongPassword: function (pwd)
25983 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25986 ClientSideMediumPassword: function (pwd)
25988 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25991 ClientSideWeakPassword: function (pwd)
25993 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25996 });Roo.rtf = {}; // namespace
25997 Roo.rtf.Hex = function(hex)
26001 Roo.rtf.Paragraph = function(opts)
26003 this.content = []; ///??? is that used?
26004 };Roo.rtf.Span = function(opts)
26006 this.value = opts.value;
26009 Roo.rtf.Group = function(parent)
26011 // we dont want to acutally store parent - it will make debug a nightmare..
26019 Roo.rtf.Group.prototype = {
26023 addContent : function(node) {
26024 // could set styles...
26025 this.content.push(node);
26027 addChild : function(cn)
26031 // only for images really...
26032 toDataURL : function()
26034 var mimetype = false;
26036 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26037 mimetype = "image/png";
26039 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26040 mimetype = "image/jpeg";
26043 return 'about:blank'; // ?? error?
26047 var hexstring = this.content[this.content.length-1].value;
26049 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26050 return String.fromCharCode(parseInt(a, 16));
26055 // this looks like it's normally the {rtf{ .... }}
26056 Roo.rtf.Document = function()
26058 // we dont want to acutally store parent - it will make debug a nightmare..
26064 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26065 addChild : function(cn)
26069 case 'rtlch': // most content seems to be inside this??
26072 this.rtlch.push(cn);
26075 this[cn.type] = cn;
26080 getElementsByType : function(type)
26083 this._getElementsByType(type, ret, this.cn, 'rtf');
26086 _getElementsByType : function (type, ret, search_array, path)
26088 search_array.forEach(function(n,i) {
26089 if (n.type == type) {
26090 n.path = path + '/' + n.type + ':' + i;
26093 if (n.cn.length > 0) {
26094 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26101 Roo.rtf.Ctrl = function(opts)
26103 this.value = opts.value;
26104 this.param = opts.param;
26109 * based on this https://github.com/iarna/rtf-parser
26110 * it's really only designed to extract pict from pasted RTF
26114 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26123 Roo.rtf.Parser = function(text) {
26124 //super({objectMode: true})
26126 this.parserState = this.parseText;
26128 // these are for interpeter...
26130 ///this.parserState = this.parseTop
26131 this.groupStack = [];
26132 this.hexStore = [];
26135 this.groups = []; // where we put the return.
26137 for (var ii = 0; ii < text.length; ++ii) {
26140 if (text[ii] === '\n') {
26146 this.parserState(text[ii]);
26152 Roo.rtf.Parser.prototype = {
26153 text : '', // string being parsed..
26155 controlWordParam : '',
26159 groupStack : false,
26164 row : 1, // reportin?
26168 push : function (el)
26170 var m = 'cmd'+ el.type;
26171 if (typeof(this[m]) == 'undefined') {
26172 Roo.log('invalid cmd:' + el.type);
26178 flushHexStore : function()
26180 if (this.hexStore.length < 1) {
26183 var hexstr = this.hexStore.map(
26188 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26191 this.hexStore.splice(0)
26195 cmdgroupstart : function()
26197 this.flushHexStore();
26199 this.groupStack.push(this.group);
26202 if (this.doc === false) {
26203 this.group = this.doc = new Roo.rtf.Document();
26207 this.group = new Roo.rtf.Group(this.group);
26209 cmdignorable : function()
26211 this.flushHexStore();
26212 this.group.ignorable = true;
26214 cmdendparagraph : function()
26216 this.flushHexStore();
26217 this.group.addContent(new Roo.rtf.Paragraph());
26219 cmdgroupend : function ()
26221 this.flushHexStore();
26222 var endingGroup = this.group;
26225 this.group = this.groupStack.pop();
26227 this.group.addChild(endingGroup);
26232 var doc = this.group || this.doc;
26233 //if (endingGroup instanceof FontTable) {
26234 // doc.fonts = endingGroup.table
26235 //} else if (endingGroup instanceof ColorTable) {
26236 // doc.colors = endingGroup.table
26237 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26238 if (endingGroup.ignorable === false) {
26240 this.groups.push(endingGroup);
26241 // Roo.log( endingGroup );
26243 //Roo.each(endingGroup.content, function(item)) {
26244 // doc.addContent(item);
26246 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26249 cmdtext : function (cmd)
26251 this.flushHexStore();
26252 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26253 //this.group = this.doc
26254 return; // we really don't care about stray text...
26256 this.group.addContent(new Roo.rtf.Span(cmd));
26258 cmdcontrolword : function (cmd)
26260 this.flushHexStore();
26261 if (!this.group.type) {
26262 this.group.type = cmd.value;
26265 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26266 // we actually don't care about ctrl words...
26269 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26270 if (this[method]) {
26271 this[method](cmd.param)
26273 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26277 cmdhexchar : function(cmd) {
26278 this.hexStore.push(cmd);
26280 cmderror : function(cmd) {
26286 if (this.text !== '\u0000') this.emitText()
26292 parseText : function(c)
26295 this.parserState = this.parseEscapes;
26296 } else if (c === '{') {
26297 this.emitStartGroup();
26298 } else if (c === '}') {
26299 this.emitEndGroup();
26300 } else if (c === '\x0A' || c === '\x0D') {
26301 // cr/lf are noise chars
26307 parseEscapes: function (c)
26309 if (c === '\\' || c === '{' || c === '}') {
26311 this.parserState = this.parseText;
26313 this.parserState = this.parseControlSymbol;
26314 this.parseControlSymbol(c);
26317 parseControlSymbol: function(c)
26320 this.text += '\u00a0'; // nbsp
26321 this.parserState = this.parseText
26322 } else if (c === '-') {
26323 this.text += '\u00ad'; // soft hyphen
26324 } else if (c === '_') {
26325 this.text += '\u2011'; // non-breaking hyphen
26326 } else if (c === '*') {
26327 this.emitIgnorable();
26328 this.parserState = this.parseText;
26329 } else if (c === "'") {
26330 this.parserState = this.parseHexChar;
26331 } else if (c === '|') { // formula cacter
26332 this.emitFormula();
26333 this.parserState = this.parseText;
26334 } else if (c === ':') { // subentry in an index entry
26335 this.emitIndexSubEntry();
26336 this.parserState = this.parseText;
26337 } else if (c === '\x0a') {
26338 this.emitEndParagraph();
26339 this.parserState = this.parseText;
26340 } else if (c === '\x0d') {
26341 this.emitEndParagraph();
26342 this.parserState = this.parseText;
26344 this.parserState = this.parseControlWord;
26345 this.parseControlWord(c);
26348 parseHexChar: function (c)
26350 if (/^[A-Fa-f0-9]$/.test(c)) {
26352 if (this.hexChar.length >= 2) {
26353 this.emitHexChar();
26354 this.parserState = this.parseText;
26358 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26359 this.parserState = this.parseText;
26362 parseControlWord : function(c)
26365 this.emitControlWord();
26366 this.parserState = this.parseText;
26367 } else if (/^[-\d]$/.test(c)) {
26368 this.parserState = this.parseControlWordParam;
26369 this.controlWordParam += c;
26370 } else if (/^[A-Za-z]$/.test(c)) {
26371 this.controlWord += c;
26373 this.emitControlWord();
26374 this.parserState = this.parseText;
26378 parseControlWordParam : function (c) {
26379 if (/^\d$/.test(c)) {
26380 this.controlWordParam += c;
26381 } else if (c === ' ') {
26382 this.emitControlWord();
26383 this.parserState = this.parseText;
26385 this.emitControlWord();
26386 this.parserState = this.parseText;
26394 emitText : function () {
26395 if (this.text === '') {
26407 emitControlWord : function ()
26410 if (this.controlWord === '') {
26411 // do we want to track this - it seems just to cause problems.
26412 //this.emitError('empty control word');
26415 type: 'controlword',
26416 value: this.controlWord,
26417 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26423 this.controlWord = '';
26424 this.controlWordParam = '';
26426 emitStartGroup : function ()
26430 type: 'groupstart',
26436 emitEndGroup : function ()
26446 emitIgnorable : function ()
26456 emitHexChar : function ()
26461 value: this.hexChar,
26468 emitError : function (message)
26476 char: this.cpos //,
26477 //stack: new Error().stack
26480 emitEndParagraph : function () {
26483 type: 'endparagraph',
26491 Roo.htmleditor = {};
26494 * @class Roo.htmleditor.Filter
26495 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26496 * @cfg {DomElement} node The node to iterate and filter
26497 * @cfg {boolean|String|Array} tag Tags to replace
26499 * Create a new Filter.
26500 * @param {Object} config Configuration options
26505 Roo.htmleditor.Filter = function(cfg) {
26506 Roo.apply(this.cfg);
26507 // this does not actually call walk as it's really just a abstract class
26511 Roo.htmleditor.Filter.prototype = {
26517 // overrride to do replace comments.
26518 replaceComment : false,
26520 // overrride to do replace or do stuff with tags..
26521 replaceTag : false,
26523 walk : function(dom)
26525 Roo.each( Array.from(dom.childNodes), function( e ) {
26528 case e.nodeType == 8 && this.replaceComment !== false: // comment
26529 this.replaceComment(e);
26532 case e.nodeType != 1: //not a node.
26535 case this.tag === true: // everything
26536 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26537 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26538 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26539 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26540 if (this.replaceTag && false === this.replaceTag(e)) {
26543 if (e.hasChildNodes()) {
26548 default: // tags .. that do not match.
26549 if (e.hasChildNodes()) {
26559 removeNodeKeepChildren : function( node)
26562 ar = Array.from(node.childNodes);
26563 for (var i = 0; i < ar.length; i++) {
26565 node.removeChild(ar[i]);
26566 // what if we need to walk these???
26567 node.parentNode.insertBefore(ar[i], node);
26570 node.parentNode.removeChild(node);
26575 * @class Roo.htmleditor.FilterAttributes
26576 * clean attributes and styles including http:// etc.. in attribute
26578 * Run a new Attribute Filter
26579 * @param {Object} config Configuration options
26581 Roo.htmleditor.FilterAttributes = function(cfg)
26583 Roo.apply(this, cfg);
26584 this.attrib_black = this.attrib_black || [];
26585 this.attrib_white = this.attrib_white || [];
26587 this.attrib_clean = this.attrib_clean || [];
26588 this.style_white = this.style_white || [];
26589 this.style_black = this.style_black || [];
26590 this.walk(cfg.node);
26593 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26595 tag: true, // all tags
26597 attrib_black : false, // array
26598 attrib_clean : false,
26599 attrib_white : false,
26601 style_white : false,
26602 style_black : false,
26605 replaceTag : function(node)
26607 if (!node.attributes || !node.attributes.length) {
26611 for (var i = node.attributes.length-1; i > -1 ; i--) {
26612 var a = node.attributes[i];
26614 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26615 node.removeAttribute(a.name);
26621 if (a.name.toLowerCase().substr(0,2)=='on') {
26622 node.removeAttribute(a.name);
26627 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26628 node.removeAttribute(a.name);
26631 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26632 this.cleanAttr(node,a.name,a.value); // fixme..
26635 if (a.name == 'style') {
26636 this.cleanStyle(node,a.name,a.value);
26639 /// clean up MS crap..
26640 // tecnically this should be a list of valid class'es..
26643 if (a.name == 'class') {
26644 if (a.value.match(/^Mso/)) {
26645 node.removeAttribute('class');
26648 if (a.value.match(/^body$/)) {
26649 node.removeAttribute('class');
26659 return true; // clean children
26662 cleanAttr: function(node, n,v)
26665 if (v.match(/^\./) || v.match(/^\//)) {
26668 if (v.match(/^(http|https):\/\//)
26669 || v.match(/^mailto:/)
26670 || v.match(/^ftp:/)
26671 || v.match(/^data:/)
26675 if (v.match(/^#/)) {
26678 if (v.match(/^\{/)) { // allow template editing.
26681 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26682 node.removeAttribute(n);
26685 cleanStyle : function(node, n,v)
26687 if (v.match(/expression/)) { //XSS?? should we even bother..
26688 node.removeAttribute(n);
26692 var parts = v.split(/;/);
26695 Roo.each(parts, function(p) {
26696 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26700 var l = p.split(':').shift().replace(/\s+/g,'');
26701 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26703 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26707 // only allow 'c whitelisted system attributes'
26708 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26716 if (clean.length) {
26717 node.setAttribute(n, clean.join(';'));
26719 node.removeAttribute(n);
26728 * @class Roo.htmleditor.FilterBlack
26729 * remove blacklisted elements.
26731 * Run a new Blacklisted Filter
26732 * @param {Object} config Configuration options
26735 Roo.htmleditor.FilterBlack = function(cfg)
26737 Roo.apply(this, cfg);
26738 this.walk(cfg.node);
26741 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26743 tag : true, // all elements.
26745 replaceTag : function(n)
26747 n.parentNode.removeChild(n);
26751 * @class Roo.htmleditor.FilterComment
26754 * Run a new Comments Filter
26755 * @param {Object} config Configuration options
26757 Roo.htmleditor.FilterComment = function(cfg)
26759 this.walk(cfg.node);
26762 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26765 replaceComment : function(n)
26767 n.parentNode.removeChild(n);
26770 * @class Roo.htmleditor.FilterKeepChildren
26771 * remove tags but keep children
26773 * Run a new Keep Children Filter
26774 * @param {Object} config Configuration options
26777 Roo.htmleditor.FilterKeepChildren = function(cfg)
26779 Roo.apply(this, cfg);
26780 if (this.tag === false) {
26781 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26784 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26785 this.cleanNamespace = true;
26788 this.walk(cfg.node);
26791 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26793 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26795 replaceTag : function(node)
26797 // walk children...
26798 //Roo.log(node.tagName);
26799 var ar = Array.from(node.childNodes);
26802 for (var i = 0; i < ar.length; i++) {
26804 if (e.nodeType == 1) {
26806 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26807 || // array and it matches
26808 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26810 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26812 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26814 this.replaceTag(ar[i]); // child is blacklisted as well...
26819 ar = Array.from(node.childNodes);
26820 for (var i = 0; i < ar.length; i++) {
26822 node.removeChild(ar[i]);
26823 // what if we need to walk these???
26824 node.parentNode.insertBefore(ar[i], node);
26825 if (this.tag !== false) {
26830 //Roo.log("REMOVE:" + node.tagName);
26831 node.parentNode.removeChild(node);
26832 return false; // don't walk children
26837 * @class Roo.htmleditor.FilterParagraph
26838 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26839 * like on 'push' to remove the <p> tags and replace them with line breaks.
26841 * Run a new Paragraph Filter
26842 * @param {Object} config Configuration options
26845 Roo.htmleditor.FilterParagraph = function(cfg)
26847 // no need to apply config.
26848 this.walk(cfg.node);
26851 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26858 replaceTag : function(node)
26861 if (node.childNodes.length == 1 &&
26862 node.childNodes[0].nodeType == 3 &&
26863 node.childNodes[0].textContent.trim().length < 1
26865 // remove and replace with '<BR>';
26866 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26867 return false; // no need to walk..
26869 var ar = Array.from(node.childNodes);
26870 for (var i = 0; i < ar.length; i++) {
26871 node.removeChild(ar[i]);
26872 // what if we need to walk these???
26873 node.parentNode.insertBefore(ar[i], node);
26875 // now what about this?
26879 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26880 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26881 node.parentNode.removeChild(node);
26888 * @class Roo.htmleditor.FilterSpan
26889 * filter span's with no attributes out..
26891 * Run a new Span Filter
26892 * @param {Object} config Configuration options
26895 Roo.htmleditor.FilterSpan = function(cfg)
26897 // no need to apply config.
26898 this.walk(cfg.node);
26901 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26907 replaceTag : function(node)
26909 if (node.attributes && node.attributes.length > 0) {
26910 return true; // walk if there are any.
26912 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26918 * @class Roo.htmleditor.FilterTableWidth
26919 try and remove table width data - as that frequently messes up other stuff.
26921 * was cleanTableWidths.
26923 * Quite often pasting from word etc.. results in tables with column and widths.
26924 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26927 * Run a new Table Filter
26928 * @param {Object} config Configuration options
26931 Roo.htmleditor.FilterTableWidth = function(cfg)
26933 // no need to apply config.
26934 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26935 this.walk(cfg.node);
26938 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26943 replaceTag: function(node) {
26947 if (node.hasAttribute('width')) {
26948 node.removeAttribute('width');
26952 if (node.hasAttribute("style")) {
26955 var styles = node.getAttribute("style").split(";");
26957 Roo.each(styles, function(s) {
26958 if (!s.match(/:/)) {
26961 var kv = s.split(":");
26962 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26965 // what ever is left... we allow.
26968 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26969 if (!nstyle.length) {
26970 node.removeAttribute('style');
26974 return true; // continue doing children..
26977 * @class Roo.htmleditor.FilterWord
26978 * try and clean up all the mess that Word generates.
26980 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26983 * Run a new Span Filter
26984 * @param {Object} config Configuration options
26987 Roo.htmleditor.FilterWord = function(cfg)
26989 // no need to apply config.
26990 this.replaceDocBullets(cfg.node);
26992 this.replaceAname(cfg.node);
26993 // this is disabled as the removal is done by other filters;
26994 // this.walk(cfg.node);
26999 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27005 * Clean up MS wordisms...
27007 replaceTag : function(node)
27010 // no idea what this does - span with text, replaceds with just text.
27012 node.nodeName == 'SPAN' &&
27013 !node.hasAttributes() &&
27014 node.childNodes.length == 1 &&
27015 node.firstChild.nodeName == "#text"
27017 var textNode = node.firstChild;
27018 node.removeChild(textNode);
27019 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27020 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27022 node.parentNode.insertBefore(textNode, node);
27023 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27024 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27027 node.parentNode.removeChild(node);
27028 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27033 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27034 node.parentNode.removeChild(node);
27035 return false; // dont do chidlren
27037 //Roo.log(node.tagName);
27038 // remove - but keep children..
27039 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27040 //Roo.log('-- removed');
27041 while (node.childNodes.length) {
27042 var cn = node.childNodes[0];
27043 node.removeChild(cn);
27044 node.parentNode.insertBefore(cn, node);
27045 // move node to parent - and clean it..
27046 if (cn.nodeType == 1) {
27047 this.replaceTag(cn);
27051 node.parentNode.removeChild(node);
27052 /// no need to iterate chidlren = it's got none..
27053 //this.iterateChildren(node, this.cleanWord);
27054 return false; // no need to iterate children.
27057 if (node.className.length) {
27059 var cn = node.className.split(/\W+/);
27061 Roo.each(cn, function(cls) {
27062 if (cls.match(/Mso[a-zA-Z]+/)) {
27067 node.className = cna.length ? cna.join(' ') : '';
27069 node.removeAttribute("class");
27073 if (node.hasAttribute("lang")) {
27074 node.removeAttribute("lang");
27077 if (node.hasAttribute("style")) {
27079 var styles = node.getAttribute("style").split(";");
27081 Roo.each(styles, function(s) {
27082 if (!s.match(/:/)) {
27085 var kv = s.split(":");
27086 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27089 // what ever is left... we allow.
27092 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27093 if (!nstyle.length) {
27094 node.removeAttribute('style');
27097 return true; // do children
27103 styleToObject: function(node)
27105 var styles = (node.getAttribute("style") || '').split(";");
27107 Roo.each(styles, function(s) {
27108 if (!s.match(/:/)) {
27111 var kv = s.split(":");
27113 // what ever is left... we allow.
27114 ret[kv[0].trim()] = kv[1];
27120 replaceAname : function (doc)
27122 // replace all the a/name without..
27123 var aa = Array.from(doc.getElementsByTagName('a'));
27124 for (var i = 0; i < aa.length; i++) {
27126 if (a.hasAttribute("name")) {
27127 a.removeAttribute("name");
27129 if (a.hasAttribute("href")) {
27132 // reparent children.
27133 this.removeNodeKeepChildren(a);
27143 replaceDocBullets : function(doc)
27145 // this is a bit odd - but it appears some indents use ql-indent-1
27146 //Roo.log(doc.innerHTML);
27148 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27149 for( var i = 0; i < listpara.length; i ++) {
27150 listpara[i].className = "MsoListParagraph";
27153 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27154 for( var i = 0; i < listpara.length; i ++) {
27155 listpara[i].className = "MsoListParagraph";
27157 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27158 for( var i = 0; i < listpara.length; i ++) {
27159 listpara[i].className = "MsoListParagraph";
27161 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27162 for( var i = 0; i < listpara.length; i ++) {
27163 listpara[i].className = "MsoListParagraph";
27166 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27167 var htwo = Array.from(doc.getElementsByTagName('h2'));
27168 for( var i = 0; i < htwo.length; i ++) {
27169 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27170 htwo[i].className = "MsoListParagraph";
27173 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27174 for( var i = 0; i < listpara.length; i ++) {
27175 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27176 listpara[i].className = "MsoListParagraph";
27178 listpara[i].className = "MsoNormalx";
27182 listpara = doc.getElementsByClassName('MsoListParagraph');
27183 // Roo.log(doc.innerHTML);
27187 while(listpara.length) {
27189 this.replaceDocBullet(listpara.item(0));
27196 replaceDocBullet : function(p)
27198 // gather all the siblings.
27200 parent = p.parentNode,
27201 doc = parent.ownerDocument,
27204 var listtype = 'ul';
27206 if (ns.nodeType != 1) {
27207 ns = ns.nextSibling;
27210 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27213 var spans = ns.getElementsByTagName('span');
27214 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27216 ns = ns.nextSibling;
27218 if (spans.length && spans[0].hasAttribute('style')) {
27219 var style = this.styleToObject(spans[0]);
27220 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
27227 var spans = ns.getElementsByTagName('span');
27228 if (!spans.length) {
27231 var has_list = false;
27232 for(var i = 0; i < spans.length; i++) {
27233 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27242 ns = ns.nextSibling;
27246 if (!items.length) {
27251 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27252 parent.insertBefore(ul, p);
27254 var stack = [ ul ];
27255 var last_li = false;
27257 var margin_to_depth = {};
27260 items.forEach(function(n, ipos) {
27261 //Roo.log("got innertHMLT=" + n.innerHTML);
27263 var spans = n.getElementsByTagName('span');
27264 if (!spans.length) {
27265 //Roo.log("No spans found");
27267 parent.removeChild(n);
27270 return; // skip it...
27276 for(var i = 0; i < spans.length; i++) {
27278 style = this.styleToObject(spans[i]);
27279 if (typeof(style['mso-list']) == 'undefined') {
27282 if (listtype == 'ol') {
27283 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27285 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27288 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27289 style = this.styleToObject(n); // mo-list is from the parent node.
27290 if (typeof(style['mso-list']) == 'undefined') {
27291 //Roo.log("parent is missing level");
27293 parent.removeChild(n);
27298 var margin = style['margin-left'];
27299 if (typeof(margin_to_depth[margin]) == 'undefined') {
27301 margin_to_depth[margin] = max_margins;
27303 nlvl = margin_to_depth[margin] ;
27307 var nul = doc.createElement(listtype); // what about number lists...
27309 last_li = doc.createElement('li');
27310 stack[lvl].appendChild(last_li);
27312 last_li.appendChild(nul);
27318 // not starting at 1..
27319 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27320 stack[nlvl].setAttribute("start", num);
27323 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27325 nli.innerHTML = n.innerHTML;
27326 //Roo.log("innerHTML = " + n.innerHTML);
27327 parent.removeChild(n);
27343 * @class Roo.htmleditor.FilterStyleToTag
27344 * part of the word stuff... - certain 'styles' should be converted to tags.
27346 * font-weight: bold -> bold
27347 * ?? super / subscrit etc..
27350 * Run a new style to tag filter.
27351 * @param {Object} config Configuration options
27353 Roo.htmleditor.FilterStyleToTag = function(cfg)
27357 B : [ 'fontWeight' , 'bold'],
27358 I : [ 'fontStyle' , 'italic'],
27359 //pre : [ 'font-style' , 'italic'],
27360 // h1.. h6 ?? font-size?
27361 SUP : [ 'verticalAlign' , 'super' ],
27362 SUB : [ 'verticalAlign' , 'sub' ]
27367 Roo.apply(this, cfg);
27370 this.walk(cfg.node);
27377 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27379 tag: true, // all tags
27384 replaceTag : function(node)
27388 if (node.getAttribute("style") === null) {
27392 for (var k in this.tags) {
27393 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27395 node.style.removeProperty(this.tags[k][0]);
27398 if (!inject.length) {
27401 var cn = Array.from(node.childNodes);
27403 Roo.each(inject, function(t) {
27404 var nc = node.ownerDocument.createElement(t);
27405 nn.appendChild(nc);
27408 for(var i = 0;i < cn.length;cn++) {
27409 node.removeChild(cn[i]);
27410 nn.appendChild(cn[i]);
27412 return true /// iterate thru
27416 * @class Roo.htmleditor.FilterLongBr
27417 * BR/BR/BR - keep a maximum of 2...
27419 * Run a new Long BR Filter
27420 * @param {Object} config Configuration options
27423 Roo.htmleditor.FilterLongBr = function(cfg)
27425 // no need to apply config.
27426 this.walk(cfg.node);
27429 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27436 replaceTag : function(node)
27439 var ps = node.nextSibling;
27440 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27441 ps = ps.nextSibling;
27444 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27445 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27449 if (!ps || ps.nodeType != 1) {
27453 if (!ps || ps.tagName != 'BR') {
27462 if (!node.previousSibling) {
27465 var ps = node.previousSibling;
27467 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27468 ps = ps.previousSibling;
27470 if (!ps || ps.nodeType != 1) {
27473 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27474 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27478 node.parentNode.removeChild(node); // remove me...
27480 return false; // no need to do children
27487 * @class Roo.htmleditor.FilterBlock
27488 * removes id / data-block and contenteditable that are associated with blocks
27489 * usage should be done on a cloned copy of the dom
27491 * Run a new Attribute Filter { node : xxxx }}
27492 * @param {Object} config Configuration options
27494 Roo.htmleditor.FilterBlock = function(cfg)
27496 Roo.apply(this, cfg);
27497 var qa = cfg.node.querySelectorAll;
27498 this.removeAttributes('data-block');
27499 this.removeAttributes('contenteditable');
27500 this.removeAttributes('id');
27504 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27506 node: true, // all tags
27509 removeAttributes : function(attr)
27511 var ar = this.node.querySelectorAll('*[' + attr + ']');
27512 for (var i =0;i<ar.length;i++) {
27513 ar[i].removeAttribute(attr);
27522 * This is based loosely on tinymce
27523 * @class Roo.htmleditor.TidySerializer
27524 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27526 * @method Serializer
27527 * @param {Object} settings Name/value settings object.
27531 Roo.htmleditor.TidySerializer = function(settings)
27533 Roo.apply(this, settings);
27535 this.writer = new Roo.htmleditor.TidyWriter(settings);
27540 Roo.htmleditor.TidySerializer.prototype = {
27543 * @param {boolean} inner do the inner of the node.
27550 * Serializes the specified node into a string.
27553 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27554 * @method serialize
27555 * @param {DomElement} node Node instance to serialize.
27556 * @return {String} String with HTML based on DOM tree.
27558 serialize : function(node) {
27560 // = settings.validate;
27561 var writer = this.writer;
27565 3: function(node) {
27567 writer.text(node.nodeValue, node);
27570 8: function(node) {
27571 writer.comment(node.nodeValue);
27573 // Processing instruction
27574 7: function(node) {
27575 writer.pi(node.name, node.nodeValue);
27578 10: function(node) {
27579 writer.doctype(node.nodeValue);
27582 4: function(node) {
27583 writer.cdata(node.nodeValue);
27585 // Document fragment
27586 11: function(node) {
27587 node = node.firstChild;
27593 node = node.nextSibling
27598 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27599 return writer.getContent();
27602 walk: function(node)
27604 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27605 handler = this.handlers[node.nodeType];
27612 var name = node.nodeName;
27613 var isEmpty = node.childNodes.length < 1;
27615 var writer = this.writer;
27616 var attrs = node.attributes;
27619 writer.start(node.nodeName, attrs, isEmpty, node);
27623 node = node.firstChild;
27630 node = node.nextSibling;
27636 // Serialize element and treat all non elements as fragments
27641 * This is based loosely on tinymce
27642 * @class Roo.htmleditor.TidyWriter
27643 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27646 * - not tested much with 'PRE' formated elements.
27652 Roo.htmleditor.TidyWriter = function(settings)
27655 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27656 Roo.apply(this, settings);
27660 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27663 Roo.htmleditor.TidyWriter.prototype = {
27670 // part of state...
27674 last_inline : false,
27679 * Writes the a start element such as <p id="a">.
27682 * @param {String} name Name of the element.
27683 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27684 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27686 start: function(name, attrs, empty, node)
27688 var i, l, attr, value;
27690 // there are some situations where adding line break && indentation will not work. will not work.
27691 // <span / b / i ... formating?
27693 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27694 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27696 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27698 var add_lb = name == 'BR' ? false : in_inline;
27700 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27704 var indentstr = this.indentstr;
27706 // e_inline = elements that can be inline, but still allow \n before and after?
27707 // only 'BR' ??? any others?
27709 // ADD LINE BEFORE tage
27710 if (!this.in_pre) {
27713 if (name == 'BR') {
27715 } else if (this.lastElementEndsWS()) {
27718 // otherwise - no new line. (and dont indent.)
27729 this.html.push(indentstr + '<', name.toLowerCase());
27732 for (i = 0, l = attrs.length; i < l; i++) {
27734 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27740 this.html[this.html.length] = '/>';
27742 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27744 var e_inline = name == 'BR' ? false : this.in_inline;
27746 if (!e_inline && !this.in_pre) {
27753 this.html[this.html.length] = '>';
27755 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27757 if (!in_inline && !in_pre) {
27758 var cn = node.firstChild;
27760 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27764 cn = cn.nextSibling;
27772 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27774 in_inline : in_inline
27776 // add a line after if we are not in a
27778 if (!in_inline && !in_pre) {
27787 lastElementEndsWS : function()
27789 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27790 if (value === false) {
27793 return value.match(/\s+$/);
27798 * Writes the a end element such as </p>.
27801 * @param {String} name Name of the element.
27803 end: function(name) {
27806 var indentstr = '';
27807 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27809 if (!this.in_pre && !in_inline) {
27811 indentstr = this.indentstr;
27813 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27814 this.last_inline = in_inline;
27816 // pop the indent state..
27819 * Writes a text node.
27821 * In pre - we should not mess with the contents.
27825 * @param {String} text String to write out.
27826 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27828 text: function(in_text, node)
27830 // if not in whitespace critical
27831 if (in_text.length < 1) {
27834 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27837 this.html[this.html.length] = text;
27841 if (this.in_inline) {
27842 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27844 text = text.replace(/\s+/,' '); // all white space to single white space
27847 // if next tag is '<BR>', then we can trim right..
27848 if (node.nextSibling &&
27849 node.nextSibling.nodeType == 1 &&
27850 node.nextSibling.nodeName == 'BR' )
27852 text = text.replace(/\s+$/g,'');
27854 // if previous tag was a BR, we can also trim..
27855 if (node.previousSibling &&
27856 node.previousSibling.nodeType == 1 &&
27857 node.previousSibling.nodeName == 'BR' )
27859 text = this.indentstr + text.replace(/^\s+/g,'');
27861 if (text.match(/\n/)) {
27862 text = text.replace(
27863 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27865 // remoeve the last whitespace / line break.
27866 text = text.replace(/\n\s+$/,'');
27868 // repace long lines
27872 this.html[this.html.length] = text;
27875 // see if previous element was a inline element.
27876 var indentstr = this.indentstr;
27878 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27880 // should trim left?
27881 if (node.previousSibling &&
27882 node.previousSibling.nodeType == 1 &&
27883 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27889 text = text.replace(/^\s+/,''); // trim left
27892 // should trim right?
27893 if (node.nextSibling &&
27894 node.nextSibling.nodeType == 1 &&
27895 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27900 text = text.replace(/\s+$/,''); // trim right
27907 if (text.length < 1) {
27910 if (!text.match(/\n/)) {
27911 this.html.push(indentstr + text);
27915 text = this.indentstr + text.replace(
27916 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27918 // remoeve the last whitespace / line break.
27919 text = text.replace(/\s+$/,'');
27921 this.html.push(text);
27923 // split and indent..
27928 * Writes a cdata node such as <![CDATA[data]]>.
27931 * @param {String} text String to write out inside the cdata.
27933 cdata: function(text) {
27934 this.html.push('<![CDATA[', text, ']]>');
27937 * Writes a comment node such as <!-- Comment -->.
27940 * @param {String} text String to write out inside the comment.
27942 comment: function(text) {
27943 this.html.push('<!--', text, '-->');
27946 * Writes a PI node such as <?xml attr="value" ?>.
27949 * @param {String} name Name of the pi.
27950 * @param {String} text String to write out inside the pi.
27952 pi: function(name, text) {
27953 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27954 this.indent != '' && this.html.push('\n');
27957 * Writes a doctype node such as <!DOCTYPE data>.
27960 * @param {String} text String to write out inside the doctype.
27962 doctype: function(text) {
27963 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27966 * Resets the internal buffer if one wants to reuse the writer.
27970 reset: function() {
27971 this.html.length = 0;
27980 * Returns the contents that got serialized.
27982 * @method getContent
27983 * @return {String} HTML contents that got written down.
27985 getContent: function() {
27986 return this.html.join('').replace(/\n$/, '');
27989 pushState : function(cfg)
27991 this.state.push(cfg);
27992 Roo.apply(this, cfg);
27995 popState : function()
27997 if (this.state.length < 1) {
27998 return; // nothing to push
28005 if (this.state.length > 0) {
28006 cfg = this.state[this.state.length-1];
28008 Roo.apply(this, cfg);
28011 addLine: function()
28013 if (this.html.length < 1) {
28018 var value = this.html[this.html.length - 1];
28019 if (value.length > 0 && '\n' !== value) {
28020 this.html.push('\n');
28025 //'pre script noscript style textarea video audio iframe object code'
28026 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28030 Roo.htmleditor.TidyWriter.inline_elements = [
28031 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28032 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28034 Roo.htmleditor.TidyWriter.shortend_elements = [
28035 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28036 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28039 Roo.htmleditor.TidyWriter.whitespace_elements = [
28040 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28042 * This is based loosely on tinymce
28043 * @class Roo.htmleditor.TidyEntities
28045 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28047 * Not 100% sure this is actually used or needed.
28050 Roo.htmleditor.TidyEntities = {
28053 * initialize data..
28055 init : function (){
28057 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28062 buildEntitiesLookup: function(items, radix) {
28063 var i, chr, entity, lookup = {};
28067 items = typeof(items) == 'string' ? items.split(',') : items;
28068 radix = radix || 10;
28069 // Build entities lookup table
28070 for (i = 0; i < items.length; i += 2) {
28071 chr = String.fromCharCode(parseInt(items[i], radix));
28072 // Only add non base entities
28073 if (!this.baseEntities[chr]) {
28074 entity = '&' + items[i + 1] + ';';
28075 lookup[chr] = entity;
28076 lookup[entity] = chr;
28115 // Needs to be escaped since the YUI compressor would otherwise break the code
28122 // Reverse lookup table for raw entities
28123 reverseEntities : {
28131 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28132 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28133 rawCharsRegExp : /[<>&\"\']/g,
28134 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28135 namedEntities : false,
28136 namedEntitiesData : [
28637 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28639 * @method encodeRaw
28640 * @param {String} text Text to encode.
28641 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28642 * @return {String} Entity encoded text.
28644 encodeRaw: function(text, attr)
28647 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28648 return t.baseEntities[chr] || chr;
28652 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28653 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28654 * and is exposed as the DOMUtils.encode function.
28656 * @method encodeAllRaw
28657 * @param {String} text Text to encode.
28658 * @return {String} Entity encoded text.
28660 encodeAllRaw: function(text) {
28662 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28663 return t.baseEntities[chr] || chr;
28667 * Encodes the specified string using numeric entities. The core entities will be
28668 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28670 * @method encodeNumeric
28671 * @param {String} text Text to encode.
28672 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28673 * @return {String} Entity encoded text.
28675 encodeNumeric: function(text, attr) {
28677 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28678 // Multi byte sequence convert it to a single entity
28679 if (chr.length > 1) {
28680 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28682 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28686 * Encodes the specified string using named entities. The core entities will be encoded
28687 * as named ones but all non lower ascii characters will be encoded into named entities.
28689 * @method encodeNamed
28690 * @param {String} text Text to encode.
28691 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28692 * @param {Object} entities Optional parameter with entities to use.
28693 * @return {String} Entity encoded text.
28695 encodeNamed: function(text, attr, entities) {
28697 entities = entities || this.namedEntities;
28698 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28699 return t.baseEntities[chr] || entities[chr] || chr;
28703 * Returns an encode function based on the name(s) and it's optional entities.
28705 * @method getEncodeFunc
28706 * @param {String} name Comma separated list of encoders for example named,numeric.
28707 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28708 * @return {function} Encode function to be used.
28710 getEncodeFunc: function(name, entities) {
28711 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28713 function encodeNamedAndNumeric(text, attr) {
28714 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28715 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28719 function encodeCustomNamed(text, attr) {
28720 return t.encodeNamed(text, attr, entities);
28722 // Replace + with , to be compatible with previous TinyMCE versions
28723 name = this.makeMap(name.replace(/\+/g, ','));
28724 // Named and numeric encoder
28725 if (name.named && name.numeric) {
28726 return this.encodeNamedAndNumeric;
28732 return encodeCustomNamed;
28734 return this.encodeNamed;
28737 if (name.numeric) {
28738 return this.encodeNumeric;
28741 return this.encodeRaw;
28744 * Decodes the specified string, this will replace entities with raw UTF characters.
28747 * @param {String} text Text to entity decode.
28748 * @return {String} Entity decoded string.
28750 decode: function(text)
28753 return text.replace(this.entityRegExp, function(all, numeric) {
28755 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28756 // Support upper UTF
28757 if (numeric > 65535) {
28759 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28761 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28763 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28766 nativeDecode : function (text) {
28769 makeMap : function (items, delim, map) {
28771 items = items || [];
28772 delim = delim || ',';
28773 if (typeof items == "string") {
28774 items = items.split(delim);
28779 map[items[i]] = {};
28787 Roo.htmleditor.TidyEntities.init();
28789 * @class Roo.htmleditor.KeyEnter
28790 * Handle Enter press..
28791 * @cfg {Roo.HtmlEditorCore} core the editor.
28793 * Create a new Filter.
28794 * @param {Object} config Configuration options
28801 Roo.htmleditor.KeyEnter = function(cfg) {
28802 Roo.apply(this, cfg);
28803 // this does not actually call walk as it's really just a abstract class
28805 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28808 //Roo.htmleditor.KeyEnter.i = 0;
28811 Roo.htmleditor.KeyEnter.prototype = {
28815 keypress : function(e)
28817 if (e.charCode != 13 && e.charCode != 10) {
28818 Roo.log([e.charCode,e]);
28821 e.preventDefault();
28822 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28823 var doc = this.core.doc;
28827 var sel = this.core.getSelection();
28828 var range = sel.getRangeAt(0);
28829 var n = range.commonAncestorContainer;
28830 var pc = range.closest([ 'ol', 'ul']);
28831 var pli = range.closest('li');
28832 if (!pc || e.ctrlKey) {
28833 // on it list, or ctrl pressed.
28835 sel.insertNode('br', 'after');
28837 // only do this if we have ctrl key..
28838 var br = doc.createElement('br');
28839 br.className = 'clear';
28840 br.setAttribute('style', 'clear: both');
28841 sel.insertNode(br, 'after');
28845 this.core.undoManager.addEvent();
28846 this.core.fireEditorEvent(e);
28850 // deal with <li> insetion
28851 if (pli.innerText.trim() == '' &&
28852 pli.previousSibling &&
28853 pli.previousSibling.nodeName == 'LI' &&
28854 pli.previousSibling.innerText.trim() == '') {
28855 pli.parentNode.removeChild(pli.previousSibling);
28856 sel.cursorAfter(pc);
28857 this.core.undoManager.addEvent();
28858 this.core.fireEditorEvent(e);
28862 var li = doc.createElement('LI');
28863 li.innerHTML = ' ';
28864 if (!pli || !pli.firstSibling) {
28865 pc.appendChild(li);
28867 pli.parentNode.insertBefore(li, pli.firstSibling);
28869 sel.cursorText (li.firstChild);
28871 this.core.undoManager.addEvent();
28872 this.core.fireEditorEvent(e);
28884 * @class Roo.htmleditor.Block
28885 * Base class for html editor blocks - do not use it directly .. extend it..
28886 * @cfg {DomElement} node The node to apply stuff to.
28887 * @cfg {String} friendly_name the name that appears in the context bar about this block
28888 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28891 * Create a new Filter.
28892 * @param {Object} config Configuration options
28895 Roo.htmleditor.Block = function(cfg)
28897 // do nothing .. should not be called really.
28900 * factory method to get the block from an element (using cache if necessary)
28902 * @param {HtmlElement} the dom element
28904 Roo.htmleditor.Block.factory = function(node)
28906 var cc = Roo.htmleditor.Block.cache;
28907 var id = Roo.get(node).id;
28908 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28909 Roo.htmleditor.Block.cache[id].readElement(node);
28910 return Roo.htmleditor.Block.cache[id];
28912 var db = node.getAttribute('data-block');
28914 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28916 var cls = Roo.htmleditor['Block' + db];
28917 if (typeof(cls) == 'undefined') {
28918 //Roo.log(node.getAttribute('data-block'));
28919 Roo.log("OOps missing block : " + 'Block' + db);
28922 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28923 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
28927 * initalize all Elements from content that are 'blockable'
28929 * @param the body element
28931 Roo.htmleditor.Block.initAll = function(body, type)
28933 if (typeof(type) == 'undefined') {
28934 var ia = Roo.htmleditor.Block.initAll;
28940 Roo.each(Roo.get(body).query(type), function(e) {
28941 Roo.htmleditor.Block.factory(e);
28944 // question goes here... do we need to clear out this cache sometimes?
28945 // or show we make it relivant to the htmleditor.
28946 Roo.htmleditor.Block.cache = {};
28948 Roo.htmleditor.Block.prototype = {
28952 // used by context menu
28953 friendly_name : 'Based Block',
28955 // text for button to delete this element
28956 deleteTitle : false,
28960 * Update a node with values from this object
28961 * @param {DomElement} node
28963 updateElement : function(node)
28965 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28968 * convert to plain HTML for calling insertAtCursor..
28970 toHTML : function()
28972 return Roo.DomHelper.markup(this.toObject());
28975 * used by readEleemnt to extract data from a node
28976 * may need improving as it's pretty basic
28978 * @param {DomElement} node
28979 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28980 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28981 * @param {String} style the style property - eg. text-align
28983 getVal : function(node, tag, attr, style)
28986 if (tag !== true && n.tagName != tag.toUpperCase()) {
28987 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28988 // but kiss for now.
28989 n = node.getElementsByTagName(tag).item(0);
28994 if (attr === false) {
28997 if (attr == 'html') {
28998 return n.innerHTML;
29000 if (attr == 'style') {
29001 return n.style[style];
29004 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29008 * create a DomHelper friendly object - for use with
29009 * Roo.DomHelper.markup / overwrite / etc..
29012 toObject : function()
29017 * Read a node that has a 'data-block' property - and extract the values from it.
29018 * @param {DomElement} node - the node
29020 readElement : function(node)
29031 * @class Roo.htmleditor.BlockFigure
29032 * Block that has an image and a figcaption
29033 * @cfg {String} image_src the url for the image
29034 * @cfg {String} align (left|right) alignment for the block default left
29035 * @cfg {String} caption the text to appear below (and in the alt tag)
29036 * @cfg {String} caption_display (block|none) display or not the caption
29037 * @cfg {String|number} image_width the width of the image number or %?
29038 * @cfg {String|number} image_height the height of the image number or %?
29041 * Create a new Filter.
29042 * @param {Object} config Configuration options
29045 Roo.htmleditor.BlockFigure = function(cfg)
29048 this.readElement(cfg.node);
29049 this.updateElement(cfg.node);
29051 Roo.apply(this, cfg);
29053 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29060 caption_display : 'block',
29066 // margin: '2%', not used
29068 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29071 // used by context menu
29072 friendly_name : 'Image with caption',
29073 deleteTitle : "Delete Image and Caption",
29075 contextMenu : function(toolbar)
29078 var block = function() {
29079 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29083 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29085 var syncValue = toolbar.editorcore.syncValue;
29091 xtype : 'TextItem',
29093 xns : rooui.Toolbar //Boostrap?
29097 text: 'Change Image URL',
29100 click: function (btn, state)
29104 Roo.MessageBox.show({
29105 title : "Image Source URL",
29106 msg : "Enter the url for the image",
29107 buttons: Roo.MessageBox.OKCANCEL,
29108 fn: function(btn, val){
29115 toolbar.editorcore.onEditorEvent();
29119 //multiline: multiline,
29121 value : b.image_src
29125 xns : rooui.Toolbar
29130 text: 'Change Link URL',
29133 click: function (btn, state)
29137 Roo.MessageBox.show({
29138 title : "Link URL",
29139 msg : "Enter the url for the link - leave blank to have no link",
29140 buttons: Roo.MessageBox.OKCANCEL,
29141 fn: function(btn, val){
29148 toolbar.editorcore.onEditorEvent();
29152 //multiline: multiline,
29158 xns : rooui.Toolbar
29162 text: 'Show Video URL',
29165 click: function (btn, state)
29167 Roo.MessageBox.alert("Video URL",
29168 block().video_url == '' ? 'This image is not linked ot a video' :
29169 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29172 xns : rooui.Toolbar
29177 xtype : 'TextItem',
29179 xns : rooui.Toolbar //Boostrap?
29182 xtype : 'ComboBox',
29183 allowBlank : false,
29184 displayField : 'val',
29187 triggerAction : 'all',
29189 valueField : 'val',
29193 select : function (combo, r, index)
29195 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29197 b.width = r.get('val');
29200 toolbar.editorcore.onEditorEvent();
29205 xtype : 'SimpleStore',
29218 xtype : 'TextItem',
29220 xns : rooui.Toolbar //Boostrap?
29223 xtype : 'ComboBox',
29224 allowBlank : false,
29225 displayField : 'val',
29228 triggerAction : 'all',
29230 valueField : 'val',
29234 select : function (combo, r, index)
29236 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29238 b.align = r.get('val');
29241 toolbar.editorcore.onEditorEvent();
29246 xtype : 'SimpleStore',
29260 text: 'Hide Caption',
29261 name : 'caption_display',
29263 enableToggle : true,
29264 setValue : function(v) {
29265 // this trigger toggle.
29267 this.setText(v ? "Hide Caption" : "Show Caption");
29268 this.setPressed(v != 'block');
29271 toggle: function (btn, state)
29274 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29275 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29278 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29279 toolbar.editorcore.onEditorEvent();
29282 xns : rooui.Toolbar
29288 * create a DomHelper friendly object - for use with
29289 * Roo.DomHelper.markup / overwrite / etc..
29291 toObject : function()
29293 var d = document.createElement('div');
29294 d.innerHTML = this.caption;
29296 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29298 var iw = this.align == 'center' ? this.width : '100%';
29301 contenteditable : 'false',
29302 src : this.image_src,
29303 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29306 maxWidth : iw + ' !important', // this is not getting rendered?
29312 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29314 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29319 if (this.href.length > 0) {
29323 contenteditable : 'true',
29331 if (this.video_url.length > 0) {
29336 allowfullscreen : true,
29337 width : 420, // these are for video tricks - that we replace the outer
29339 src : this.video_url,
29345 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29346 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29351 'data-block' : 'Figure',
29352 'data-width' : this.width,
29353 contenteditable : 'false',
29357 float : this.align ,
29358 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29359 width : this.align == 'center' ? '100%' : this.width,
29361 padding: this.align == 'center' ? '0' : '0 10px' ,
29362 textAlign : this.align // seems to work for email..
29367 align : this.align,
29373 'data-display' : this.caption_display,
29375 textAlign : 'left',
29377 lineHeight : '24px',
29378 display : this.caption_display,
29379 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29381 width: this.align == 'center' ? this.width : '100%'
29385 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29390 marginTop : '16px',
29396 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29398 contenteditable : true,
29414 readElement : function(node)
29416 // this should not really come from the link...
29417 this.video_url = this.getVal(node, 'div', 'src');
29418 this.cls = this.getVal(node, 'div', 'class');
29419 this.href = this.getVal(node, 'a', 'href');
29422 this.image_src = this.getVal(node, 'img', 'src');
29424 this.align = this.getVal(node, 'figure', 'align');
29425 var figcaption = this.getVal(node, 'figcaption', false);
29426 if (figcaption !== '') {
29427 this.caption = this.getVal(figcaption, 'i', 'html');
29431 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29432 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29433 this.width = this.getVal(node, true, 'data-width');
29434 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29437 removeNode : function()
29454 * @class Roo.htmleditor.BlockTable
29455 * Block that manages a table
29458 * Create a new Filter.
29459 * @param {Object} config Configuration options
29462 Roo.htmleditor.BlockTable = function(cfg)
29465 this.readElement(cfg.node);
29466 this.updateElement(cfg.node);
29468 Roo.apply(this, cfg);
29471 for(var r = 0; r < this.no_row; r++) {
29473 for(var c = 0; c < this.no_col; c++) {
29474 this.rows[r][c] = this.emptyCell();
29481 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29490 // used by context menu
29491 friendly_name : 'Table',
29492 deleteTitle : 'Delete Table',
29493 // context menu is drawn once..
29495 contextMenu : function(toolbar)
29498 var block = function() {
29499 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29503 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29505 var syncValue = toolbar.editorcore.syncValue;
29511 xtype : 'TextItem',
29513 xns : rooui.Toolbar //Boostrap?
29516 xtype : 'ComboBox',
29517 allowBlank : false,
29518 displayField : 'val',
29521 triggerAction : 'all',
29523 valueField : 'val',
29527 select : function (combo, r, index)
29529 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29531 b.width = r.get('val');
29534 toolbar.editorcore.onEditorEvent();
29539 xtype : 'SimpleStore',
29551 xtype : 'TextItem',
29552 text : "Columns: ",
29553 xns : rooui.Toolbar //Boostrap?
29560 click : function (_self, e)
29562 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29563 block().removeColumn();
29565 toolbar.editorcore.onEditorEvent();
29568 xns : rooui.Toolbar
29574 click : function (_self, e)
29576 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29577 block().addColumn();
29579 toolbar.editorcore.onEditorEvent();
29582 xns : rooui.Toolbar
29586 xtype : 'TextItem',
29588 xns : rooui.Toolbar //Boostrap?
29595 click : function (_self, e)
29597 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29598 block().removeRow();
29600 toolbar.editorcore.onEditorEvent();
29603 xns : rooui.Toolbar
29609 click : function (_self, e)
29613 toolbar.editorcore.onEditorEvent();
29616 xns : rooui.Toolbar
29621 text: 'Reset Column Widths',
29624 click : function (_self, e)
29626 block().resetWidths();
29628 toolbar.editorcore.onEditorEvent();
29631 xns : rooui.Toolbar
29642 * create a DomHelper friendly object - for use with
29643 * Roo.DomHelper.markup / overwrite / etc..
29644 * ?? should it be called with option to hide all editing features?
29646 toObject : function()
29651 contenteditable : 'false', // this stops cell selection from picking the table.
29652 'data-block' : 'Table',
29655 border : 'solid 1px #000', // ??? hard coded?
29656 'border-collapse' : 'collapse'
29659 { tag : 'tbody' , cn : [] }
29663 // do we have a head = not really
29665 Roo.each(this.rows, function( row ) {
29670 border : 'solid 1px #000',
29676 ret.cn[0].cn.push(tr);
29677 // does the row have any properties? ?? height?
29679 Roo.each(row, function( cell ) {
29683 contenteditable : 'true',
29684 'data-block' : 'Td',
29688 if (cell.colspan > 1) {
29689 td.colspan = cell.colspan ;
29690 nc += cell.colspan;
29694 if (cell.rowspan > 1) {
29695 td.rowspan = cell.rowspan ;
29704 ncols = Math.max(nc, ncols);
29708 // add the header row..
29717 readElement : function(node)
29719 node = node ? node : this.node ;
29720 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29724 var trs = Array.from(node.rows);
29725 trs.forEach(function(tr) {
29727 this.rows.push(row);
29731 Array.from(tr.cells).forEach(function(td) {
29734 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29735 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29736 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29737 html : td.innerHTML
29739 no_column += add.colspan;
29746 this.no_col = Math.max(this.no_col, no_column);
29753 normalizeRows: function()
29757 this.rows.forEach(function(row) {
29760 row = this.normalizeRow(row);
29762 row.forEach(function(c) {
29763 while (typeof(ret[rid][cid]) != 'undefined') {
29766 if (typeof(ret[rid]) == 'undefined') {
29772 if (c.rowspan < 2) {
29776 for(var i = 1 ;i < c.rowspan; i++) {
29777 if (typeof(ret[rid+i]) == 'undefined') {
29780 ret[rid+i][cid] = c;
29788 normalizeRow: function(row)
29791 row.forEach(function(c) {
29792 if (c.colspan < 2) {
29796 for(var i =0 ;i < c.colspan; i++) {
29804 deleteColumn : function(sel)
29806 if (!sel || sel.type != 'col') {
29809 if (this.no_col < 2) {
29813 this.rows.forEach(function(row) {
29814 var cols = this.normalizeRow(row);
29815 var col = cols[sel.col];
29816 if (col.colspan > 1) {
29826 removeColumn : function()
29828 this.deleteColumn({
29830 col : this.no_col-1
29832 this.updateElement();
29836 addColumn : function()
29839 this.rows.forEach(function(row) {
29840 row.push(this.emptyCell());
29843 this.updateElement();
29846 deleteRow : function(sel)
29848 if (!sel || sel.type != 'row') {
29852 if (this.no_row < 2) {
29856 var rows = this.normalizeRows();
29859 rows[sel.row].forEach(function(col) {
29860 if (col.rowspan > 1) {
29863 col.remove = 1; // flage it as removed.
29868 this.rows.forEach(function(row) {
29870 row.forEach(function(c) {
29871 if (typeof(c.remove) == 'undefined') {
29876 if (newrow.length > 0) {
29880 this.rows = newrows;
29885 this.updateElement();
29888 removeRow : function()
29892 row : this.no_row-1
29898 addRow : function()
29902 for (var i = 0; i < this.no_col; i++ ) {
29904 row.push(this.emptyCell());
29907 this.rows.push(row);
29908 this.updateElement();
29912 // the default cell object... at present...
29913 emptyCell : function() {
29914 return (new Roo.htmleditor.BlockTd({})).toObject();
29919 removeNode : function()
29926 resetWidths : function()
29928 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29929 var nn = Roo.htmleditor.Block.factory(n);
29931 nn.updateElement(n);
29944 * since selections really work on the table cell, then editing really should work from there
29946 * The original plan was to support merging etc... - but that may not be needed yet..
29948 * So this simple version will support:
29950 * adjust the width +/-
29951 * reset the width...
29960 * @class Roo.htmleditor.BlockTable
29961 * Block that manages a table
29964 * Create a new Filter.
29965 * @param {Object} config Configuration options
29968 Roo.htmleditor.BlockTd = function(cfg)
29971 this.readElement(cfg.node);
29972 this.updateElement(cfg.node);
29974 Roo.apply(this, cfg);
29979 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29984 textAlign : 'left',
29991 // used by context menu
29992 friendly_name : 'Table Cell',
29993 deleteTitle : false, // use our customer delete
29995 // context menu is drawn once..
29997 contextMenu : function(toolbar)
30000 var cell = function() {
30001 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30004 var table = function() {
30005 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30009 var saveSel = function()
30011 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30013 var restoreSel = function()
30017 toolbar.editorcore.focus();
30018 var cr = toolbar.editorcore.getSelection();
30019 cr.removeAllRanges();
30021 toolbar.editorcore.onEditorEvent();
30022 }).defer(10, this);
30028 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30030 var syncValue = toolbar.editorcore.syncValue;
30037 text : 'Edit Table',
30039 click : function() {
30040 var t = toolbar.tb.selectedNode.closest('table');
30041 toolbar.editorcore.selectNode(t);
30042 toolbar.editorcore.onEditorEvent();
30051 xtype : 'TextItem',
30052 text : "Column Width: ",
30053 xns : rooui.Toolbar
30060 click : function (_self, e)
30062 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30063 cell().shrinkColumn();
30065 toolbar.editorcore.onEditorEvent();
30068 xns : rooui.Toolbar
30074 click : function (_self, e)
30076 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30077 cell().growColumn();
30079 toolbar.editorcore.onEditorEvent();
30082 xns : rooui.Toolbar
30086 xtype : 'TextItem',
30087 text : "Vertical Align: ",
30088 xns : rooui.Toolbar //Boostrap?
30091 xtype : 'ComboBox',
30092 allowBlank : false,
30093 displayField : 'val',
30096 triggerAction : 'all',
30098 valueField : 'val',
30102 select : function (combo, r, index)
30104 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30106 b.valign = r.get('val');
30109 toolbar.editorcore.onEditorEvent();
30114 xtype : 'SimpleStore',
30118 ['bottom'] // there are afew more...
30126 xtype : 'TextItem',
30127 text : "Merge Cells: ",
30128 xns : rooui.Toolbar
30137 click : function (_self, e)
30139 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30140 cell().mergeRight();
30141 //block().growColumn();
30143 toolbar.editorcore.onEditorEvent();
30146 xns : rooui.Toolbar
30153 click : function (_self, e)
30155 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30156 cell().mergeBelow();
30157 //block().growColumn();
30159 toolbar.editorcore.onEditorEvent();
30162 xns : rooui.Toolbar
30165 xtype : 'TextItem',
30167 xns : rooui.Toolbar
30175 click : function (_self, e)
30177 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30180 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30181 toolbar.editorcore.onEditorEvent();
30185 xns : rooui.Toolbar
30189 xns : rooui.Toolbar
30198 xns : rooui.Toolbar,
30207 click : function (_self, e)
30211 cell().deleteColumn();
30213 toolbar.editorcore.selectNode(t.node);
30214 toolbar.editorcore.onEditorEvent();
30223 click : function (_self, e)
30226 cell().deleteRow();
30229 toolbar.editorcore.selectNode(t.node);
30230 toolbar.editorcore.onEditorEvent();
30237 xtype : 'Separator',
30244 click : function (_self, e)
30247 var nn = t.node.nextSibling || t.node.previousSibling;
30248 t.node.parentNode.removeChild(t.node);
30250 toolbar.editorcore.selectNode(nn, true);
30252 toolbar.editorcore.onEditorEvent();
30262 // align... << fixme
30270 * create a DomHelper friendly object - for use with
30271 * Roo.DomHelper.markup / overwrite / etc..
30272 * ?? should it be called with option to hide all editing features?
30275 * create a DomHelper friendly object - for use with
30276 * Roo.DomHelper.markup / overwrite / etc..
30277 * ?? should it be called with option to hide all editing features?
30279 toObject : function()
30283 contenteditable : 'true', // this stops cell selection from picking the table.
30284 'data-block' : 'Td',
30285 valign : this.valign,
30287 'text-align' : this.textAlign,
30288 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30289 'border-collapse' : 'collapse',
30290 padding : '6px', // 8 for desktop / 4 for mobile
30291 'vertical-align': this.valign
30295 if (this.width != '') {
30296 ret.width = this.width;
30297 ret.style.width = this.width;
30301 if (this.colspan > 1) {
30302 ret.colspan = this.colspan ;
30304 if (this.rowspan > 1) {
30305 ret.rowspan = this.rowspan ;
30314 readElement : function(node)
30316 node = node ? node : this.node ;
30317 this.width = node.style.width;
30318 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30319 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30320 this.html = node.innerHTML;
30321 if (node.style.textAlign != '') {
30322 this.textAlign = node.style.textAlign;
30328 // the default cell object... at present...
30329 emptyCell : function() {
30333 textAlign : 'left',
30334 html : " " // is this going to be editable now?
30339 removeNode : function()
30341 return this.node.closest('table');
30349 toTableArray : function()
30352 var tab = this.node.closest('tr').closest('table');
30353 Array.from(tab.rows).forEach(function(r, ri){
30357 this.colWidths = [];
30358 var all_auto = true;
30359 Array.from(tab.rows).forEach(function(r, ri){
30362 Array.from(r.cells).forEach(function(ce, ci){
30367 colspan : ce.colSpan,
30368 rowspan : ce.rowSpan
30370 if (ce.isEqualNode(this.node)) {
30373 // if we have been filled up by a row?
30374 if (typeof(ret[rn][cn]) != 'undefined') {
30375 while(typeof(ret[rn][cn]) != 'undefined') {
30381 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30382 this.colWidths[cn] = ce.style.width;
30383 if (this.colWidths[cn] != '') {
30389 if (c.colspan < 2 && c.rowspan < 2 ) {
30394 for(var j = 0; j < c.rowspan; j++) {
30395 if (typeof(ret[rn+j]) == 'undefined') {
30396 continue; // we have a problem..
30399 for(var i = 0; i < c.colspan; i++) {
30400 ret[rn+j][cn+i] = c;
30409 // initalize widths.?
30410 // either all widths or no widths..
30412 this.colWidths[0] = false; // no widths flag.
30423 mergeRight: function()
30426 // get the contents of the next cell along..
30427 var tr = this.node.closest('tr');
30428 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30429 if (i >= tr.childNodes.length - 1) {
30430 return; // no cells on right to merge with.
30432 var table = this.toTableArray();
30434 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30435 return; // nothing right?
30437 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30438 // right cell - must be same rowspan and on the same row.
30439 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30440 return; // right hand side is not same rowspan.
30445 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30446 tr.removeChild(rc.cell);
30447 this.colspan += rc.colspan;
30448 this.node.setAttribute('colspan', this.colspan);
30450 var table = this.toTableArray();
30451 this.normalizeWidths(table);
30452 this.updateWidths(table);
30456 mergeBelow : function()
30458 var table = this.toTableArray();
30459 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30460 return; // no row below
30462 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30463 return; // nothing right?
30465 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30467 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30468 return; // right hand side is not same rowspan.
30470 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30471 rc.cell.parentNode.removeChild(rc.cell);
30472 this.rowspan += rc.rowspan;
30473 this.node.setAttribute('rowspan', this.rowspan);
30478 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30481 var table = this.toTableArray();
30482 var cd = this.cellData;
30486 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30489 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30490 if (r == cd.row && c == cd.col) {
30491 this.node.removeAttribute('rowspan');
30492 this.node.removeAttribute('colspan');
30495 var ntd = this.node.cloneNode(); // which col/row should be 0..
30496 ntd.removeAttribute('id');
30497 ntd.style.width = this.colWidths[c];
30498 ntd.innerHTML = '';
30499 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30503 this.redrawAllCells(table);
30509 redrawAllCells: function(table)
30513 var tab = this.node.closest('tr').closest('table');
30514 var ctr = tab.rows[0].parentNode;
30515 Array.from(tab.rows).forEach(function(r, ri){
30517 Array.from(r.cells).forEach(function(ce, ci){
30518 ce.parentNode.removeChild(ce);
30520 r.parentNode.removeChild(r);
30522 for(var r = 0 ; r < table.length; r++) {
30523 var re = tab.rows[r];
30525 var re = tab.ownerDocument.createElement('tr');
30526 ctr.appendChild(re);
30527 for(var c = 0 ; c < table[r].length; c++) {
30528 if (table[r][c].cell === false) {
30532 re.appendChild(table[r][c].cell);
30534 table[r][c].cell = false;
30539 updateWidths : function(table)
30541 for(var r = 0 ; r < table.length; r++) {
30543 for(var c = 0 ; c < table[r].length; c++) {
30544 if (table[r][c].cell === false) {
30548 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30549 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30550 el.width = Math.floor(this.colWidths[c]) +'%';
30551 el.updateElement(el.node);
30553 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30554 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30556 for(var i = 0; i < table[r][c].colspan; i ++) {
30557 width += Math.floor(this.colWidths[c + i]);
30559 el.width = width +'%';
30560 el.updateElement(el.node);
30562 table[r][c].cell = false; // done
30566 normalizeWidths : function(table)
30568 if (this.colWidths[0] === false) {
30569 var nw = 100.0 / this.colWidths.length;
30570 this.colWidths.forEach(function(w,i) {
30571 this.colWidths[i] = nw;
30576 var t = 0, missing = [];
30578 this.colWidths.forEach(function(w,i) {
30580 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30581 var add = this.colWidths[i];
30590 var nc = this.colWidths.length;
30591 if (missing.length) {
30592 var mult = (nc - missing.length) / (1.0 * nc);
30594 var ew = (100 -t) / (1.0 * missing.length);
30595 this.colWidths.forEach(function(w,i) {
30597 this.colWidths[i] = w * mult;
30601 this.colWidths[i] = ew;
30603 // have to make up numbers..
30606 // now we should have all the widths..
30611 shrinkColumn : function()
30613 var table = this.toTableArray();
30614 this.normalizeWidths(table);
30615 var col = this.cellData.col;
30616 var nw = this.colWidths[col] * 0.8;
30620 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30621 this.colWidths.forEach(function(w,i) {
30623 this.colWidths[i] = nw;
30626 this.colWidths[i] += otherAdd
30628 this.updateWidths(table);
30631 growColumn : function()
30633 var table = this.toTableArray();
30634 this.normalizeWidths(table);
30635 var col = this.cellData.col;
30636 var nw = this.colWidths[col] * 1.2;
30640 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30641 this.colWidths.forEach(function(w,i) {
30643 this.colWidths[i] = nw;
30646 this.colWidths[i] -= otherSub
30648 this.updateWidths(table);
30651 deleteRow : function()
30653 // delete this rows 'tr'
30654 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30655 // then reduce the rowspan.
30656 var table = this.toTableArray();
30657 // this.cellData.row;
30658 for (var i =0;i< table[this.cellData.row].length ; i++) {
30659 var c = table[this.cellData.row][i];
30660 if (c.row != this.cellData.row) {
30663 c.cell.setAttribute('rowspan', c.rowspan);
30666 if (c.rowspan > 1) {
30668 c.cell.setAttribute('rowspan', c.rowspan);
30671 table.splice(this.cellData.row,1);
30672 this.redrawAllCells(table);
30675 deleteColumn : function()
30677 var table = this.toTableArray();
30679 for (var i =0;i< table.length ; i++) {
30680 var c = table[i][this.cellData.col];
30681 if (c.col != this.cellData.col) {
30682 table[i][this.cellData.col].colspan--;
30683 } else if (c.colspan > 1) {
30685 c.cell.setAttribute('colspan', c.colspan);
30687 table[i].splice(this.cellData.col,1);
30690 this.redrawAllCells(table);
30698 //<script type="text/javascript">
30701 * Based Ext JS Library 1.1.1
30702 * Copyright(c) 2006-2007, Ext JS, LLC.
30708 * @class Roo.HtmlEditorCore
30709 * @extends Roo.Component
30710 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30712 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30715 Roo.HtmlEditorCore = function(config){
30718 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30723 * @event initialize
30724 * Fires when the editor is fully initialized (including the iframe)
30725 * @param {Roo.HtmlEditorCore} this
30730 * Fires when the editor is first receives the focus. Any insertion must wait
30731 * until after this event.
30732 * @param {Roo.HtmlEditorCore} this
30736 * @event beforesync
30737 * Fires before the textarea is updated with content from the editor iframe. Return false
30738 * to cancel the sync.
30739 * @param {Roo.HtmlEditorCore} this
30740 * @param {String} html
30744 * @event beforepush
30745 * Fires before the iframe editor is updated with content from the textarea. Return false
30746 * to cancel the push.
30747 * @param {Roo.HtmlEditorCore} this
30748 * @param {String} html
30753 * Fires when the textarea is updated with content from the editor iframe.
30754 * @param {Roo.HtmlEditorCore} this
30755 * @param {String} html
30760 * Fires when the iframe editor is updated with content from the textarea.
30761 * @param {Roo.HtmlEditorCore} this
30762 * @param {String} html
30767 * @event editorevent
30768 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30769 * @param {Roo.HtmlEditorCore} this
30776 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30778 // defaults : white / black...
30779 this.applyBlacklists();
30786 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30790 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30796 * @cfg {String} css styling for resizing. (used on bootstrap only)
30800 * @cfg {Number} height (in pixels)
30804 * @cfg {Number} width (in pixels)
30808 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30809 * if you are doing an email editor, this probably needs disabling, it's designed
30814 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30816 enableBlocks : true,
30818 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30821 stylesheets: false,
30823 * @cfg {String} language default en - language of text (usefull for rtl languages)
30829 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30830 * - by default they are stripped - if you are editing email you may need this.
30832 allowComments: false,
30836 // private properties
30837 validationEvent : false,
30839 initialized : false,
30841 sourceEditMode : false,
30842 onFocus : Roo.emptyFn,
30844 hideMode:'offsets',
30848 // blacklist + whitelisted elements..
30855 undoManager : false,
30857 * Protected method that will not generally be called directly. It
30858 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30859 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30861 getDocMarkup : function(){
30865 // inherit styels from page...??
30866 if (this.stylesheets === false) {
30868 Roo.get(document.head).select('style').each(function(node) {
30869 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30872 Roo.get(document.head).select('link').each(function(node) {
30873 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30876 } else if (!this.stylesheets.length) {
30878 st = '<style type="text/css">' +
30879 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30882 for (var i in this.stylesheets) {
30883 if (typeof(this.stylesheets[i]) != 'string') {
30886 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30891 st += '<style type="text/css">' +
30892 'IMG { cursor: pointer } ' +
30895 st += '<meta name="google" content="notranslate">';
30897 var cls = 'notranslate roo-htmleditor-body';
30899 if(this.bodyCls.length){
30900 cls += ' ' + this.bodyCls;
30903 return '<html class="notranslate" translate="no"><head>' + st +
30904 //<style type="text/css">' +
30905 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30907 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30911 onRender : function(ct, position)
30914 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30915 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30918 this.el.dom.style.border = '0 none';
30919 this.el.dom.setAttribute('tabIndex', -1);
30920 this.el.addClass('x-hidden hide');
30924 if(Roo.isIE){ // fix IE 1px bogus margin
30925 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30929 this.frameId = Roo.id();
30933 cls: 'form-control', // bootstrap..
30935 name: this.frameId,
30936 frameBorder : 'no',
30937 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
30940 ifcfg.style = { resize : this.resize };
30943 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
30946 this.iframe = iframe.dom;
30948 this.assignDocWin();
30950 this.doc.designMode = 'on';
30953 this.doc.write(this.getDocMarkup());
30957 var task = { // must defer to wait for browser to be ready
30959 //console.log("run task?" + this.doc.readyState);
30960 this.assignDocWin();
30961 if(this.doc.body || this.doc.readyState == 'complete'){
30963 this.doc.designMode="on";
30968 Roo.TaskMgr.stop(task);
30969 this.initEditor.defer(10, this);
30976 Roo.TaskMgr.start(task);
30981 onResize : function(w, h)
30983 Roo.log('resize: ' +w + ',' + h );
30984 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30988 if(typeof w == 'number'){
30990 this.iframe.style.width = w + 'px';
30992 if(typeof h == 'number'){
30994 this.iframe.style.height = h + 'px';
30996 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31003 * Toggles the editor between standard and source edit mode.
31004 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31006 toggleSourceEdit : function(sourceEditMode){
31008 this.sourceEditMode = sourceEditMode === true;
31010 if(this.sourceEditMode){
31012 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31015 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31016 //this.iframe.className = '';
31019 //this.setSize(this.owner.wrap.getSize());
31020 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31027 * Protected method that will not generally be called directly. If you need/want
31028 * custom HTML cleanup, this is the method you should override.
31029 * @param {String} html The HTML to be cleaned
31030 * return {String} The cleaned HTML
31032 cleanHtml : function(html)
31034 html = String(html);
31035 if(html.length > 5){
31036 if(Roo.isSafari){ // strip safari nonsense
31037 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31040 if(html == ' '){
31047 * HTML Editor -> Textarea
31048 * Protected method that will not generally be called directly. Syncs the contents
31049 * of the editor iframe with the textarea.
31051 syncValue : function()
31053 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31054 if(this.initialized){
31056 if (this.undoManager) {
31057 this.undoManager.addEvent();
31061 var bd = (this.doc.body || this.doc.documentElement);
31064 var sel = this.win.getSelection();
31066 var div = document.createElement('div');
31067 div.innerHTML = bd.innerHTML;
31068 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31069 if (gtx.length > 0) {
31070 var rm = gtx.item(0).parentNode;
31071 rm.parentNode.removeChild(rm);
31075 if (this.enableBlocks) {
31076 new Roo.htmleditor.FilterBlock({ node : div });
31079 var html = div.innerHTML;
31082 if (this.autoClean) {
31084 new Roo.htmleditor.FilterAttributes({
31105 attrib_clean : ['href', 'src' ]
31108 var tidy = new Roo.htmleditor.TidySerializer({
31111 html = tidy.serialize(div);
31117 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31118 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31120 html = '<div style="'+m[0]+'">' + html + '</div>';
31123 html = this.cleanHtml(html);
31124 // fix up the special chars.. normaly like back quotes in word...
31125 // however we do not want to do this with chinese..
31126 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31128 var cc = match.charCodeAt();
31130 // Get the character value, handling surrogate pairs
31131 if (match.length == 2) {
31132 // It's a surrogate pair, calculate the Unicode code point
31133 var high = match.charCodeAt(0) - 0xD800;
31134 var low = match.charCodeAt(1) - 0xDC00;
31135 cc = (high * 0x400) + low + 0x10000;
31137 (cc >= 0x4E00 && cc < 0xA000 ) ||
31138 (cc >= 0x3400 && cc < 0x4E00 ) ||
31139 (cc >= 0xf900 && cc < 0xfb00 )
31144 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31145 return "&#" + cc + ";";
31152 if(this.owner.fireEvent('beforesync', this, html) !== false){
31153 this.el.dom.value = html;
31154 this.owner.fireEvent('sync', this, html);
31160 * TEXTAREA -> EDITABLE
31161 * Protected method that will not generally be called directly. Pushes the value of the textarea
31162 * into the iframe editor.
31164 pushValue : function()
31166 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31167 if(this.initialized){
31168 var v = this.el.dom.value.trim();
31171 if(this.owner.fireEvent('beforepush', this, v) !== false){
31172 var d = (this.doc.body || this.doc.documentElement);
31175 this.el.dom.value = d.innerHTML;
31176 this.owner.fireEvent('push', this, v);
31178 if (this.autoClean) {
31179 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31180 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31182 if (this.enableBlocks) {
31183 Roo.htmleditor.Block.initAll(this.doc.body);
31186 this.updateLanguage();
31188 var lc = this.doc.body.lastChild;
31189 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31190 // add an extra line at the end.
31191 this.doc.body.appendChild(this.doc.createElement('br'));
31199 deferFocus : function(){
31200 this.focus.defer(10, this);
31204 focus : function(){
31205 if(this.win && !this.sourceEditMode){
31212 assignDocWin: function()
31214 var iframe = this.iframe;
31217 this.doc = iframe.contentWindow.document;
31218 this.win = iframe.contentWindow;
31220 // if (!Roo.get(this.frameId)) {
31223 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31224 // this.win = Roo.get(this.frameId).dom.contentWindow;
31226 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31230 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31231 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31236 initEditor : function(){
31237 //console.log("INIT EDITOR");
31238 this.assignDocWin();
31242 this.doc.designMode="on";
31244 this.doc.write(this.getDocMarkup());
31247 var dbody = (this.doc.body || this.doc.documentElement);
31248 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31249 // this copies styles from the containing element into thsi one..
31250 // not sure why we need all of this..
31251 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31253 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31254 //ss['background-attachment'] = 'fixed'; // w3c
31255 dbody.bgProperties = 'fixed'; // ie
31256 dbody.setAttribute("translate", "no");
31258 //Roo.DomHelper.applyStyles(dbody, ss);
31259 Roo.EventManager.on(this.doc, {
31261 'mouseup': this.onEditorEvent,
31262 'dblclick': this.onEditorEvent,
31263 'click': this.onEditorEvent,
31264 'keyup': this.onEditorEvent,
31269 Roo.EventManager.on(this.doc, {
31270 'paste': this.onPasteEvent,
31274 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31277 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31278 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31280 this.initialized = true;
31283 // initialize special key events - enter
31284 new Roo.htmleditor.KeyEnter({core : this});
31288 this.owner.fireEvent('initialize', this);
31291 // this is to prevent a href clicks resulting in a redirect?
31293 onPasteEvent : function(e,v)
31295 // I think we better assume paste is going to be a dirty load of rubish from word..
31297 // even pasting into a 'email version' of this widget will have to clean up that mess.
31298 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31300 // check what type of paste - if it's an image, then handle it differently.
31301 if (cd.files && cd.files.length > 0) {
31303 var urlAPI = (window.createObjectURL && window) ||
31304 (window.URL && URL.revokeObjectURL && URL) ||
31305 (window.webkitURL && webkitURL);
31307 var url = urlAPI.createObjectURL( cd.files[0]);
31308 this.insertAtCursor('<img src=" + url + ">');
31311 if (cd.types.indexOf('text/html') < 0 ) {
31315 var html = cd.getData('text/html'); // clipboard event
31316 if (cd.types.indexOf('text/rtf') > -1) {
31317 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31318 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31323 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31324 .map(function(g) { return g.toDataURL(); })
31325 .filter(function(g) { return g != 'about:blank'; });
31328 html = this.cleanWordChars(html);
31330 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31333 var sn = this.getParentElement();
31334 // check if d contains a table, and prevent nesting??
31335 //Roo.log(d.getElementsByTagName('table'));
31337 //Roo.log(sn.closest('table'));
31338 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31339 e.preventDefault();
31340 this.insertAtCursor("You can not nest tables");
31341 //Roo.log("prevent?"); // fixme -
31347 if (images.length > 0) {
31348 // replace all v:imagedata - with img.
31349 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31350 Roo.each(ar, function(node) {
31351 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31352 node.parentNode.removeChild(node);
31356 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31357 img.setAttribute('src', images[i]);
31360 if (this.autoClean) {
31361 new Roo.htmleditor.FilterWord({ node : d });
31363 new Roo.htmleditor.FilterStyleToTag({ node : d });
31364 new Roo.htmleditor.FilterAttributes({
31366 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31367 attrib_clean : ['href', 'src' ]
31369 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31370 // should be fonts..
31371 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31372 new Roo.htmleditor.FilterParagraph({ node : d });
31373 new Roo.htmleditor.FilterSpan({ node : d });
31374 new Roo.htmleditor.FilterLongBr({ node : d });
31375 new Roo.htmleditor.FilterComment({ node : d });
31379 if (this.enableBlocks) {
31381 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31382 if (img.closest('figure')) { // assume!! that it's aready
31385 var fig = new Roo.htmleditor.BlockFigure({
31386 image_src : img.src
31388 fig.updateElement(img); // replace it..
31394 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31395 if (this.enableBlocks) {
31396 Roo.htmleditor.Block.initAll(this.doc.body);
31400 e.preventDefault();
31401 this.owner.fireEvent('paste', this);
31403 // default behaveiour should be our local cleanup paste? (optional?)
31404 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31405 //this.owner.fireEvent('paste', e, v);
31408 onDestroy : function(){
31414 //for (var i =0; i < this.toolbars.length;i++) {
31415 // // fixme - ask toolbars for heights?
31416 // this.toolbars[i].onDestroy();
31419 //this.wrap.dom.innerHTML = '';
31420 //this.wrap.remove();
31425 onFirstFocus : function(){
31427 this.assignDocWin();
31428 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31430 this.activated = true;
31433 if(Roo.isGecko){ // prevent silly gecko errors
31435 var s = this.win.getSelection();
31436 if(!s.focusNode || s.focusNode.nodeType != 3){
31437 var r = s.getRangeAt(0);
31438 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31443 this.execCmd('useCSS', true);
31444 this.execCmd('styleWithCSS', false);
31447 this.owner.fireEvent('activate', this);
31451 adjustFont: function(btn){
31452 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31453 //if(Roo.isSafari){ // safari
31456 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31457 if(Roo.isSafari){ // safari
31458 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31459 v = (v < 10) ? 10 : v;
31460 v = (v > 48) ? 48 : v;
31461 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31466 v = Math.max(1, v+adjust);
31468 this.execCmd('FontSize', v );
31471 onEditorEvent : function(e)
31475 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31476 return; // we do not handle this.. (undo manager does..)
31478 // in theory this detects if the last element is not a br, then we try and do that.
31479 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31481 e.target.nodeName == 'BODY' &&
31482 e.type == "mouseup" &&
31483 this.doc.body.lastChild
31485 var lc = this.doc.body.lastChild;
31486 // gtx-trans is google translate plugin adding crap.
31487 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31488 lc = lc.previousSibling;
31490 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31491 // if last element is <BR> - then dont do anything.
31493 var ns = this.doc.createElement('br');
31494 this.doc.body.appendChild(ns);
31495 range = this.doc.createRange();
31496 range.setStartAfter(ns);
31497 range.collapse(true);
31498 var sel = this.win.getSelection();
31499 sel.removeAllRanges();
31500 sel.addRange(range);
31506 this.fireEditorEvent(e);
31507 // this.updateToolbar();
31508 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31511 fireEditorEvent: function(e)
31513 this.owner.fireEvent('editorevent', this, e);
31516 insertTag : function(tg)
31518 // could be a bit smarter... -> wrap the current selected tRoo..
31519 if (tg.toLowerCase() == 'span' ||
31520 tg.toLowerCase() == 'code' ||
31521 tg.toLowerCase() == 'sup' ||
31522 tg.toLowerCase() == 'sub'
31525 range = this.createRange(this.getSelection());
31526 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31527 wrappingNode.appendChild(range.extractContents());
31528 range.insertNode(wrappingNode);
31535 this.execCmd("formatblock", tg);
31536 this.undoManager.addEvent();
31539 insertText : function(txt)
31543 var range = this.createRange();
31544 range.deleteContents();
31545 //alert(Sender.getAttribute('label'));
31547 range.insertNode(this.doc.createTextNode(txt));
31548 this.undoManager.addEvent();
31554 * Executes a Midas editor command on the editor document and performs necessary focus and
31555 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31556 * @param {String} cmd The Midas command
31557 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31559 relayCmd : function(cmd, value)
31563 case 'justifyleft':
31564 case 'justifyright':
31565 case 'justifycenter':
31566 // if we are in a cell, then we will adjust the
31567 var n = this.getParentElement();
31568 var td = n.closest('td');
31570 var bl = Roo.htmleditor.Block.factory(td);
31571 bl.textAlign = cmd.replace('justify','');
31572 bl.updateElement();
31573 this.owner.fireEvent('editorevent', this);
31576 this.execCmd('styleWithCSS', true); //
31580 // if there is no selection, then we insert, and set the curson inside it..
31581 this.execCmd('styleWithCSS', false);
31591 this.execCmd(cmd, value);
31592 this.owner.fireEvent('editorevent', this);
31593 //this.updateToolbar();
31594 this.owner.deferFocus();
31598 * Executes a Midas editor command directly on the editor document.
31599 * For visual commands, you should use {@link #relayCmd} instead.
31600 * <b>This should only be called after the editor is initialized.</b>
31601 * @param {String} cmd The Midas command
31602 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31604 execCmd : function(cmd, value){
31605 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31612 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31614 * @param {String} text | dom node..
31616 insertAtCursor : function(text)
31619 if(!this.activated){
31623 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31627 // from jquery ui (MIT licenced)
31629 var win = this.win;
31631 if (win.getSelection && win.getSelection().getRangeAt) {
31633 // delete the existing?
31635 this.createRange(this.getSelection()).deleteContents();
31636 range = win.getSelection().getRangeAt(0);
31637 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31638 range.insertNode(node);
31639 range = range.cloneRange();
31640 range.collapse(false);
31642 win.getSelection().removeAllRanges();
31643 win.getSelection().addRange(range);
31647 } else if (win.document.selection && win.document.selection.createRange) {
31648 // no firefox support
31649 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31650 win.document.selection.createRange().pasteHTML(txt);
31653 // no firefox support
31654 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31655 this.execCmd('InsertHTML', txt);
31663 mozKeyPress : function(e){
31665 var c = e.getCharCode(), cmd;
31668 c = String.fromCharCode(c).toLowerCase();
31682 // this.cleanUpPaste.defer(100, this);
31688 this.relayCmd(cmd);
31689 //this.win.focus();
31690 //this.execCmd(cmd);
31691 //this.deferFocus();
31692 e.preventDefault();
31700 fixKeys : function(){ // load time branching for fastest keydown performance
31704 return function(e){
31705 var k = e.getKey(), r;
31708 r = this.doc.selection.createRange();
31711 r.pasteHTML('    ');
31716 /// this is handled by Roo.htmleditor.KeyEnter
31719 r = this.doc.selection.createRange();
31721 var target = r.parentElement();
31722 if(!target || target.tagName.toLowerCase() != 'li'){
31724 r.pasteHTML('<br/>');
31731 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31732 // this.cleanUpPaste.defer(100, this);
31738 }else if(Roo.isOpera){
31739 return function(e){
31740 var k = e.getKey();
31744 this.execCmd('InsertHTML','    ');
31748 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31749 // this.cleanUpPaste.defer(100, this);
31754 }else if(Roo.isSafari){
31755 return function(e){
31756 var k = e.getKey();
31760 this.execCmd('InsertText','\t');
31764 this.mozKeyPress(e);
31766 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31767 // this.cleanUpPaste.defer(100, this);
31775 getAllAncestors: function()
31777 var p = this.getSelectedNode();
31780 a.push(p); // push blank onto stack..
31781 p = this.getParentElement();
31785 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31789 a.push(this.doc.body);
31793 lastSelNode : false,
31796 getSelection : function()
31798 this.assignDocWin();
31799 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31802 * Select a dom node
31803 * @param {DomElement} node the node to select
31805 selectNode : function(node, collapse)
31807 var nodeRange = node.ownerDocument.createRange();
31809 nodeRange.selectNode(node);
31811 nodeRange.selectNodeContents(node);
31813 if (collapse === true) {
31814 nodeRange.collapse(true);
31817 var s = this.win.getSelection();
31818 s.removeAllRanges();
31819 s.addRange(nodeRange);
31822 getSelectedNode: function()
31824 // this may only work on Gecko!!!
31826 // should we cache this!!!!
31830 var range = this.createRange(this.getSelection()).cloneRange();
31833 var parent = range.parentElement();
31835 var testRange = range.duplicate();
31836 testRange.moveToElementText(parent);
31837 if (testRange.inRange(range)) {
31840 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31843 parent = parent.parentElement;
31848 // is ancestor a text element.
31849 var ac = range.commonAncestorContainer;
31850 if (ac.nodeType == 3) {
31851 ac = ac.parentNode;
31854 var ar = ac.childNodes;
31857 var other_nodes = [];
31858 var has_other_nodes = false;
31859 for (var i=0;i<ar.length;i++) {
31860 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31863 // fullly contained node.
31865 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31870 // probably selected..
31871 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31872 other_nodes.push(ar[i]);
31876 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31881 has_other_nodes = true;
31883 if (!nodes.length && other_nodes.length) {
31884 nodes= other_nodes;
31886 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31894 createRange: function(sel)
31896 // this has strange effects when using with
31897 // top toolbar - not sure if it's a great idea.
31898 //this.editor.contentWindow.focus();
31899 if (typeof sel != "undefined") {
31901 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31903 return this.doc.createRange();
31906 return this.doc.createRange();
31909 getParentElement: function()
31912 this.assignDocWin();
31913 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31915 var range = this.createRange(sel);
31918 var p = range.commonAncestorContainer;
31919 while (p.nodeType == 3) { // text node
31930 * Range intersection.. the hard stuff...
31934 * [ -- selected range --- ]
31938 * if end is before start or hits it. fail.
31939 * if start is after end or hits it fail.
31941 * if either hits (but other is outside. - then it's not
31947 // @see http://www.thismuchiknow.co.uk/?p=64.
31948 rangeIntersectsNode : function(range, node)
31950 var nodeRange = node.ownerDocument.createRange();
31952 nodeRange.selectNode(node);
31954 nodeRange.selectNodeContents(node);
31957 var rangeStartRange = range.cloneRange();
31958 rangeStartRange.collapse(true);
31960 var rangeEndRange = range.cloneRange();
31961 rangeEndRange.collapse(false);
31963 var nodeStartRange = nodeRange.cloneRange();
31964 nodeStartRange.collapse(true);
31966 var nodeEndRange = nodeRange.cloneRange();
31967 nodeEndRange.collapse(false);
31969 return rangeStartRange.compareBoundaryPoints(
31970 Range.START_TO_START, nodeEndRange) == -1 &&
31971 rangeEndRange.compareBoundaryPoints(
31972 Range.START_TO_START, nodeStartRange) == 1;
31976 rangeCompareNode : function(range, node)
31978 var nodeRange = node.ownerDocument.createRange();
31980 nodeRange.selectNode(node);
31982 nodeRange.selectNodeContents(node);
31986 range.collapse(true);
31988 nodeRange.collapse(true);
31990 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31991 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
31993 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31995 var nodeIsBefore = ss == 1;
31996 var nodeIsAfter = ee == -1;
31998 if (nodeIsBefore && nodeIsAfter) {
32001 if (!nodeIsBefore && nodeIsAfter) {
32002 return 1; //right trailed.
32005 if (nodeIsBefore && !nodeIsAfter) {
32006 return 2; // left trailed.
32012 cleanWordChars : function(input) {// change the chars to hex code
32015 [ 8211, "–" ],
32016 [ 8212, "—" ],
32024 var output = input;
32025 Roo.each(swapCodes, function(sw) {
32026 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32028 output = output.replace(swapper, sw[1]);
32038 cleanUpChild : function (node)
32041 new Roo.htmleditor.FilterComment({node : node});
32042 new Roo.htmleditor.FilterAttributes({
32044 attrib_black : this.ablack,
32045 attrib_clean : this.aclean,
32046 style_white : this.cwhite,
32047 style_black : this.cblack
32049 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32050 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32056 * Clean up MS wordisms...
32057 * @deprecated - use filter directly
32059 cleanWord : function(node)
32061 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32062 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32069 * @deprecated - use filters
32071 cleanTableWidths : function(node)
32073 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32080 applyBlacklists : function()
32082 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32083 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32085 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32086 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32087 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32091 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32092 if (b.indexOf(tag) > -1) {
32095 this.white.push(tag);
32099 Roo.each(w, function(tag) {
32100 if (b.indexOf(tag) > -1) {
32103 if (this.white.indexOf(tag) > -1) {
32106 this.white.push(tag);
32111 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32112 if (w.indexOf(tag) > -1) {
32115 this.black.push(tag);
32119 Roo.each(b, function(tag) {
32120 if (w.indexOf(tag) > -1) {
32123 if (this.black.indexOf(tag) > -1) {
32126 this.black.push(tag);
32131 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32132 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32136 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32137 if (b.indexOf(tag) > -1) {
32140 this.cwhite.push(tag);
32144 Roo.each(w, function(tag) {
32145 if (b.indexOf(tag) > -1) {
32148 if (this.cwhite.indexOf(tag) > -1) {
32151 this.cwhite.push(tag);
32156 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32157 if (w.indexOf(tag) > -1) {
32160 this.cblack.push(tag);
32164 Roo.each(b, function(tag) {
32165 if (w.indexOf(tag) > -1) {
32168 if (this.cblack.indexOf(tag) > -1) {
32171 this.cblack.push(tag);
32176 setStylesheets : function(stylesheets)
32178 if(typeof(stylesheets) == 'string'){
32179 Roo.get(this.iframe.contentDocument.head).createChild({
32181 rel : 'stylesheet',
32190 Roo.each(stylesheets, function(s) {
32195 Roo.get(_this.iframe.contentDocument.head).createChild({
32197 rel : 'stylesheet',
32207 updateLanguage : function()
32209 if (!this.iframe || !this.iframe.contentDocument) {
32212 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32216 removeStylesheets : function()
32220 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32225 setStyle : function(style)
32227 Roo.get(this.iframe.contentDocument.head).createChild({
32236 // hide stuff that is not compatible
32250 * @event specialkey
32254 * @cfg {String} fieldClass @hide
32257 * @cfg {String} focusClass @hide
32260 * @cfg {String} autoCreate @hide
32263 * @cfg {String} inputType @hide
32266 * @cfg {String} invalidClass @hide
32269 * @cfg {String} invalidText @hide
32272 * @cfg {String} msgFx @hide
32275 * @cfg {String} validateOnBlur @hide
32279 Roo.HtmlEditorCore.white = [
32280 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32282 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32283 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32284 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32285 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32286 'TABLE', 'UL', 'XMP',
32288 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32291 'DIR', 'MENU', 'OL', 'UL', 'DL',
32297 Roo.HtmlEditorCore.black = [
32298 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32300 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32301 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32302 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32303 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32304 //'FONT' // CLEAN LATER..
32305 'COLGROUP', 'COL' // messy tables.
32309 Roo.HtmlEditorCore.clean = [ // ?? needed???
32310 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32312 Roo.HtmlEditorCore.tag_remove = [
32317 Roo.HtmlEditorCore.ablack = [
32321 Roo.HtmlEditorCore.aclean = [
32322 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32326 Roo.HtmlEditorCore.pwhite= [
32327 'http', 'https', 'mailto'
32330 // white listed style attributes.
32331 Roo.HtmlEditorCore.cwhite= [
32332 // 'text-align', /// default is to allow most things..
32338 // black listed style attributes.
32339 Roo.HtmlEditorCore.cblack= [
32340 // 'font-size' -- this can be set by the project
32354 * @class Roo.bootstrap.form.HtmlEditor
32355 * @extends Roo.bootstrap.form.TextArea
32356 * Bootstrap HtmlEditor class
32359 * Create a new HtmlEditor
32360 * @param {Object} config The config object
32363 Roo.bootstrap.form.HtmlEditor = function(config){
32364 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32365 if (!this.toolbars) {
32366 this.toolbars = [];
32369 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32372 * @event initialize
32373 * Fires when the editor is fully initialized (including the iframe)
32374 * @param {HtmlEditor} this
32379 * Fires when the editor is first receives the focus. Any insertion must wait
32380 * until after this event.
32381 * @param {HtmlEditor} this
32385 * @event beforesync
32386 * Fires before the textarea is updated with content from the editor iframe. Return false
32387 * to cancel the sync.
32388 * @param {HtmlEditor} this
32389 * @param {String} html
32393 * @event beforepush
32394 * Fires before the iframe editor is updated with content from the textarea. Return false
32395 * to cancel the push.
32396 * @param {HtmlEditor} this
32397 * @param {String} html
32402 * Fires when the textarea is updated with content from the editor iframe.
32403 * @param {HtmlEditor} this
32404 * @param {String} html
32409 * Fires when the iframe editor is updated with content from the textarea.
32410 * @param {HtmlEditor} this
32411 * @param {String} html
32415 * @event editmodechange
32416 * Fires when the editor switches edit modes
32417 * @param {HtmlEditor} this
32418 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32420 editmodechange: true,
32422 * @event editorevent
32423 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32424 * @param {HtmlEditor} this
32428 * @event firstfocus
32429 * Fires when on first focus - needed by toolbars..
32430 * @param {HtmlEditor} this
32435 * Auto save the htmlEditor value as a file into Events
32436 * @param {HtmlEditor} this
32440 * @event savedpreview
32441 * preview the saved version of htmlEditor
32442 * @param {HtmlEditor} this
32449 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32453 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32458 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32463 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32467 * @cfg {Number} height (in pixels)
32471 * @cfg {Number} width (in pixels)
32476 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32479 stylesheets: false,
32484 // private properties
32485 validationEvent : false,
32487 initialized : false,
32490 onFocus : Roo.emptyFn,
32492 hideMode:'offsets',
32494 tbContainer : false,
32498 toolbarContainer :function() {
32499 return this.wrap.select('.x-html-editor-tb',true).first();
32503 * Protected method that will not generally be called directly. It
32504 * is called when the editor creates its toolbar. Override this method if you need to
32505 * add custom toolbar buttons.
32506 * @param {HtmlEditor} editor
32508 createToolbar : function(){
32509 Roo.log('renewing');
32510 Roo.log("create toolbars");
32512 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32513 this.toolbars[0].render(this.toolbarContainer());
32517 // if (!editor.toolbars || !editor.toolbars.length) {
32518 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32521 // for (var i =0 ; i < editor.toolbars.length;i++) {
32522 // editor.toolbars[i] = Roo.factory(
32523 // typeof(editor.toolbars[i]) == 'string' ?
32524 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
32525 // Roo.bootstrap.form.HtmlEditor);
32526 // editor.toolbars[i].init(editor);
32532 onRender : function(ct, position)
32534 // Roo.log("Call onRender: " + this.xtype);
32536 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32538 this.wrap = this.inputEl().wrap({
32539 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32542 this.editorcore.onRender(ct, position);
32545 this.createToolbar(this);
32553 onResize : function(w, h)
32555 Roo.log('resize: ' +w + ',' + h );
32556 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32560 if(this.inputEl() ){
32561 if(typeof w == 'number'){
32562 var aw = w - this.wrap.getFrameWidth('lr');
32563 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32566 if(typeof h == 'number'){
32567 var tbh = -11; // fixme it needs to tool bar size!
32568 for (var i =0; i < this.toolbars.length;i++) {
32569 // fixme - ask toolbars for heights?
32570 tbh += this.toolbars[i].el.getHeight();
32571 //if (this.toolbars[i].footer) {
32572 // tbh += this.toolbars[i].footer.el.getHeight();
32580 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32581 ah -= 5; // knock a few pixes off for look..
32582 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32586 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32587 this.editorcore.onResize(ew,eh);
32592 * Toggles the editor between standard and source edit mode.
32593 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32595 toggleSourceEdit : function(sourceEditMode)
32597 this.editorcore.toggleSourceEdit(sourceEditMode);
32599 if(this.editorcore.sourceEditMode){
32600 Roo.log('editor - showing textarea');
32603 // Roo.log(this.syncValue());
32605 this.inputEl().removeClass(['hide', 'x-hidden']);
32606 this.inputEl().dom.removeAttribute('tabIndex');
32607 this.inputEl().focus();
32609 Roo.log('editor - hiding textarea');
32611 // Roo.log(this.pushValue());
32614 this.inputEl().addClass(['hide', 'x-hidden']);
32615 this.inputEl().dom.setAttribute('tabIndex', -1);
32616 //this.deferFocus();
32619 //if(this.resizable){
32620 // this.setSize(this.wrap.getSize());
32623 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32626 // private (for BoxComponent)
32627 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32629 // private (for BoxComponent)
32630 getResizeEl : function(){
32634 // private (for BoxComponent)
32635 getPositionEl : function(){
32640 initEvents : function(){
32641 this.originalValue = this.getValue();
32645 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32648 // markInvalid : Roo.emptyFn,
32650 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32653 // clearInvalid : Roo.emptyFn,
32655 setValue : function(v){
32656 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32657 this.editorcore.pushValue();
32662 deferFocus : function(){
32663 this.focus.defer(10, this);
32667 focus : function(){
32668 this.editorcore.focus();
32674 onDestroy : function(){
32680 for (var i =0; i < this.toolbars.length;i++) {
32681 // fixme - ask toolbars for heights?
32682 this.toolbars[i].onDestroy();
32685 this.wrap.dom.innerHTML = '';
32686 this.wrap.remove();
32691 onFirstFocus : function(){
32692 //Roo.log("onFirstFocus");
32693 this.editorcore.onFirstFocus();
32694 for (var i =0; i < this.toolbars.length;i++) {
32695 this.toolbars[i].onFirstFocus();
32701 syncValue : function()
32703 this.editorcore.syncValue();
32706 pushValue : function()
32708 this.editorcore.pushValue();
32712 // hide stuff that is not compatible
32726 * @event specialkey
32730 * @cfg {String} fieldClass @hide
32733 * @cfg {String} focusClass @hide
32736 * @cfg {String} autoCreate @hide
32739 * @cfg {String} inputType @hide
32743 * @cfg {String} invalidText @hide
32746 * @cfg {String} msgFx @hide
32749 * @cfg {String} validateOnBlur @hide
32758 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32760 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32761 * @parent Roo.bootstrap.form.HtmlEditor
32762 * @extends Roo.bootstrap.nav.Simplebar
32768 new Roo.bootstrap.form.HtmlEditor({
32771 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32772 disable : { fonts: 1 , format: 1, ..., ... , ...],
32778 * @cfg {Object} disable List of elements to disable..
32779 * @cfg {Array} btns List of additional buttons.
32783 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32786 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32789 Roo.apply(this, config);
32791 // default disabled, based on 'good practice'..
32792 this.disable = this.disable || {};
32793 Roo.applyIf(this.disable, {
32796 specialElements : true
32798 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32800 this.editor = config.editor;
32801 this.editorcore = config.editor.editorcore;
32803 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32805 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32806 // dont call parent... till later.
32808 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
32813 editorcore : false,
32818 "h1","h2","h3","h4","h5","h6",
32820 "abbr", "acronym", "address", "cite", "samp", "var",
32824 onRender : function(ct, position)
32826 // Roo.log("Call onRender: " + this.xtype);
32828 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32830 this.el.dom.style.marginBottom = '0';
32832 var editorcore = this.editorcore;
32833 var editor= this.editor;
32836 var btn = function(id,cmd , toggle, handler, html){
32838 var event = toggle ? 'toggle' : 'click';
32843 xns: Roo.bootstrap,
32847 enableToggle:toggle !== false,
32849 pressed : toggle ? false : null,
32852 a.listeners[toggle ? 'toggle' : 'click'] = function() {
32853 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
32859 // var cb_box = function...
32864 xns: Roo.bootstrap,
32869 xns: Roo.bootstrap,
32873 Roo.each(this.formats, function(f) {
32874 style.menu.items.push({
32876 xns: Roo.bootstrap,
32877 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32882 editorcore.insertTag(this.tagname);
32889 children.push(style);
32891 btn('bold',false,true);
32892 btn('italic',false,true);
32893 btn('align-left', 'justifyleft',true);
32894 btn('align-center', 'justifycenter',true);
32895 btn('align-right' , 'justifyright',true);
32896 btn('link', false, false, function(btn) {
32897 //Roo.log("create link?");
32898 var url = prompt(this.createLinkText, this.defaultLinkValue);
32899 if(url && url != 'http:/'+'/'){
32900 this.editorcore.relayCmd('createlink', url);
32903 btn('list','insertunorderedlist',true);
32904 btn('pencil', false,true, function(btn){
32906 this.toggleSourceEdit(btn.pressed);
32909 if (this.editor.btns.length > 0) {
32910 for (var i = 0; i<this.editor.btns.length; i++) {
32911 children.push(this.editor.btns[i]);
32919 xns: Roo.bootstrap,
32924 xns: Roo.bootstrap,
32929 cog.menu.items.push({
32931 xns: Roo.bootstrap,
32932 html : Clean styles,
32937 editorcore.insertTag(this.tagname);
32946 this.xtype = 'NavSimplebar';
32948 for(var i=0;i< children.length;i++) {
32950 this.buttons.add(this.addxtypeChild(children[i]));
32954 editor.on('editorevent', this.updateToolbar, this);
32956 onBtnClick : function(id)
32958 this.editorcore.relayCmd(id);
32959 this.editorcore.focus();
32963 * Protected method that will not generally be called directly. It triggers
32964 * a toolbar update by reading the markup state of the current selection in the editor.
32966 updateToolbar: function(){
32968 if(!this.editorcore.activated){
32969 this.editor.onFirstFocus(); // is this neeed?
32973 var btns = this.buttons;
32974 var doc = this.editorcore.doc;
32975 btns.get('bold').setActive(doc.queryCommandState('bold'));
32976 btns.get('italic').setActive(doc.queryCommandState('italic'));
32977 //btns.get('underline').setActive(doc.queryCommandState('underline'));
32979 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32980 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32981 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32983 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32984 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32987 var ans = this.editorcore.getAllAncestors();
32988 if (this.formatCombo) {
32991 var store = this.formatCombo.store;
32992 this.formatCombo.setValue("");
32993 for (var i =0; i < ans.length;i++) {
32994 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32996 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33004 // hides menus... - so this cant be on a menu...
33005 Roo.bootstrap.MenuMgr.hideAll();
33007 Roo.bootstrap.menu.Manager.hideAll();
33008 //this.editorsyncValue();
33010 onFirstFocus: function() {
33011 this.buttons.each(function(item){
33015 toggleSourceEdit : function(sourceEditMode){
33018 if(sourceEditMode){
33019 Roo.log("disabling buttons");
33020 this.buttons.each( function(item){
33021 if(item.cmd != 'pencil'){
33027 Roo.log("enabling buttons");
33028 if(this.editorcore.initialized){
33029 this.buttons.each( function(item){
33035 Roo.log("calling toggole on editor");
33036 // tell the editor that it's been pressed..
33037 this.editor.toggleSourceEdit(sourceEditMode);
33051 * @class Roo.bootstrap.form.Markdown
33052 * @extends Roo.bootstrap.form.TextArea
33053 * Bootstrap Showdown editable area
33054 * @cfg {string} content
33057 * Create a new Showdown
33060 Roo.bootstrap.form.Markdown = function(config){
33061 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33065 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33069 initEvents : function()
33072 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33073 this.markdownEl = this.el.createChild({
33074 cls : 'roo-markdown-area'
33076 this.inputEl().addClass('d-none');
33077 if (this.getValue() == '') {
33078 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33081 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33083 this.markdownEl.on('click', this.toggleTextEdit, this);
33084 this.on('blur', this.toggleTextEdit, this);
33085 this.on('specialkey', this.resizeTextArea, this);
33088 toggleTextEdit : function()
33090 var sh = this.markdownEl.getHeight();
33091 this.inputEl().addClass('d-none');
33092 this.markdownEl.addClass('d-none');
33093 if (!this.editing) {
33095 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33096 this.inputEl().removeClass('d-none');
33097 this.inputEl().focus();
33098 this.editing = true;
33101 // show showdown...
33102 this.updateMarkdown();
33103 this.markdownEl.removeClass('d-none');
33104 this.editing = false;
33107 updateMarkdown : function()
33109 if (this.getValue() == '') {
33110 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33114 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33117 resizeTextArea: function () {
33120 Roo.log([sh, this.getValue().split("\n").length * 30]);
33121 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33123 setValue : function(val)
33125 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33126 if (!this.editing) {
33127 this.updateMarkdown();
33133 if (!this.editing) {
33134 this.toggleTextEdit();
33142 * Ext JS Library 1.1.1
33143 * Copyright(c) 2006-2007, Ext JS, LLC.
33145 * Originally Released Under LGPL - original licence link has changed is not relivant.
33148 * <script type="text/javascript">
33152 * @class Roo.bootstrap.PagingToolbar
33153 * @extends Roo.bootstrap.nav.Simplebar
33154 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33156 * Create a new PagingToolbar
33157 * @param {Object} config The config object
33158 * @param {Roo.data.Store} store
33160 Roo.bootstrap.PagingToolbar = function(config)
33162 // old args format still supported... - xtype is prefered..
33163 // created from xtype...
33165 this.ds = config.dataSource;
33167 if (config.store && !this.ds) {
33168 this.store= Roo.factory(config.store, Roo.data);
33169 this.ds = this.store;
33170 this.ds.xmodule = this.xmodule || false;
33173 this.toolbarItems = [];
33174 if (config.items) {
33175 this.toolbarItems = config.items;
33178 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33183 this.bind(this.ds);
33186 if (Roo.bootstrap.version == 4) {
33187 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33189 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33194 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33196 * @cfg {Roo.bootstrap.Button} buttons[]
33197 * Buttons for the toolbar
33200 * @cfg {Roo.data.Store} store
33201 * The underlying data store providing the paged data
33204 * @cfg {String/HTMLElement/Element} container
33205 * container The id or element that will contain the toolbar
33208 * @cfg {Boolean} displayInfo
33209 * True to display the displayMsg (defaults to false)
33212 * @cfg {Number} pageSize
33213 * The number of records to display per page (defaults to 20)
33217 * @cfg {String} displayMsg
33218 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33220 displayMsg : 'Displaying {0} - {1} of {2}',
33222 * @cfg {String} emptyMsg
33223 * The message to display when no records are found (defaults to "No data to display")
33225 emptyMsg : 'No data to display',
33227 * Customizable piece of the default paging text (defaults to "Page")
33230 beforePageText : "Page",
33232 * Customizable piece of the default paging text (defaults to "of %0")
33235 afterPageText : "of {0}",
33237 * Customizable piece of the default paging text (defaults to "First Page")
33240 firstText : "First Page",
33242 * Customizable piece of the default paging text (defaults to "Previous Page")
33245 prevText : "Previous Page",
33247 * Customizable piece of the default paging text (defaults to "Next Page")
33250 nextText : "Next Page",
33252 * Customizable piece of the default paging text (defaults to "Last Page")
33255 lastText : "Last Page",
33257 * Customizable piece of the default paging text (defaults to "Refresh")
33260 refreshText : "Refresh",
33264 onRender : function(ct, position)
33266 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33267 this.navgroup.parentId = this.id;
33268 this.navgroup.onRender(this.el, null);
33269 // add the buttons to the navgroup
33271 if(this.displayInfo){
33272 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33273 this.displayEl = this.el.select('.x-paging-info', true).first();
33274 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33275 // this.displayEl = navel.el.select('span',true).first();
33281 Roo.each(_this.buttons, function(e){ // this might need to use render????
33282 Roo.factory(e).render(_this.el);
33286 Roo.each(_this.toolbarItems, function(e) {
33287 _this.navgroup.addItem(e);
33291 this.first = this.navgroup.addItem({
33292 tooltip: this.firstText,
33293 cls: "prev btn-outline-secondary",
33294 html : ' <i class="fa fa-step-backward"></i>',
33296 preventDefault: true,
33297 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33300 this.prev = this.navgroup.addItem({
33301 tooltip: this.prevText,
33302 cls: "prev btn-outline-secondary",
33303 html : ' <i class="fa fa-backward"></i>',
33305 preventDefault: true,
33306 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33308 //this.addSeparator();
33311 var field = this.navgroup.addItem( {
33313 cls : 'x-paging-position btn-outline-secondary',
33315 html : this.beforePageText +
33316 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33317 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33320 this.field = field.el.select('input', true).first();
33321 this.field.on("keydown", this.onPagingKeydown, this);
33322 this.field.on("focus", function(){this.dom.select();});
33325 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33326 //this.field.setHeight(18);
33327 //this.addSeparator();
33328 this.next = this.navgroup.addItem({
33329 tooltip: this.nextText,
33330 cls: "next btn-outline-secondary",
33331 html : ' <i class="fa fa-forward"></i>',
33333 preventDefault: true,
33334 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33336 this.last = this.navgroup.addItem({
33337 tooltip: this.lastText,
33338 html : ' <i class="fa fa-step-forward"></i>',
33339 cls: "next btn-outline-secondary",
33341 preventDefault: true,
33342 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33344 //this.addSeparator();
33345 this.loading = this.navgroup.addItem({
33346 tooltip: this.refreshText,
33347 cls: "btn-outline-secondary",
33348 html : ' <i class="fa fa-refresh"></i>',
33349 preventDefault: true,
33350 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33356 updateInfo : function(){
33357 if(this.displayEl){
33358 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33359 var msg = count == 0 ?
33363 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33365 this.displayEl.update(msg);
33370 onLoad : function(ds, r, o)
33372 this.cursor = o.params && o.params.start ? o.params.start : 0;
33374 var d = this.getPageData(),
33379 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33380 this.field.dom.value = ap;
33381 this.first.setDisabled(ap == 1);
33382 this.prev.setDisabled(ap == 1);
33383 this.next.setDisabled(ap == ps);
33384 this.last.setDisabled(ap == ps);
33385 this.loading.enable();
33390 getPageData : function(){
33391 var total = this.ds.getTotalCount();
33394 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33395 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33400 onLoadError : function(proxy, o){
33401 this.loading.enable();
33402 if (this.ds.events.loadexception.listeners.length < 2) {
33403 // nothing has been assigned to loadexception except this...
33405 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33411 onPagingKeydown : function(e){
33412 var k = e.getKey();
33413 var d = this.getPageData();
33415 var v = this.field.dom.value, pageNum;
33416 if(!v || isNaN(pageNum = parseInt(v, 10))){
33417 this.field.dom.value = d.activePage;
33420 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33421 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33424 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))
33426 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33427 this.field.dom.value = pageNum;
33428 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33431 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33433 var v = this.field.dom.value, pageNum;
33434 var increment = (e.shiftKey) ? 10 : 1;
33435 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33438 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33439 this.field.dom.value = d.activePage;
33442 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33444 this.field.dom.value = parseInt(v, 10) + increment;
33445 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33446 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33453 beforeLoad : function(){
33455 this.loading.disable();
33460 onClick : function(which){
33469 ds.load({params:{start: 0, limit: this.pageSize}});
33472 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33475 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33478 var total = ds.getTotalCount();
33479 var extra = total % this.pageSize;
33480 var lastStart = extra ? (total - extra) : total-this.pageSize;
33481 ds.load({params:{start: lastStart, limit: this.pageSize}});
33484 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33490 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33491 * @param {Roo.data.Store} store The data store to unbind
33493 unbind : function(ds){
33494 ds.un("beforeload", this.beforeLoad, this);
33495 ds.un("load", this.onLoad, this);
33496 ds.un("loadexception", this.onLoadError, this);
33497 ds.un("remove", this.updateInfo, this);
33498 ds.un("add", this.updateInfo, this);
33499 this.ds = undefined;
33503 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33504 * @param {Roo.data.Store} store The data store to bind
33506 bind : function(ds){
33507 ds.on("beforeload", this.beforeLoad, this);
33508 ds.on("load", this.onLoad, this);
33509 ds.on("loadexception", this.onLoadError, this);
33510 ds.on("remove", this.updateInfo, this);
33511 ds.on("add", this.updateInfo, this);
33522 * @class Roo.bootstrap.MessageBar
33523 * @extends Roo.bootstrap.Component
33524 * Bootstrap MessageBar class
33525 * @cfg {String} html contents of the MessageBar
33526 * @cfg {String} weight (info | success | warning | danger) default info
33527 * @cfg {String} beforeClass insert the bar before the given class
33528 * @cfg {Boolean} closable (true | false) default false
33529 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33532 * Create a new Element
33533 * @param {Object} config The config object
33536 Roo.bootstrap.MessageBar = function(config){
33537 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33540 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33546 beforeClass: 'bootstrap-sticky-wrap',
33548 getAutoCreate : function(){
33552 cls: 'alert alert-dismissable alert-' + this.weight,
33557 html: this.html || ''
33563 cfg.cls += ' alert-messages-fixed';
33577 onRender : function(ct, position)
33579 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33582 var cfg = Roo.apply({}, this.getAutoCreate());
33586 cfg.cls += ' ' + this.cls;
33589 cfg.style = this.style;
33591 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33593 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33596 this.el.select('>button.close').on('click', this.hide, this);
33602 if (!this.rendered) {
33608 this.fireEvent('show', this);
33614 if (!this.rendered) {
33620 this.fireEvent('hide', this);
33623 update : function()
33625 // var e = this.el.dom.firstChild;
33627 // if(this.closable){
33628 // e = e.nextSibling;
33631 // e.data = this.html || '';
33633 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33649 * @class Roo.bootstrap.Graph
33650 * @extends Roo.bootstrap.Component
33651 * Bootstrap Graph class
33655 @cfg {String} graphtype bar | vbar | pie
33656 @cfg {number} g_x coodinator | centre x (pie)
33657 @cfg {number} g_y coodinator | centre y (pie)
33658 @cfg {number} g_r radius (pie)
33659 @cfg {number} g_height height of the chart (respected by all elements in the set)
33660 @cfg {number} g_width width of the chart (respected by all elements in the set)
33661 @cfg {Object} title The title of the chart
33664 -opts (object) options for the chart
33666 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33667 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33669 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.
33670 o stacked (boolean) whether or not to tread values as in a stacked bar chart
33672 o stretch (boolean)
33674 -opts (object) options for the pie
33677 o startAngle (number)
33678 o endAngle (number)
33682 * Create a new Input
33683 * @param {Object} config The config object
33686 Roo.bootstrap.Graph = function(config){
33687 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33693 * The img click event for the img.
33694 * @param {Roo.EventObject} e
33700 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
33711 //g_colors: this.colors,
33718 getAutoCreate : function(){
33729 onRender : function(ct,position){
33732 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33734 if (typeof(Raphael) == 'undefined') {
33735 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33739 this.raphael = Raphael(this.el.dom);
33741 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33742 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33743 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33744 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33746 r.text(160, 10, "Single Series Chart").attr(txtattr);
33747 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33748 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33749 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33751 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33752 r.barchart(330, 10, 300, 220, data1);
33753 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33754 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33757 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33758 // r.barchart(30, 30, 560, 250, xdata, {
33759 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33760 // axis : "0 0 1 1",
33761 // axisxlabels : xdata
33762 // //yvalues : cols,
33765 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33767 // this.load(null,xdata,{
33768 // axis : "0 0 1 1",
33769 // axisxlabels : xdata
33774 load : function(graphtype,xdata,opts)
33776 this.raphael.clear();
33778 graphtype = this.graphtype;
33783 var r = this.raphael,
33784 fin = function () {
33785 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33787 fout = function () {
33788 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33790 pfin = function() {
33791 this.sector.stop();
33792 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33795 this.label[0].stop();
33796 this.label[0].attr({ r: 7.5 });
33797 this.label[1].attr({ "font-weight": 800 });
33800 pfout = function() {
33801 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33804 this.label[0].animate({ r: 5 }, 500, "bounce");
33805 this.label[1].attr({ "font-weight": 400 });
33811 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33814 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33817 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
33818 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33820 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33827 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33832 setTitle: function(o)
33837 initEvents: function() {
33840 this.el.on('click', this.onClick, this);
33844 onClick : function(e)
33846 Roo.log('img onclick');
33847 this.fireEvent('click', this, e);
33853 Roo.bootstrap.dash = {};/*
33859 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33862 * @class Roo.bootstrap.dash.NumberBox
33863 * @extends Roo.bootstrap.Component
33864 * Bootstrap NumberBox class
33865 * @cfg {String} headline Box headline
33866 * @cfg {String} content Box content
33867 * @cfg {String} icon Box icon
33868 * @cfg {String} footer Footer text
33869 * @cfg {String} fhref Footer href
33872 * Create a new NumberBox
33873 * @param {Object} config The config object
33877 Roo.bootstrap.dash.NumberBox = function(config){
33878 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33882 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
33891 getAutoCreate : function(){
33895 cls : 'small-box ',
33903 cls : 'roo-headline',
33904 html : this.headline
33908 cls : 'roo-content',
33909 html : this.content
33923 cls : 'ion ' + this.icon
33932 cls : 'small-box-footer',
33933 href : this.fhref || '#',
33937 cfg.cn.push(footer);
33944 onRender : function(ct,position){
33945 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33952 setHeadline: function (value)
33954 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33957 setFooter: function (value, href)
33959 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33962 this.el.select('a.small-box-footer',true).first().attr('href', href);
33967 setContent: function (value)
33969 this.el.select('.roo-content',true).first().dom.innerHTML = value;
33972 initEvents: function()
33986 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33989 * @class Roo.bootstrap.dash.TabBox
33990 * @extends Roo.bootstrap.Component
33991 * @children Roo.bootstrap.dash.TabPane
33992 * Bootstrap TabBox class
33993 * @cfg {String} title Title of the TabBox
33994 * @cfg {String} icon Icon of the TabBox
33995 * @cfg {Boolean} showtabs (true|false) show the tabs default true
33996 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33999 * Create a new TabBox
34000 * @param {Object} config The config object
34004 Roo.bootstrap.dash.TabBox = function(config){
34005 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34010 * When a pane is added
34011 * @param {Roo.bootstrap.dash.TabPane} pane
34015 * @event activatepane
34016 * When a pane is activated
34017 * @param {Roo.bootstrap.dash.TabPane} pane
34019 "activatepane" : true
34027 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34032 tabScrollable : false,
34034 getChildContainer : function()
34036 return this.el.select('.tab-content', true).first();
34039 getAutoCreate : function(){
34043 cls: 'pull-left header',
34051 cls: 'fa ' + this.icon
34057 cls: 'nav nav-tabs pull-right',
34063 if(this.tabScrollable){
34070 cls: 'nav nav-tabs pull-right',
34081 cls: 'nav-tabs-custom',
34086 cls: 'tab-content no-padding',
34094 initEvents : function()
34096 //Roo.log('add add pane handler');
34097 this.on('addpane', this.onAddPane, this);
34100 * Updates the box title
34101 * @param {String} html to set the title to.
34103 setTitle : function(value)
34105 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34107 onAddPane : function(pane)
34109 this.panes.push(pane);
34110 //Roo.log('addpane');
34112 // tabs are rendere left to right..
34113 if(!this.showtabs){
34117 var ctr = this.el.select('.nav-tabs', true).first();
34120 var existing = ctr.select('.nav-tab',true);
34121 var qty = existing.getCount();;
34124 var tab = ctr.createChild({
34126 cls : 'nav-tab' + (qty ? '' : ' active'),
34134 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34137 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34139 pane.el.addClass('active');
34144 onTabClick : function(ev,un,ob,pane)
34146 //Roo.log('tab - prev default');
34147 ev.preventDefault();
34150 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34151 pane.tab.addClass('active');
34152 //Roo.log(pane.title);
34153 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34154 // technically we should have a deactivate event.. but maybe add later.
34155 // and it should not de-activate the selected tab...
34156 this.fireEvent('activatepane', pane);
34157 pane.el.addClass('active');
34158 pane.fireEvent('activate');
34163 getActivePane : function()
34166 Roo.each(this.panes, function(p) {
34167 if(p.el.hasClass('active')){
34188 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34190 * @class Roo.bootstrap.TabPane
34191 * @extends Roo.bootstrap.Component
34192 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34193 * Bootstrap TabPane class
34194 * @cfg {Boolean} active (false | true) Default false
34195 * @cfg {String} title title of panel
34199 * Create a new TabPane
34200 * @param {Object} config The config object
34203 Roo.bootstrap.dash.TabPane = function(config){
34204 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34210 * When a pane is activated
34211 * @param {Roo.bootstrap.dash.TabPane} pane
34218 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34223 // the tabBox that this is attached to.
34226 getAutoCreate : function()
34234 cfg.cls += ' active';
34239 initEvents : function()
34241 //Roo.log('trigger add pane handler');
34242 this.parent().fireEvent('addpane', this)
34246 * Updates the tab title
34247 * @param {String} html to set the title to.
34249 setTitle: function(str)
34255 this.tab.select('a', true).first().dom.innerHTML = str;
34274 * @class Roo.bootstrap.Tooltip
34275 * Bootstrap Tooltip class
34276 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34277 * to determine which dom element triggers the tooltip.
34279 * It needs to add support for additional attributes like tooltip-position
34282 * Create a new Toolti
34283 * @param {Object} config The config object
34286 Roo.bootstrap.Tooltip = function(config){
34287 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34289 this.alignment = Roo.bootstrap.Tooltip.alignment;
34291 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34292 this.alignment = config.alignment;
34297 Roo.apply(Roo.bootstrap.Tooltip, {
34299 * @function init initialize tooltip monitoring.
34303 currentTip : false,
34304 currentRegion : false,
34310 Roo.get(document).on('mouseover', this.enter ,this);
34311 Roo.get(document).on('mouseout', this.leave, this);
34314 this.currentTip = new Roo.bootstrap.Tooltip();
34317 enter : function(ev)
34319 var dom = ev.getTarget();
34321 //Roo.log(['enter',dom]);
34322 var el = Roo.fly(dom);
34323 if (this.currentEl) {
34325 //Roo.log(this.currentEl);
34326 //Roo.log(this.currentEl.contains(dom));
34327 if (this.currentEl == el) {
34330 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34336 if (this.currentTip.el) {
34337 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34341 if(!el || el.dom == document){
34347 if (!el.attr('tooltip')) {
34348 pel = el.findParent("[tooltip]");
34350 bindEl = Roo.get(pel);
34356 // you can not look for children, as if el is the body.. then everythign is the child..
34357 if (!pel && !el.attr('tooltip')) { //
34358 if (!el.select("[tooltip]").elements.length) {
34361 // is the mouse over this child...?
34362 bindEl = el.select("[tooltip]").first();
34363 var xy = ev.getXY();
34364 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34365 //Roo.log("not in region.");
34368 //Roo.log("child element over..");
34371 this.currentEl = el;
34372 this.currentTip.bind(bindEl);
34373 this.currentRegion = Roo.lib.Region.getRegion(dom);
34374 this.currentTip.enter();
34377 leave : function(ev)
34379 var dom = ev.getTarget();
34380 //Roo.log(['leave',dom]);
34381 if (!this.currentEl) {
34386 if (dom != this.currentEl.dom) {
34389 var xy = ev.getXY();
34390 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34393 // only activate leave if mouse cursor is outside... bounding box..
34398 if (this.currentTip) {
34399 this.currentTip.leave();
34401 //Roo.log('clear currentEl');
34402 this.currentEl = false;
34407 'left' : ['r-l', [-2,0], 'right'],
34408 'right' : ['l-r', [2,0], 'left'],
34409 'bottom' : ['t-b', [0,2], 'top'],
34410 'top' : [ 'b-t', [0,-2], 'bottom']
34416 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34421 delay : null, // can be { show : 300 , hide: 500}
34425 hoverState : null, //???
34427 placement : 'bottom',
34431 getAutoCreate : function(){
34438 cls : 'tooltip-arrow arrow'
34441 cls : 'tooltip-inner'
34448 bind : function(el)
34453 initEvents : function()
34455 this.arrowEl = this.el.select('.arrow', true).first();
34456 this.innerEl = this.el.select('.tooltip-inner', true).first();
34459 enter : function () {
34461 if (this.timeout != null) {
34462 clearTimeout(this.timeout);
34465 this.hoverState = 'in';
34466 //Roo.log("enter - show");
34467 if (!this.delay || !this.delay.show) {
34472 this.timeout = setTimeout(function () {
34473 if (_t.hoverState == 'in') {
34476 }, this.delay.show);
34480 clearTimeout(this.timeout);
34482 this.hoverState = 'out';
34483 if (!this.delay || !this.delay.hide) {
34489 this.timeout = setTimeout(function () {
34490 //Roo.log("leave - timeout");
34492 if (_t.hoverState == 'out') {
34494 Roo.bootstrap.Tooltip.currentEl = false;
34499 show : function (msg)
34502 this.render(document.body);
34505 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34507 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34509 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34511 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34512 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34514 if(this.bindEl.attr('tooltip-class')) {
34515 this.el.addClass(this.bindEl.attr('tooltip-class'));
34518 var placement = typeof this.placement == 'function' ?
34519 this.placement.call(this, this.el, on_el) :
34522 if(this.bindEl.attr('tooltip-placement')) {
34523 placement = this.bindEl.attr('tooltip-placement');
34526 var autoToken = /\s?auto?\s?/i;
34527 var autoPlace = autoToken.test(placement);
34529 placement = placement.replace(autoToken, '') || 'top';
34533 //this.el.setXY([0,0]);
34535 //this.el.dom.style.display='block';
34537 //this.el.appendTo(on_el);
34539 var p = this.getPosition();
34540 var box = this.el.getBox();
34546 var align = this.alignment[placement];
34548 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34550 if(placement == 'top' || placement == 'bottom'){
34552 placement = 'right';
34555 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34556 placement = 'left';
34559 var scroll = Roo.select('body', true).first().getScroll();
34561 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34565 align = this.alignment[placement];
34567 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34571 var elems = document.getElementsByTagName('div');
34572 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34573 for (var i = 0; i < elems.length; i++) {
34574 var zindex = Number.parseInt(
34575 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34578 if (zindex > highest) {
34585 this.el.dom.style.zIndex = highest;
34587 this.el.alignTo(this.bindEl, align[0],align[1]);
34588 //var arrow = this.el.select('.arrow',true).first();
34589 //arrow.set(align[2],
34591 this.el.addClass(placement);
34592 this.el.addClass("bs-tooltip-"+ placement);
34594 this.el.addClass('in fade show');
34596 this.hoverState = null;
34598 if (this.el.hasClass('fade')) {
34613 //this.el.setXY([0,0]);
34614 if(this.bindEl.attr('tooltip-class')) {
34615 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34617 this.el.removeClass(['show', 'in']);
34633 * @class Roo.bootstrap.LocationPicker
34634 * @extends Roo.bootstrap.Component
34635 * Bootstrap LocationPicker class
34636 * @cfg {Number} latitude Position when init default 0
34637 * @cfg {Number} longitude Position when init default 0
34638 * @cfg {Number} zoom default 15
34639 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34640 * @cfg {Boolean} mapTypeControl default false
34641 * @cfg {Boolean} disableDoubleClickZoom default false
34642 * @cfg {Boolean} scrollwheel default true
34643 * @cfg {Boolean} streetViewControl default false
34644 * @cfg {Number} radius default 0
34645 * @cfg {String} locationName
34646 * @cfg {Boolean} draggable default true
34647 * @cfg {Boolean} enableAutocomplete default false
34648 * @cfg {Boolean} enableReverseGeocode default true
34649 * @cfg {String} markerTitle
34652 * Create a new LocationPicker
34653 * @param {Object} config The config object
34657 Roo.bootstrap.LocationPicker = function(config){
34659 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34664 * Fires when the picker initialized.
34665 * @param {Roo.bootstrap.LocationPicker} this
34666 * @param {Google Location} location
34670 * @event positionchanged
34671 * Fires when the picker position changed.
34672 * @param {Roo.bootstrap.LocationPicker} this
34673 * @param {Google Location} location
34675 positionchanged : true,
34678 * Fires when the map resize.
34679 * @param {Roo.bootstrap.LocationPicker} this
34684 * Fires when the map show.
34685 * @param {Roo.bootstrap.LocationPicker} this
34690 * Fires when the map hide.
34691 * @param {Roo.bootstrap.LocationPicker} this
34696 * Fires when click the map.
34697 * @param {Roo.bootstrap.LocationPicker} this
34698 * @param {Map event} e
34702 * @event mapRightClick
34703 * Fires when right click the map.
34704 * @param {Roo.bootstrap.LocationPicker} this
34705 * @param {Map event} e
34707 mapRightClick : true,
34709 * @event markerClick
34710 * Fires when click the marker.
34711 * @param {Roo.bootstrap.LocationPicker} this
34712 * @param {Map event} e
34714 markerClick : true,
34716 * @event markerRightClick
34717 * Fires when right click the marker.
34718 * @param {Roo.bootstrap.LocationPicker} this
34719 * @param {Map event} e
34721 markerRightClick : true,
34723 * @event OverlayViewDraw
34724 * Fires when OverlayView Draw
34725 * @param {Roo.bootstrap.LocationPicker} this
34727 OverlayViewDraw : true,
34729 * @event OverlayViewOnAdd
34730 * Fires when OverlayView Draw
34731 * @param {Roo.bootstrap.LocationPicker} this
34733 OverlayViewOnAdd : true,
34735 * @event OverlayViewOnRemove
34736 * Fires when OverlayView Draw
34737 * @param {Roo.bootstrap.LocationPicker} this
34739 OverlayViewOnRemove : true,
34741 * @event OverlayViewShow
34742 * Fires when OverlayView Draw
34743 * @param {Roo.bootstrap.LocationPicker} this
34744 * @param {Pixel} cpx
34746 OverlayViewShow : true,
34748 * @event OverlayViewHide
34749 * Fires when OverlayView Draw
34750 * @param {Roo.bootstrap.LocationPicker} this
34752 OverlayViewHide : true,
34754 * @event loadexception
34755 * Fires when load google lib failed.
34756 * @param {Roo.bootstrap.LocationPicker} this
34758 loadexception : true
34763 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
34765 gMapContext: false,
34771 mapTypeControl: false,
34772 disableDoubleClickZoom: false,
34774 streetViewControl: false,
34778 enableAutocomplete: false,
34779 enableReverseGeocode: true,
34782 getAutoCreate: function()
34787 cls: 'roo-location-picker'
34793 initEvents: function(ct, position)
34795 if(!this.el.getWidth() || this.isApplied()){
34799 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34804 initial: function()
34806 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34807 this.fireEvent('loadexception', this);
34811 if(!this.mapTypeId){
34812 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34815 this.gMapContext = this.GMapContext();
34817 this.initOverlayView();
34819 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34823 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34824 _this.setPosition(_this.gMapContext.marker.position);
34827 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34828 _this.fireEvent('mapClick', this, event);
34832 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34833 _this.fireEvent('mapRightClick', this, event);
34837 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34838 _this.fireEvent('markerClick', this, event);
34842 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34843 _this.fireEvent('markerRightClick', this, event);
34847 this.setPosition(this.gMapContext.location);
34849 this.fireEvent('initial', this, this.gMapContext.location);
34852 initOverlayView: function()
34856 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34860 _this.fireEvent('OverlayViewDraw', _this);
34865 _this.fireEvent('OverlayViewOnAdd', _this);
34868 onRemove: function()
34870 _this.fireEvent('OverlayViewOnRemove', _this);
34873 show: function(cpx)
34875 _this.fireEvent('OverlayViewShow', _this, cpx);
34880 _this.fireEvent('OverlayViewHide', _this);
34886 fromLatLngToContainerPixel: function(event)
34888 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34891 isApplied: function()
34893 return this.getGmapContext() == false ? false : true;
34896 getGmapContext: function()
34898 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34901 GMapContext: function()
34903 var position = new google.maps.LatLng(this.latitude, this.longitude);
34905 var _map = new google.maps.Map(this.el.dom, {
34908 mapTypeId: this.mapTypeId,
34909 mapTypeControl: this.mapTypeControl,
34910 disableDoubleClickZoom: this.disableDoubleClickZoom,
34911 scrollwheel: this.scrollwheel,
34912 streetViewControl: this.streetViewControl,
34913 locationName: this.locationName,
34914 draggable: this.draggable,
34915 enableAutocomplete: this.enableAutocomplete,
34916 enableReverseGeocode: this.enableReverseGeocode
34919 var _marker = new google.maps.Marker({
34920 position: position,
34922 title: this.markerTitle,
34923 draggable: this.draggable
34930 location: position,
34931 radius: this.radius,
34932 locationName: this.locationName,
34933 addressComponents: {
34934 formatted_address: null,
34935 addressLine1: null,
34936 addressLine2: null,
34938 streetNumber: null,
34942 stateOrProvince: null
34945 domContainer: this.el.dom,
34946 geodecoder: new google.maps.Geocoder()
34950 drawCircle: function(center, radius, options)
34952 if (this.gMapContext.circle != null) {
34953 this.gMapContext.circle.setMap(null);
34957 options = Roo.apply({}, options, {
34958 strokeColor: "#0000FF",
34959 strokeOpacity: .35,
34961 fillColor: "#0000FF",
34965 options.map = this.gMapContext.map;
34966 options.radius = radius;
34967 options.center = center;
34968 this.gMapContext.circle = new google.maps.Circle(options);
34969 return this.gMapContext.circle;
34975 setPosition: function(location)
34977 this.gMapContext.location = location;
34978 this.gMapContext.marker.setPosition(location);
34979 this.gMapContext.map.panTo(location);
34980 this.drawCircle(location, this.gMapContext.radius, {});
34984 if (this.gMapContext.settings.enableReverseGeocode) {
34985 this.gMapContext.geodecoder.geocode({
34986 latLng: this.gMapContext.location
34987 }, function(results, status) {
34989 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34990 _this.gMapContext.locationName = results[0].formatted_address;
34991 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34993 _this.fireEvent('positionchanged', this, location);
35000 this.fireEvent('positionchanged', this, location);
35005 google.maps.event.trigger(this.gMapContext.map, "resize");
35007 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35009 this.fireEvent('resize', this);
35012 setPositionByLatLng: function(latitude, longitude)
35014 this.setPosition(new google.maps.LatLng(latitude, longitude));
35017 getCurrentPosition: function()
35020 latitude: this.gMapContext.location.lat(),
35021 longitude: this.gMapContext.location.lng()
35025 getAddressName: function()
35027 return this.gMapContext.locationName;
35030 getAddressComponents: function()
35032 return this.gMapContext.addressComponents;
35035 address_component_from_google_geocode: function(address_components)
35039 for (var i = 0; i < address_components.length; i++) {
35040 var component = address_components[i];
35041 if (component.types.indexOf("postal_code") >= 0) {
35042 result.postalCode = component.short_name;
35043 } else if (component.types.indexOf("street_number") >= 0) {
35044 result.streetNumber = component.short_name;
35045 } else if (component.types.indexOf("route") >= 0) {
35046 result.streetName = component.short_name;
35047 } else if (component.types.indexOf("neighborhood") >= 0) {
35048 result.city = component.short_name;
35049 } else if (component.types.indexOf("locality") >= 0) {
35050 result.city = component.short_name;
35051 } else if (component.types.indexOf("sublocality") >= 0) {
35052 result.district = component.short_name;
35053 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35054 result.stateOrProvince = component.short_name;
35055 } else if (component.types.indexOf("country") >= 0) {
35056 result.country = component.short_name;
35060 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35061 result.addressLine2 = "";
35065 setZoomLevel: function(zoom)
35067 this.gMapContext.map.setZoom(zoom);
35080 this.fireEvent('show', this);
35091 this.fireEvent('hide', this);
35096 Roo.apply(Roo.bootstrap.LocationPicker, {
35098 OverlayView : function(map, options)
35100 options = options || {};
35107 * @class Roo.bootstrap.Alert
35108 * @extends Roo.bootstrap.Component
35109 * Bootstrap Alert class - shows an alert area box
35111 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35112 Enter a valid email address
35115 * @cfg {String} title The title of alert
35116 * @cfg {String} html The content of alert
35117 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35118 * @cfg {String} fa font-awesomeicon
35119 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35120 * @cfg {Boolean} close true to show a x closer
35124 * Create a new alert
35125 * @param {Object} config The config object
35129 Roo.bootstrap.Alert = function(config){
35130 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35134 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35140 faicon: false, // BC
35144 getAutoCreate : function()
35156 style : this.close ? '' : 'display:none'
35160 cls : 'roo-alert-icon'
35165 cls : 'roo-alert-title',
35170 cls : 'roo-alert-text',
35177 cfg.cn[0].cls += ' fa ' + this.faicon;
35180 cfg.cn[0].cls += ' fa ' + this.fa;
35184 cfg.cls += ' alert-' + this.weight;
35190 initEvents: function()
35192 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35193 this.titleEl = this.el.select('.roo-alert-title',true).first();
35194 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35195 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35196 if (this.seconds > 0) {
35197 this.hide.defer(this.seconds, this);
35201 * Set the Title Message HTML
35202 * @param {String} html
35204 setTitle : function(str)
35206 this.titleEl.dom.innerHTML = str;
35210 * Set the Body Message HTML
35211 * @param {String} html
35213 setHtml : function(str)
35215 this.htmlEl.dom.innerHTML = str;
35218 * Set the Weight of the alert
35219 * @param {String} (success|info|warning|danger) weight
35222 setWeight : function(weight)
35225 this.el.removeClass('alert-' + this.weight);
35228 this.weight = weight;
35230 this.el.addClass('alert-' + this.weight);
35233 * Set the Icon of the alert
35234 * @param {String} see fontawsome names (name without the 'fa-' bit)
35236 setIcon : function(icon)
35239 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35242 this.faicon = icon;
35244 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35269 * @class Roo.bootstrap.UploadCropbox
35270 * @extends Roo.bootstrap.Component
35271 * Bootstrap UploadCropbox class
35272 * @cfg {String} emptyText show when image has been loaded
35273 * @cfg {String} rotateNotify show when image too small to rotate
35274 * @cfg {Number} errorTimeout default 3000
35275 * @cfg {Number} minWidth default 300
35276 * @cfg {Number} minHeight default 300
35277 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35278 * @cfg {Boolean} isDocument (true|false) default false
35279 * @cfg {String} url action url
35280 * @cfg {String} paramName default 'imageUpload'
35281 * @cfg {String} method default POST
35282 * @cfg {Boolean} loadMask (true|false) default true
35283 * @cfg {Boolean} loadingText default 'Loading...'
35286 * Create a new UploadCropbox
35287 * @param {Object} config The config object
35290 Roo.bootstrap.UploadCropbox = function(config){
35291 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35295 * @event beforeselectfile
35296 * Fire before select file
35297 * @param {Roo.bootstrap.UploadCropbox} this
35299 "beforeselectfile" : true,
35302 * Fire after initEvent
35303 * @param {Roo.bootstrap.UploadCropbox} this
35308 * Fire after initEvent
35309 * @param {Roo.bootstrap.UploadCropbox} this
35310 * @param {String} data
35315 * Fire when preparing the file data
35316 * @param {Roo.bootstrap.UploadCropbox} this
35317 * @param {Object} file
35322 * Fire when get exception
35323 * @param {Roo.bootstrap.UploadCropbox} this
35324 * @param {XMLHttpRequest} xhr
35326 "exception" : true,
35328 * @event beforeloadcanvas
35329 * Fire before load the canvas
35330 * @param {Roo.bootstrap.UploadCropbox} this
35331 * @param {String} src
35333 "beforeloadcanvas" : true,
35336 * Fire when trash image
35337 * @param {Roo.bootstrap.UploadCropbox} this
35342 * Fire when download the image
35343 * @param {Roo.bootstrap.UploadCropbox} this
35347 * @event footerbuttonclick
35348 * Fire when footerbuttonclick
35349 * @param {Roo.bootstrap.UploadCropbox} this
35350 * @param {String} type
35352 "footerbuttonclick" : true,
35356 * @param {Roo.bootstrap.UploadCropbox} this
35361 * Fire when rotate the image
35362 * @param {Roo.bootstrap.UploadCropbox} this
35363 * @param {String} pos
35368 * Fire when inspect the file
35369 * @param {Roo.bootstrap.UploadCropbox} this
35370 * @param {Object} file
35375 * Fire when xhr upload the file
35376 * @param {Roo.bootstrap.UploadCropbox} this
35377 * @param {Object} data
35382 * Fire when arrange the file data
35383 * @param {Roo.bootstrap.UploadCropbox} this
35384 * @param {Object} formData
35389 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35392 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35394 emptyText : 'Click to upload image',
35395 rotateNotify : 'Image is too small to rotate',
35396 errorTimeout : 3000,
35410 cropType : 'image/jpeg',
35412 canvasLoaded : false,
35413 isDocument : false,
35415 paramName : 'imageUpload',
35417 loadingText : 'Loading...',
35420 getAutoCreate : function()
35424 cls : 'roo-upload-cropbox',
35428 cls : 'roo-upload-cropbox-selector',
35433 cls : 'roo-upload-cropbox-body',
35434 style : 'cursor:pointer',
35438 cls : 'roo-upload-cropbox-preview'
35442 cls : 'roo-upload-cropbox-thumb'
35446 cls : 'roo-upload-cropbox-empty-notify',
35447 html : this.emptyText
35451 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35452 html : this.rotateNotify
35458 cls : 'roo-upload-cropbox-footer',
35461 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35471 onRender : function(ct, position)
35473 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35475 if (this.buttons.length) {
35477 Roo.each(this.buttons, function(bb) {
35479 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35481 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35487 this.maskEl = this.el;
35491 initEvents : function()
35493 this.urlAPI = (window.createObjectURL && window) ||
35494 (window.URL && URL.revokeObjectURL && URL) ||
35495 (window.webkitURL && webkitURL);
35497 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35498 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35500 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35501 this.selectorEl.hide();
35503 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35504 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35506 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35507 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35508 this.thumbEl.hide();
35510 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35511 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35513 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35514 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35515 this.errorEl.hide();
35517 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35518 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35519 this.footerEl.hide();
35521 this.setThumbBoxSize();
35527 this.fireEvent('initial', this);
35534 window.addEventListener("resize", function() { _this.resize(); } );
35536 this.bodyEl.on('click', this.beforeSelectFile, this);
35539 this.bodyEl.on('touchstart', this.onTouchStart, this);
35540 this.bodyEl.on('touchmove', this.onTouchMove, this);
35541 this.bodyEl.on('touchend', this.onTouchEnd, this);
35545 this.bodyEl.on('mousedown', this.onMouseDown, this);
35546 this.bodyEl.on('mousemove', this.onMouseMove, this);
35547 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35548 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35549 Roo.get(document).on('mouseup', this.onMouseUp, this);
35552 this.selectorEl.on('change', this.onFileSelected, this);
35558 this.baseScale = 1;
35560 this.baseRotate = 1;
35561 this.dragable = false;
35562 this.pinching = false;
35565 this.cropData = false;
35566 this.notifyEl.dom.innerHTML = this.emptyText;
35568 this.selectorEl.dom.value = '';
35572 resize : function()
35574 if(this.fireEvent('resize', this) != false){
35575 this.setThumbBoxPosition();
35576 this.setCanvasPosition();
35580 onFooterButtonClick : function(e, el, o, type)
35583 case 'rotate-left' :
35584 this.onRotateLeft(e);
35586 case 'rotate-right' :
35587 this.onRotateRight(e);
35590 this.beforeSelectFile(e);
35605 this.fireEvent('footerbuttonclick', this, type);
35608 beforeSelectFile : function(e)
35610 e.preventDefault();
35612 if(this.fireEvent('beforeselectfile', this) != false){
35613 this.selectorEl.dom.click();
35617 onFileSelected : function(e)
35619 e.preventDefault();
35621 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35625 var file = this.selectorEl.dom.files[0];
35627 if(this.fireEvent('inspect', this, file) != false){
35628 this.prepare(file);
35633 trash : function(e)
35635 this.fireEvent('trash', this);
35638 download : function(e)
35640 this.fireEvent('download', this);
35643 loadCanvas : function(src)
35645 if(this.fireEvent('beforeloadcanvas', this, src) != false){
35649 this.imageEl = document.createElement('img');
35653 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35655 this.imageEl.src = src;
35659 onLoadCanvas : function()
35661 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35662 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35664 this.bodyEl.un('click', this.beforeSelectFile, this);
35666 this.notifyEl.hide();
35667 this.thumbEl.show();
35668 this.footerEl.show();
35670 this.baseRotateLevel();
35672 if(this.isDocument){
35673 this.setThumbBoxSize();
35676 this.setThumbBoxPosition();
35678 this.baseScaleLevel();
35684 this.canvasLoaded = true;
35687 this.maskEl.unmask();
35692 setCanvasPosition : function()
35694 if(!this.canvasEl){
35698 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35699 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35701 this.previewEl.setLeft(pw);
35702 this.previewEl.setTop(ph);
35706 onMouseDown : function(e)
35710 this.dragable = true;
35711 this.pinching = false;
35713 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35714 this.dragable = false;
35718 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35719 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35723 onMouseMove : function(e)
35727 if(!this.canvasLoaded){
35731 if (!this.dragable){
35735 var minX = Math.ceil(this.thumbEl.getLeft(true));
35736 var minY = Math.ceil(this.thumbEl.getTop(true));
35738 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35739 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35741 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35742 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35744 x = x - this.mouseX;
35745 y = y - this.mouseY;
35747 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35748 var bgY = Math.ceil(y + this.previewEl.getTop(true));
35750 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35751 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35753 this.previewEl.setLeft(bgX);
35754 this.previewEl.setTop(bgY);
35756 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35757 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35760 onMouseUp : function(e)
35764 this.dragable = false;
35767 onMouseWheel : function(e)
35771 this.startScale = this.scale;
35773 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35775 if(!this.zoomable()){
35776 this.scale = this.startScale;
35785 zoomable : function()
35787 var minScale = this.thumbEl.getWidth() / this.minWidth;
35789 if(this.minWidth < this.minHeight){
35790 minScale = this.thumbEl.getHeight() / this.minHeight;
35793 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35794 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35798 (this.rotate == 0 || this.rotate == 180) &&
35800 width > this.imageEl.OriginWidth ||
35801 height > this.imageEl.OriginHeight ||
35802 (width < this.minWidth && height < this.minHeight)
35810 (this.rotate == 90 || this.rotate == 270) &&
35812 width > this.imageEl.OriginWidth ||
35813 height > this.imageEl.OriginHeight ||
35814 (width < this.minHeight && height < this.minWidth)
35821 !this.isDocument &&
35822 (this.rotate == 0 || this.rotate == 180) &&
35824 width < this.minWidth ||
35825 width > this.imageEl.OriginWidth ||
35826 height < this.minHeight ||
35827 height > this.imageEl.OriginHeight
35834 !this.isDocument &&
35835 (this.rotate == 90 || this.rotate == 270) &&
35837 width < this.minHeight ||
35838 width > this.imageEl.OriginWidth ||
35839 height < this.minWidth ||
35840 height > this.imageEl.OriginHeight
35850 onRotateLeft : function(e)
35852 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35854 var minScale = this.thumbEl.getWidth() / this.minWidth;
35856 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35857 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35859 this.startScale = this.scale;
35861 while (this.getScaleLevel() < minScale){
35863 this.scale = this.scale + 1;
35865 if(!this.zoomable()){
35870 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35871 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35876 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35883 this.scale = this.startScale;
35885 this.onRotateFail();
35890 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35892 if(this.isDocument){
35893 this.setThumbBoxSize();
35894 this.setThumbBoxPosition();
35895 this.setCanvasPosition();
35900 this.fireEvent('rotate', this, 'left');
35904 onRotateRight : function(e)
35906 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35908 var minScale = this.thumbEl.getWidth() / this.minWidth;
35910 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35911 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35913 this.startScale = this.scale;
35915 while (this.getScaleLevel() < minScale){
35917 this.scale = this.scale + 1;
35919 if(!this.zoomable()){
35924 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35925 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35930 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35937 this.scale = this.startScale;
35939 this.onRotateFail();
35944 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35946 if(this.isDocument){
35947 this.setThumbBoxSize();
35948 this.setThumbBoxPosition();
35949 this.setCanvasPosition();
35954 this.fireEvent('rotate', this, 'right');
35957 onRotateFail : function()
35959 this.errorEl.show(true);
35963 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35968 this.previewEl.dom.innerHTML = '';
35970 var canvasEl = document.createElement("canvas");
35972 var contextEl = canvasEl.getContext("2d");
35974 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35975 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35976 var center = this.imageEl.OriginWidth / 2;
35978 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35979 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35980 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35981 center = this.imageEl.OriginHeight / 2;
35984 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35986 contextEl.translate(center, center);
35987 contextEl.rotate(this.rotate * Math.PI / 180);
35989 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35991 this.canvasEl = document.createElement("canvas");
35993 this.contextEl = this.canvasEl.getContext("2d");
35995 switch (this.rotate) {
35998 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35999 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36001 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36006 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36007 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36009 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36010 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);
36014 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36019 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36020 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36022 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36023 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);
36027 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);
36032 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36033 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36035 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36036 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36040 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);
36047 this.previewEl.appendChild(this.canvasEl);
36049 this.setCanvasPosition();
36054 if(!this.canvasLoaded){
36058 var imageCanvas = document.createElement("canvas");
36060 var imageContext = imageCanvas.getContext("2d");
36062 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36063 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36065 var center = imageCanvas.width / 2;
36067 imageContext.translate(center, center);
36069 imageContext.rotate(this.rotate * Math.PI / 180);
36071 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36073 var canvas = document.createElement("canvas");
36075 var context = canvas.getContext("2d");
36077 canvas.width = this.minWidth;
36078 canvas.height = this.minHeight;
36080 switch (this.rotate) {
36083 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36084 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36086 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36087 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36089 var targetWidth = this.minWidth - 2 * x;
36090 var targetHeight = this.minHeight - 2 * y;
36094 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36095 scale = targetWidth / width;
36098 if(x > 0 && y == 0){
36099 scale = targetHeight / height;
36102 if(x > 0 && y > 0){
36103 scale = targetWidth / width;
36105 if(width < height){
36106 scale = targetHeight / height;
36110 context.scale(scale, scale);
36112 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36113 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36115 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36116 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36118 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36123 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36124 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36126 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36127 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36129 var targetWidth = this.minWidth - 2 * x;
36130 var targetHeight = this.minHeight - 2 * y;
36134 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36135 scale = targetWidth / width;
36138 if(x > 0 && y == 0){
36139 scale = targetHeight / height;
36142 if(x > 0 && y > 0){
36143 scale = targetWidth / width;
36145 if(width < height){
36146 scale = targetHeight / height;
36150 context.scale(scale, scale);
36152 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36153 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36155 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36156 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36158 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36160 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36165 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36166 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36168 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36169 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36171 var targetWidth = this.minWidth - 2 * x;
36172 var targetHeight = this.minHeight - 2 * y;
36176 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36177 scale = targetWidth / width;
36180 if(x > 0 && y == 0){
36181 scale = targetHeight / height;
36184 if(x > 0 && y > 0){
36185 scale = targetWidth / width;
36187 if(width < height){
36188 scale = targetHeight / height;
36192 context.scale(scale, scale);
36194 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36195 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36197 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36198 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36200 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36201 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36203 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36208 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36209 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36211 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36212 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36214 var targetWidth = this.minWidth - 2 * x;
36215 var targetHeight = this.minHeight - 2 * y;
36219 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36220 scale = targetWidth / width;
36223 if(x > 0 && y == 0){
36224 scale = targetHeight / height;
36227 if(x > 0 && y > 0){
36228 scale = targetWidth / width;
36230 if(width < height){
36231 scale = targetHeight / height;
36235 context.scale(scale, scale);
36237 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36238 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36240 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36241 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36243 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36245 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36252 this.cropData = canvas.toDataURL(this.cropType);
36254 if(this.fireEvent('crop', this, this.cropData) !== false){
36255 this.process(this.file, this.cropData);
36262 setThumbBoxSize : function()
36266 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36267 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36268 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36270 this.minWidth = width;
36271 this.minHeight = height;
36273 if(this.rotate == 90 || this.rotate == 270){
36274 this.minWidth = height;
36275 this.minHeight = width;
36280 width = Math.ceil(this.minWidth * height / this.minHeight);
36282 if(this.minWidth > this.minHeight){
36284 height = Math.ceil(this.minHeight * width / this.minWidth);
36287 this.thumbEl.setStyle({
36288 width : width + 'px',
36289 height : height + 'px'
36296 setThumbBoxPosition : function()
36298 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36299 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36301 this.thumbEl.setLeft(x);
36302 this.thumbEl.setTop(y);
36306 baseRotateLevel : function()
36308 this.baseRotate = 1;
36311 typeof(this.exif) != 'undefined' &&
36312 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36313 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36315 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36318 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36322 baseScaleLevel : function()
36326 if(this.isDocument){
36328 if(this.baseRotate == 6 || this.baseRotate == 8){
36330 height = this.thumbEl.getHeight();
36331 this.baseScale = height / this.imageEl.OriginWidth;
36333 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36334 width = this.thumbEl.getWidth();
36335 this.baseScale = width / this.imageEl.OriginHeight;
36341 height = this.thumbEl.getHeight();
36342 this.baseScale = height / this.imageEl.OriginHeight;
36344 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36345 width = this.thumbEl.getWidth();
36346 this.baseScale = width / this.imageEl.OriginWidth;
36352 if(this.baseRotate == 6 || this.baseRotate == 8){
36354 width = this.thumbEl.getHeight();
36355 this.baseScale = width / this.imageEl.OriginHeight;
36357 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36358 height = this.thumbEl.getWidth();
36359 this.baseScale = height / this.imageEl.OriginHeight;
36362 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36363 height = this.thumbEl.getWidth();
36364 this.baseScale = height / this.imageEl.OriginHeight;
36366 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36367 width = this.thumbEl.getHeight();
36368 this.baseScale = width / this.imageEl.OriginWidth;
36375 width = this.thumbEl.getWidth();
36376 this.baseScale = width / this.imageEl.OriginWidth;
36378 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36379 height = this.thumbEl.getHeight();
36380 this.baseScale = height / this.imageEl.OriginHeight;
36383 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36385 height = this.thumbEl.getHeight();
36386 this.baseScale = height / this.imageEl.OriginHeight;
36388 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36389 width = this.thumbEl.getWidth();
36390 this.baseScale = width / this.imageEl.OriginWidth;
36398 getScaleLevel : function()
36400 return this.baseScale * Math.pow(1.1, this.scale);
36403 onTouchStart : function(e)
36405 if(!this.canvasLoaded){
36406 this.beforeSelectFile(e);
36410 var touches = e.browserEvent.touches;
36416 if(touches.length == 1){
36417 this.onMouseDown(e);
36421 if(touches.length != 2){
36427 for(var i = 0, finger; finger = touches[i]; i++){
36428 coords.push(finger.pageX, finger.pageY);
36431 var x = Math.pow(coords[0] - coords[2], 2);
36432 var y = Math.pow(coords[1] - coords[3], 2);
36434 this.startDistance = Math.sqrt(x + y);
36436 this.startScale = this.scale;
36438 this.pinching = true;
36439 this.dragable = false;
36443 onTouchMove : function(e)
36445 if(!this.pinching && !this.dragable){
36449 var touches = e.browserEvent.touches;
36456 this.onMouseMove(e);
36462 for(var i = 0, finger; finger = touches[i]; i++){
36463 coords.push(finger.pageX, finger.pageY);
36466 var x = Math.pow(coords[0] - coords[2], 2);
36467 var y = Math.pow(coords[1] - coords[3], 2);
36469 this.endDistance = Math.sqrt(x + y);
36471 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36473 if(!this.zoomable()){
36474 this.scale = this.startScale;
36482 onTouchEnd : function(e)
36484 this.pinching = false;
36485 this.dragable = false;
36489 process : function(file, crop)
36492 this.maskEl.mask(this.loadingText);
36495 this.xhr = new XMLHttpRequest();
36497 file.xhr = this.xhr;
36499 this.xhr.open(this.method, this.url, true);
36502 "Accept": "application/json",
36503 "Cache-Control": "no-cache",
36504 "X-Requested-With": "XMLHttpRequest"
36507 for (var headerName in headers) {
36508 var headerValue = headers[headerName];
36510 this.xhr.setRequestHeader(headerName, headerValue);
36516 this.xhr.onload = function()
36518 _this.xhrOnLoad(_this.xhr);
36521 this.xhr.onerror = function()
36523 _this.xhrOnError(_this.xhr);
36526 var formData = new FormData();
36528 formData.append('returnHTML', 'NO');
36531 formData.append('crop', crop);
36534 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36535 formData.append(this.paramName, file, file.name);
36538 if(typeof(file.filename) != 'undefined'){
36539 formData.append('filename', file.filename);
36542 if(typeof(file.mimetype) != 'undefined'){
36543 formData.append('mimetype', file.mimetype);
36546 if(this.fireEvent('arrange', this, formData) != false){
36547 this.xhr.send(formData);
36551 xhrOnLoad : function(xhr)
36554 this.maskEl.unmask();
36557 if (xhr.readyState !== 4) {
36558 this.fireEvent('exception', this, xhr);
36562 var response = Roo.decode(xhr.responseText);
36564 if(!response.success){
36565 this.fireEvent('exception', this, xhr);
36569 var response = Roo.decode(xhr.responseText);
36571 this.fireEvent('upload', this, response);
36575 xhrOnError : function()
36578 this.maskEl.unmask();
36581 Roo.log('xhr on error');
36583 var response = Roo.decode(xhr.responseText);
36589 prepare : function(file)
36592 this.maskEl.mask(this.loadingText);
36598 if(typeof(file) === 'string'){
36599 this.loadCanvas(file);
36603 if(!file || !this.urlAPI){
36608 this.cropType = file.type;
36612 if(this.fireEvent('prepare', this, this.file) != false){
36614 var reader = new FileReader();
36616 reader.onload = function (e) {
36617 if (e.target.error) {
36618 Roo.log(e.target.error);
36622 var buffer = e.target.result,
36623 dataView = new DataView(buffer),
36625 maxOffset = dataView.byteLength - 4,
36629 if (dataView.getUint16(0) === 0xffd8) {
36630 while (offset < maxOffset) {
36631 markerBytes = dataView.getUint16(offset);
36633 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36634 markerLength = dataView.getUint16(offset + 2) + 2;
36635 if (offset + markerLength > dataView.byteLength) {
36636 Roo.log('Invalid meta data: Invalid segment size.');
36640 if(markerBytes == 0xffe1){
36641 _this.parseExifData(
36648 offset += markerLength;
36658 var url = _this.urlAPI.createObjectURL(_this.file);
36660 _this.loadCanvas(url);
36665 reader.readAsArrayBuffer(this.file);
36671 parseExifData : function(dataView, offset, length)
36673 var tiffOffset = offset + 10,
36677 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36678 // No Exif data, might be XMP data instead
36682 // Check for the ASCII code for "Exif" (0x45786966):
36683 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36684 // No Exif data, might be XMP data instead
36687 if (tiffOffset + 8 > dataView.byteLength) {
36688 Roo.log('Invalid Exif data: Invalid segment size.');
36691 // Check for the two null bytes:
36692 if (dataView.getUint16(offset + 8) !== 0x0000) {
36693 Roo.log('Invalid Exif data: Missing byte alignment offset.');
36696 // Check the byte alignment:
36697 switch (dataView.getUint16(tiffOffset)) {
36699 littleEndian = true;
36702 littleEndian = false;
36705 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36708 // Check for the TIFF tag marker (0x002A):
36709 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36710 Roo.log('Invalid Exif data: Missing TIFF marker.');
36713 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36714 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36716 this.parseExifTags(
36719 tiffOffset + dirOffset,
36724 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36729 if (dirOffset + 6 > dataView.byteLength) {
36730 Roo.log('Invalid Exif data: Invalid directory offset.');
36733 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36734 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36735 if (dirEndOffset + 4 > dataView.byteLength) {
36736 Roo.log('Invalid Exif data: Invalid directory size.');
36739 for (i = 0; i < tagsNumber; i += 1) {
36743 dirOffset + 2 + 12 * i, // tag offset
36747 // Return the offset to the next directory:
36748 return dataView.getUint32(dirEndOffset, littleEndian);
36751 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
36753 var tag = dataView.getUint16(offset, littleEndian);
36755 this.exif[tag] = this.getExifValue(
36759 dataView.getUint16(offset + 2, littleEndian), // tag type
36760 dataView.getUint32(offset + 4, littleEndian), // tag length
36765 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36767 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36776 Roo.log('Invalid Exif data: Invalid tag type.');
36780 tagSize = tagType.size * length;
36781 // Determine if the value is contained in the dataOffset bytes,
36782 // or if the value at the dataOffset is a pointer to the actual data:
36783 dataOffset = tagSize > 4 ?
36784 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36785 if (dataOffset + tagSize > dataView.byteLength) {
36786 Roo.log('Invalid Exif data: Invalid data offset.');
36789 if (length === 1) {
36790 return tagType.getValue(dataView, dataOffset, littleEndian);
36793 for (i = 0; i < length; i += 1) {
36794 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36797 if (tagType.ascii) {
36799 // Concatenate the chars:
36800 for (i = 0; i < values.length; i += 1) {
36802 // Ignore the terminating NULL byte(s):
36803 if (c === '\u0000') {
36815 Roo.apply(Roo.bootstrap.UploadCropbox, {
36817 'Orientation': 0x0112
36821 1: 0, //'top-left',
36823 3: 180, //'bottom-right',
36824 // 4: 'bottom-left',
36826 6: 90, //'right-top',
36827 // 7: 'right-bottom',
36828 8: 270 //'left-bottom'
36832 // byte, 8-bit unsigned int:
36834 getValue: function (dataView, dataOffset) {
36835 return dataView.getUint8(dataOffset);
36839 // ascii, 8-bit byte:
36841 getValue: function (dataView, dataOffset) {
36842 return String.fromCharCode(dataView.getUint8(dataOffset));
36847 // short, 16 bit int:
36849 getValue: function (dataView, dataOffset, littleEndian) {
36850 return dataView.getUint16(dataOffset, littleEndian);
36854 // long, 32 bit int:
36856 getValue: function (dataView, dataOffset, littleEndian) {
36857 return dataView.getUint32(dataOffset, littleEndian);
36861 // rational = two long values, first is numerator, second is denominator:
36863 getValue: function (dataView, dataOffset, littleEndian) {
36864 return dataView.getUint32(dataOffset, littleEndian) /
36865 dataView.getUint32(dataOffset + 4, littleEndian);
36869 // slong, 32 bit signed int:
36871 getValue: function (dataView, dataOffset, littleEndian) {
36872 return dataView.getInt32(dataOffset, littleEndian);
36876 // srational, two slongs, first is numerator, second is denominator:
36878 getValue: function (dataView, dataOffset, littleEndian) {
36879 return dataView.getInt32(dataOffset, littleEndian) /
36880 dataView.getInt32(dataOffset + 4, littleEndian);
36890 cls : 'btn-group roo-upload-cropbox-rotate-left',
36891 action : 'rotate-left',
36895 cls : 'btn btn-default',
36896 html : '<i class="fa fa-undo"></i>'
36902 cls : 'btn-group roo-upload-cropbox-picture',
36903 action : 'picture',
36907 cls : 'btn btn-default',
36908 html : '<i class="fa fa-picture-o"></i>'
36914 cls : 'btn-group roo-upload-cropbox-rotate-right',
36915 action : 'rotate-right',
36919 cls : 'btn btn-default',
36920 html : '<i class="fa fa-repeat"></i>'
36928 cls : 'btn-group roo-upload-cropbox-rotate-left',
36929 action : 'rotate-left',
36933 cls : 'btn btn-default',
36934 html : '<i class="fa fa-undo"></i>'
36940 cls : 'btn-group roo-upload-cropbox-download',
36941 action : 'download',
36945 cls : 'btn btn-default',
36946 html : '<i class="fa fa-download"></i>'
36952 cls : 'btn-group roo-upload-cropbox-crop',
36957 cls : 'btn btn-default',
36958 html : '<i class="fa fa-crop"></i>'
36964 cls : 'btn-group roo-upload-cropbox-trash',
36969 cls : 'btn btn-default',
36970 html : '<i class="fa fa-trash"></i>'
36976 cls : 'btn-group roo-upload-cropbox-rotate-right',
36977 action : 'rotate-right',
36981 cls : 'btn btn-default',
36982 html : '<i class="fa fa-repeat"></i>'
36990 cls : 'btn-group roo-upload-cropbox-rotate-left',
36991 action : 'rotate-left',
36995 cls : 'btn btn-default',
36996 html : '<i class="fa fa-undo"></i>'
37002 cls : 'btn-group roo-upload-cropbox-rotate-right',
37003 action : 'rotate-right',
37007 cls : 'btn btn-default',
37008 html : '<i class="fa fa-repeat"></i>'
37021 * @class Roo.bootstrap.DocumentManager
37022 * @extends Roo.bootstrap.Component
37023 * Bootstrap DocumentManager class
37024 * @cfg {String} paramName default 'imageUpload'
37025 * @cfg {String} toolTipName default 'filename'
37026 * @cfg {String} method default POST
37027 * @cfg {String} url action url
37028 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37029 * @cfg {Boolean} multiple multiple upload default true
37030 * @cfg {Number} thumbSize default 300
37031 * @cfg {String} fieldLabel
37032 * @cfg {Number} labelWidth default 4
37033 * @cfg {String} labelAlign (left|top) default left
37034 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37035 * @cfg {Number} labellg set the width of label (1-12)
37036 * @cfg {Number} labelmd set the width of label (1-12)
37037 * @cfg {Number} labelsm set the width of label (1-12)
37038 * @cfg {Number} labelxs set the width of label (1-12)
37041 * Create a new DocumentManager
37042 * @param {Object} config The config object
37045 Roo.bootstrap.DocumentManager = function(config){
37046 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37049 this.delegates = [];
37054 * Fire when initial the DocumentManager
37055 * @param {Roo.bootstrap.DocumentManager} this
37060 * inspect selected file
37061 * @param {Roo.bootstrap.DocumentManager} this
37062 * @param {File} file
37067 * Fire when xhr load exception
37068 * @param {Roo.bootstrap.DocumentManager} this
37069 * @param {XMLHttpRequest} xhr
37071 "exception" : true,
37073 * @event afterupload
37074 * Fire when xhr load exception
37075 * @param {Roo.bootstrap.DocumentManager} this
37076 * @param {XMLHttpRequest} xhr
37078 "afterupload" : true,
37081 * prepare the form data
37082 * @param {Roo.bootstrap.DocumentManager} this
37083 * @param {Object} formData
37088 * Fire when remove the file
37089 * @param {Roo.bootstrap.DocumentManager} this
37090 * @param {Object} file
37095 * Fire after refresh the file
37096 * @param {Roo.bootstrap.DocumentManager} this
37101 * Fire after click the image
37102 * @param {Roo.bootstrap.DocumentManager} this
37103 * @param {Object} file
37108 * Fire when upload a image and editable set to true
37109 * @param {Roo.bootstrap.DocumentManager} this
37110 * @param {Object} file
37114 * @event beforeselectfile
37115 * Fire before select file
37116 * @param {Roo.bootstrap.DocumentManager} this
37118 "beforeselectfile" : true,
37121 * Fire before process file
37122 * @param {Roo.bootstrap.DocumentManager} this
37123 * @param {Object} file
37127 * @event previewrendered
37128 * Fire when preview rendered
37129 * @param {Roo.bootstrap.DocumentManager} this
37130 * @param {Object} file
37132 "previewrendered" : true,
37135 "previewResize" : true
37140 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37149 paramName : 'imageUpload',
37150 toolTipName : 'filename',
37153 labelAlign : 'left',
37163 getAutoCreate : function()
37165 var managerWidget = {
37167 cls : 'roo-document-manager',
37171 cls : 'roo-document-manager-selector',
37176 cls : 'roo-document-manager-uploader',
37180 cls : 'roo-document-manager-upload-btn',
37181 html : '<i class="fa fa-plus"></i>'
37192 cls : 'column col-md-12',
37197 if(this.fieldLabel.length){
37202 cls : 'column col-md-12',
37203 html : this.fieldLabel
37207 cls : 'column col-md-12',
37212 if(this.labelAlign == 'left'){
37217 html : this.fieldLabel
37226 if(this.labelWidth > 12){
37227 content[0].style = "width: " + this.labelWidth + 'px';
37230 if(this.labelWidth < 13 && this.labelmd == 0){
37231 this.labelmd = this.labelWidth;
37234 if(this.labellg > 0){
37235 content[0].cls += ' col-lg-' + this.labellg;
37236 content[1].cls += ' col-lg-' + (12 - this.labellg);
37239 if(this.labelmd > 0){
37240 content[0].cls += ' col-md-' + this.labelmd;
37241 content[1].cls += ' col-md-' + (12 - this.labelmd);
37244 if(this.labelsm > 0){
37245 content[0].cls += ' col-sm-' + this.labelsm;
37246 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37249 if(this.labelxs > 0){
37250 content[0].cls += ' col-xs-' + this.labelxs;
37251 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37259 cls : 'row clearfix',
37267 initEvents : function()
37269 this.managerEl = this.el.select('.roo-document-manager', true).first();
37270 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37272 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37273 this.selectorEl.hide();
37276 this.selectorEl.attr('multiple', 'multiple');
37279 this.selectorEl.on('change', this.onFileSelected, this);
37281 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37282 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37284 this.uploader.on('click', this.onUploaderClick, this);
37286 this.renderProgressDialog();
37290 window.addEventListener("resize", function() { _this.refresh(); } );
37292 this.fireEvent('initial', this);
37295 renderProgressDialog : function()
37299 this.progressDialog = new Roo.bootstrap.Modal({
37300 cls : 'roo-document-manager-progress-dialog',
37301 allow_close : false,
37312 btnclick : function() {
37313 _this.uploadCancel();
37319 this.progressDialog.render(Roo.get(document.body));
37321 this.progress = new Roo.bootstrap.Progress({
37322 cls : 'roo-document-manager-progress',
37327 this.progress.render(this.progressDialog.getChildContainer());
37329 this.progressBar = new Roo.bootstrap.ProgressBar({
37330 cls : 'roo-document-manager-progress-bar',
37333 aria_valuemax : 12,
37337 this.progressBar.render(this.progress.getChildContainer());
37340 onUploaderClick : function(e)
37342 e.preventDefault();
37344 if(this.fireEvent('beforeselectfile', this) != false){
37345 this.selectorEl.dom.click();
37350 onFileSelected : function(e)
37352 e.preventDefault();
37354 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37358 Roo.each(this.selectorEl.dom.files, function(file){
37359 if(this.fireEvent('inspect', this, file) != false){
37360 this.files.push(file);
37370 this.selectorEl.dom.value = '';
37372 if(!this.files || !this.files.length){
37376 if(this.boxes > 0 && this.files.length > this.boxes){
37377 this.files = this.files.slice(0, this.boxes);
37380 this.uploader.show();
37382 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37383 this.uploader.hide();
37392 Roo.each(this.files, function(file){
37394 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37395 var f = this.renderPreview(file);
37400 if(file.type.indexOf('image') != -1){
37401 this.delegates.push(
37403 _this.process(file);
37404 }).createDelegate(this)
37412 _this.process(file);
37413 }).createDelegate(this)
37418 this.files = files;
37420 this.delegates = this.delegates.concat(docs);
37422 if(!this.delegates.length){
37427 this.progressBar.aria_valuemax = this.delegates.length;
37434 arrange : function()
37436 if(!this.delegates.length){
37437 this.progressDialog.hide();
37442 var delegate = this.delegates.shift();
37444 this.progressDialog.show();
37446 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37448 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37453 refresh : function()
37455 this.uploader.show();
37457 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37458 this.uploader.hide();
37461 Roo.isTouch ? this.closable(false) : this.closable(true);
37463 this.fireEvent('refresh', this);
37466 onRemove : function(e, el, o)
37468 e.preventDefault();
37470 this.fireEvent('remove', this, o);
37474 remove : function(o)
37478 Roo.each(this.files, function(file){
37479 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37488 this.files = files;
37495 Roo.each(this.files, function(file){
37500 file.target.remove();
37509 onClick : function(e, el, o)
37511 e.preventDefault();
37513 this.fireEvent('click', this, o);
37517 closable : function(closable)
37519 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37521 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37533 xhrOnLoad : function(xhr)
37535 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37539 if (xhr.readyState !== 4) {
37541 this.fireEvent('exception', this, xhr);
37545 var response = Roo.decode(xhr.responseText);
37547 if(!response.success){
37549 this.fireEvent('exception', this, xhr);
37553 var file = this.renderPreview(response.data);
37555 this.files.push(file);
37559 this.fireEvent('afterupload', this, xhr);
37563 xhrOnError : function(xhr)
37565 Roo.log('xhr on error');
37567 var response = Roo.decode(xhr.responseText);
37574 process : function(file)
37576 if(this.fireEvent('process', this, file) !== false){
37577 if(this.editable && file.type.indexOf('image') != -1){
37578 this.fireEvent('edit', this, file);
37582 this.uploadStart(file, false);
37589 uploadStart : function(file, crop)
37591 this.xhr = new XMLHttpRequest();
37593 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37598 file.xhr = this.xhr;
37600 this.managerEl.createChild({
37602 cls : 'roo-document-manager-loading',
37606 tooltip : file.name,
37607 cls : 'roo-document-manager-thumb',
37608 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37614 this.xhr.open(this.method, this.url, true);
37617 "Accept": "application/json",
37618 "Cache-Control": "no-cache",
37619 "X-Requested-With": "XMLHttpRequest"
37622 for (var headerName in headers) {
37623 var headerValue = headers[headerName];
37625 this.xhr.setRequestHeader(headerName, headerValue);
37631 this.xhr.onload = function()
37633 _this.xhrOnLoad(_this.xhr);
37636 this.xhr.onerror = function()
37638 _this.xhrOnError(_this.xhr);
37641 var formData = new FormData();
37643 formData.append('returnHTML', 'NO');
37646 formData.append('crop', crop);
37649 formData.append(this.paramName, file, file.name);
37656 if(this.fireEvent('prepare', this, formData, options) != false){
37658 if(options.manually){
37662 this.xhr.send(formData);
37666 this.uploadCancel();
37669 uploadCancel : function()
37675 this.delegates = [];
37677 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37684 renderPreview : function(file)
37686 if(typeof(file.target) != 'undefined' && file.target){
37690 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37692 var previewEl = this.managerEl.createChild({
37694 cls : 'roo-document-manager-preview',
37698 tooltip : file[this.toolTipName],
37699 cls : 'roo-document-manager-thumb',
37700 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37705 html : '<i class="fa fa-times-circle"></i>'
37710 var close = previewEl.select('button.close', true).first();
37712 close.on('click', this.onRemove, this, file);
37714 file.target = previewEl;
37716 var image = previewEl.select('img', true).first();
37720 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37722 image.on('click', this.onClick, this, file);
37724 this.fireEvent('previewrendered', this, file);
37730 onPreviewLoad : function(file, image)
37732 if(typeof(file.target) == 'undefined' || !file.target){
37736 var width = image.dom.naturalWidth || image.dom.width;
37737 var height = image.dom.naturalHeight || image.dom.height;
37739 if(!this.previewResize) {
37743 if(width > height){
37744 file.target.addClass('wide');
37748 file.target.addClass('tall');
37753 uploadFromSource : function(file, crop)
37755 this.xhr = new XMLHttpRequest();
37757 this.managerEl.createChild({
37759 cls : 'roo-document-manager-loading',
37763 tooltip : file.name,
37764 cls : 'roo-document-manager-thumb',
37765 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37771 this.xhr.open(this.method, this.url, true);
37774 "Accept": "application/json",
37775 "Cache-Control": "no-cache",
37776 "X-Requested-With": "XMLHttpRequest"
37779 for (var headerName in headers) {
37780 var headerValue = headers[headerName];
37782 this.xhr.setRequestHeader(headerName, headerValue);
37788 this.xhr.onload = function()
37790 _this.xhrOnLoad(_this.xhr);
37793 this.xhr.onerror = function()
37795 _this.xhrOnError(_this.xhr);
37798 var formData = new FormData();
37800 formData.append('returnHTML', 'NO');
37802 formData.append('crop', crop);
37804 if(typeof(file.filename) != 'undefined'){
37805 formData.append('filename', file.filename);
37808 if(typeof(file.mimetype) != 'undefined'){
37809 formData.append('mimetype', file.mimetype);
37814 if(this.fireEvent('prepare', this, formData) != false){
37815 this.xhr.send(formData);
37825 * @class Roo.bootstrap.DocumentViewer
37826 * @extends Roo.bootstrap.Component
37827 * Bootstrap DocumentViewer class
37828 * @cfg {Boolean} showDownload (true|false) show download button (default true)
37829 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37832 * Create a new DocumentViewer
37833 * @param {Object} config The config object
37836 Roo.bootstrap.DocumentViewer = function(config){
37837 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37842 * Fire after initEvent
37843 * @param {Roo.bootstrap.DocumentViewer} this
37849 * @param {Roo.bootstrap.DocumentViewer} this
37854 * Fire after download button
37855 * @param {Roo.bootstrap.DocumentViewer} this
37860 * Fire after trash button
37861 * @param {Roo.bootstrap.DocumentViewer} this
37868 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
37870 showDownload : true,
37874 getAutoCreate : function()
37878 cls : 'roo-document-viewer',
37882 cls : 'roo-document-viewer-body',
37886 cls : 'roo-document-viewer-thumb',
37890 cls : 'roo-document-viewer-image'
37898 cls : 'roo-document-viewer-footer',
37901 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37905 cls : 'btn-group roo-document-viewer-download',
37909 cls : 'btn btn-default',
37910 html : '<i class="fa fa-download"></i>'
37916 cls : 'btn-group roo-document-viewer-trash',
37920 cls : 'btn btn-default',
37921 html : '<i class="fa fa-trash"></i>'
37934 initEvents : function()
37936 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37937 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37939 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37940 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37942 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37943 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37945 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37946 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37948 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37949 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37951 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37952 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37954 this.bodyEl.on('click', this.onClick, this);
37955 this.downloadBtn.on('click', this.onDownload, this);
37956 this.trashBtn.on('click', this.onTrash, this);
37958 this.downloadBtn.hide();
37959 this.trashBtn.hide();
37961 if(this.showDownload){
37962 this.downloadBtn.show();
37965 if(this.showTrash){
37966 this.trashBtn.show();
37969 if(!this.showDownload && !this.showTrash) {
37970 this.footerEl.hide();
37975 initial : function()
37977 this.fireEvent('initial', this);
37981 onClick : function(e)
37983 e.preventDefault();
37985 this.fireEvent('click', this);
37988 onDownload : function(e)
37990 e.preventDefault();
37992 this.fireEvent('download', this);
37995 onTrash : function(e)
37997 e.preventDefault();
37999 this.fireEvent('trash', this);
38011 * @class Roo.bootstrap.form.FieldLabel
38012 * @extends Roo.bootstrap.Component
38013 * Bootstrap FieldLabel class
38014 * @cfg {String} html contents of the element
38015 * @cfg {String} tag tag of the element default label
38016 * @cfg {String} cls class of the element
38017 * @cfg {String} target label target
38018 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38019 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38020 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38021 * @cfg {String} iconTooltip default "This field is required"
38022 * @cfg {String} indicatorpos (left|right) default left
38025 * Create a new FieldLabel
38026 * @param {Object} config The config object
38029 Roo.bootstrap.form.FieldLabel = function(config){
38030 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38035 * Fires after the field has been marked as invalid.
38036 * @param {Roo.form.FieldLabel} this
38037 * @param {String} msg The validation message
38042 * Fires after the field has been validated with no errors.
38043 * @param {Roo.form.FieldLabel} this
38049 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38056 invalidClass : 'has-warning',
38057 validClass : 'has-success',
38058 iconTooltip : 'This field is required',
38059 indicatorpos : 'left',
38061 getAutoCreate : function(){
38064 if (!this.allowBlank) {
38070 cls : 'roo-bootstrap-field-label ' + this.cls,
38075 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38076 tooltip : this.iconTooltip
38085 if(this.indicatorpos == 'right'){
38088 cls : 'roo-bootstrap-field-label ' + this.cls,
38097 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38098 tooltip : this.iconTooltip
38107 initEvents: function()
38109 Roo.bootstrap.Element.superclass.initEvents.call(this);
38111 this.indicator = this.indicatorEl();
38113 if(this.indicator){
38114 this.indicator.removeClass('visible');
38115 this.indicator.addClass('invisible');
38118 Roo.bootstrap.form.FieldLabel.register(this);
38121 indicatorEl : function()
38123 var indicator = this.el.select('i.roo-required-indicator',true).first();
38134 * Mark this field as valid
38136 markValid : function()
38138 if(this.indicator){
38139 this.indicator.removeClass('visible');
38140 this.indicator.addClass('invisible');
38142 if (Roo.bootstrap.version == 3) {
38143 this.el.removeClass(this.invalidClass);
38144 this.el.addClass(this.validClass);
38146 this.el.removeClass('is-invalid');
38147 this.el.addClass('is-valid');
38151 this.fireEvent('valid', this);
38155 * Mark this field as invalid
38156 * @param {String} msg The validation message
38158 markInvalid : function(msg)
38160 if(this.indicator){
38161 this.indicator.removeClass('invisible');
38162 this.indicator.addClass('visible');
38164 if (Roo.bootstrap.version == 3) {
38165 this.el.removeClass(this.validClass);
38166 this.el.addClass(this.invalidClass);
38168 this.el.removeClass('is-valid');
38169 this.el.addClass('is-invalid');
38173 this.fireEvent('invalid', this, msg);
38179 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38184 * register a FieldLabel Group
38185 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38187 register : function(label)
38189 if(this.groups.hasOwnProperty(label.target)){
38193 this.groups[label.target] = label;
38197 * fetch a FieldLabel Group based on the target
38198 * @param {string} target
38199 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38201 get: function(target) {
38202 if (typeof(this.groups[target]) == 'undefined') {
38206 return this.groups[target] ;
38215 * page DateSplitField.
38221 * @class Roo.bootstrap.form.DateSplitField
38222 * @extends Roo.bootstrap.Component
38223 * Bootstrap DateSplitField class
38224 * @cfg {string} fieldLabel - the label associated
38225 * @cfg {Number} labelWidth set the width of label (0-12)
38226 * @cfg {String} labelAlign (top|left)
38227 * @cfg {Boolean} dayAllowBlank (true|false) default false
38228 * @cfg {Boolean} monthAllowBlank (true|false) default false
38229 * @cfg {Boolean} yearAllowBlank (true|false) default false
38230 * @cfg {string} dayPlaceholder
38231 * @cfg {string} monthPlaceholder
38232 * @cfg {string} yearPlaceholder
38233 * @cfg {string} dayFormat default 'd'
38234 * @cfg {string} monthFormat default 'm'
38235 * @cfg {string} yearFormat default 'Y'
38236 * @cfg {Number} labellg set the width of label (1-12)
38237 * @cfg {Number} labelmd set the width of label (1-12)
38238 * @cfg {Number} labelsm set the width of label (1-12)
38239 * @cfg {Number} labelxs set the width of label (1-12)
38243 * Create a new DateSplitField
38244 * @param {Object} config The config object
38247 Roo.bootstrap.form.DateSplitField = function(config){
38248 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38254 * getting the data of years
38255 * @param {Roo.bootstrap.form.DateSplitField} this
38256 * @param {Object} years
38261 * getting the data of days
38262 * @param {Roo.bootstrap.form.DateSplitField} this
38263 * @param {Object} days
38268 * Fires after the field has been marked as invalid.
38269 * @param {Roo.form.Field} this
38270 * @param {String} msg The validation message
38275 * Fires after the field has been validated with no errors.
38276 * @param {Roo.form.Field} this
38282 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38285 labelAlign : 'top',
38287 dayAllowBlank : false,
38288 monthAllowBlank : false,
38289 yearAllowBlank : false,
38290 dayPlaceholder : '',
38291 monthPlaceholder : '',
38292 yearPlaceholder : '',
38296 isFormField : true,
38302 getAutoCreate : function()
38306 cls : 'row roo-date-split-field-group',
38311 cls : 'form-hidden-field roo-date-split-field-group-value',
38317 var labelCls = 'col-md-12';
38318 var contentCls = 'col-md-4';
38320 if(this.fieldLabel){
38324 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38328 html : this.fieldLabel
38333 if(this.labelAlign == 'left'){
38335 if(this.labelWidth > 12){
38336 label.style = "width: " + this.labelWidth + 'px';
38339 if(this.labelWidth < 13 && this.labelmd == 0){
38340 this.labelmd = this.labelWidth;
38343 if(this.labellg > 0){
38344 labelCls = ' col-lg-' + this.labellg;
38345 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38348 if(this.labelmd > 0){
38349 labelCls = ' col-md-' + this.labelmd;
38350 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38353 if(this.labelsm > 0){
38354 labelCls = ' col-sm-' + this.labelsm;
38355 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38358 if(this.labelxs > 0){
38359 labelCls = ' col-xs-' + this.labelxs;
38360 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38364 label.cls += ' ' + labelCls;
38366 cfg.cn.push(label);
38369 Roo.each(['day', 'month', 'year'], function(t){
38372 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38379 inputEl: function ()
38381 return this.el.select('.roo-date-split-field-group-value', true).first();
38384 onRender : function(ct, position)
38388 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38390 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38392 this.dayField = new Roo.bootstrap.form.ComboBox({
38393 allowBlank : this.dayAllowBlank,
38394 alwaysQuery : true,
38395 displayField : 'value',
38398 forceSelection : true,
38400 placeholder : this.dayPlaceholder,
38401 selectOnFocus : true,
38402 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38403 triggerAction : 'all',
38405 valueField : 'value',
38406 store : new Roo.data.SimpleStore({
38407 data : (function() {
38409 _this.fireEvent('days', _this, days);
38412 fields : [ 'value' ]
38415 select : function (_self, record, index)
38417 _this.setValue(_this.getValue());
38422 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38424 this.monthField = new Roo.bootstrap.form.MonthField({
38425 after : '<i class=\"fa fa-calendar\"></i>',
38426 allowBlank : this.monthAllowBlank,
38427 placeholder : this.monthPlaceholder,
38430 render : function (_self)
38432 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38433 e.preventDefault();
38437 select : function (_self, oldvalue, newvalue)
38439 _this.setValue(_this.getValue());
38444 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38446 this.yearField = new Roo.bootstrap.form.ComboBox({
38447 allowBlank : this.yearAllowBlank,
38448 alwaysQuery : true,
38449 displayField : 'value',
38452 forceSelection : true,
38454 placeholder : this.yearPlaceholder,
38455 selectOnFocus : true,
38456 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38457 triggerAction : 'all',
38459 valueField : 'value',
38460 store : new Roo.data.SimpleStore({
38461 data : (function() {
38463 _this.fireEvent('years', _this, years);
38466 fields : [ 'value' ]
38469 select : function (_self, record, index)
38471 _this.setValue(_this.getValue());
38476 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38479 setValue : function(v, format)
38481 this.inputEl.dom.value = v;
38483 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38485 var d = Date.parseDate(v, f);
38492 this.setDay(d.format(this.dayFormat));
38493 this.setMonth(d.format(this.monthFormat));
38494 this.setYear(d.format(this.yearFormat));
38501 setDay : function(v)
38503 this.dayField.setValue(v);
38504 this.inputEl.dom.value = this.getValue();
38509 setMonth : function(v)
38511 this.monthField.setValue(v, true);
38512 this.inputEl.dom.value = this.getValue();
38517 setYear : function(v)
38519 this.yearField.setValue(v);
38520 this.inputEl.dom.value = this.getValue();
38525 getDay : function()
38527 return this.dayField.getValue();
38530 getMonth : function()
38532 return this.monthField.getValue();
38535 getYear : function()
38537 return this.yearField.getValue();
38540 getValue : function()
38542 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38544 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38554 this.inputEl.dom.value = '';
38559 validate : function()
38561 var d = this.dayField.validate();
38562 var m = this.monthField.validate();
38563 var y = this.yearField.validate();
38568 (!this.dayAllowBlank && !d) ||
38569 (!this.monthAllowBlank && !m) ||
38570 (!this.yearAllowBlank && !y)
38575 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38584 this.markInvalid();
38589 markValid : function()
38592 var label = this.el.select('label', true).first();
38593 var icon = this.el.select('i.fa-star', true).first();
38599 this.fireEvent('valid', this);
38603 * Mark this field as invalid
38604 * @param {String} msg The validation message
38606 markInvalid : function(msg)
38609 var label = this.el.select('label', true).first();
38610 var icon = this.el.select('i.fa-star', true).first();
38612 if(label && !icon){
38613 this.el.select('.roo-date-split-field-label', true).createChild({
38615 cls : 'text-danger fa fa-lg fa-star',
38616 tooltip : 'This field is required',
38617 style : 'margin-right:5px;'
38621 this.fireEvent('invalid', this, msg);
38624 clearInvalid : function()
38626 var label = this.el.select('label', true).first();
38627 var icon = this.el.select('i.fa-star', true).first();
38633 this.fireEvent('valid', this);
38636 getName: function()
38646 * @class Roo.bootstrap.LayoutMasonry
38647 * @extends Roo.bootstrap.Component
38648 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38649 * Bootstrap Layout Masonry class
38652 * http://masonry.desandro.com
38654 * The idea is to render all the bricks based on vertical width...
38656 * The original code extends 'outlayer' - we might need to use that....
38659 * Create a new Element
38660 * @param {Object} config The config object
38663 Roo.bootstrap.LayoutMasonry = function(config){
38665 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38669 Roo.bootstrap.LayoutMasonry.register(this);
38675 * Fire after layout the items
38676 * @param {Roo.bootstrap.LayoutMasonry} this
38677 * @param {Roo.EventObject} e
38684 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
38687 * @cfg {Boolean} isLayoutInstant = no animation?
38689 isLayoutInstant : false, // needed?
38692 * @cfg {Number} boxWidth width of the columns
38697 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
38702 * @cfg {Number} padWidth padding below box..
38707 * @cfg {Number} gutter gutter width..
38712 * @cfg {Number} maxCols maximum number of columns
38718 * @cfg {Boolean} isAutoInitial defalut true
38720 isAutoInitial : true,
38725 * @cfg {Boolean} isHorizontal defalut false
38727 isHorizontal : false,
38729 currentSize : null,
38735 bricks: null, //CompositeElement
38739 _isLayoutInited : false,
38741 // isAlternative : false, // only use for vertical layout...
38744 * @cfg {Number} alternativePadWidth padding below box..
38746 alternativePadWidth : 50,
38748 selectedBrick : [],
38750 getAutoCreate : function(){
38752 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38756 cls: 'blog-masonary-wrapper ' + this.cls,
38758 cls : 'mas-boxes masonary'
38765 getChildContainer: function( )
38767 if (this.boxesEl) {
38768 return this.boxesEl;
38771 this.boxesEl = this.el.select('.mas-boxes').first();
38773 return this.boxesEl;
38777 initEvents : function()
38781 if(this.isAutoInitial){
38782 Roo.log('hook children rendered');
38783 this.on('childrenrendered', function() {
38784 Roo.log('children rendered');
38790 initial : function()
38792 this.selectedBrick = [];
38794 this.currentSize = this.el.getBox(true);
38796 Roo.EventManager.onWindowResize(this.resize, this);
38798 if(!this.isAutoInitial){
38806 //this.layout.defer(500,this);
38810 resize : function()
38812 var cs = this.el.getBox(true);
38815 this.currentSize.width == cs.width &&
38816 this.currentSize.x == cs.x &&
38817 this.currentSize.height == cs.height &&
38818 this.currentSize.y == cs.y
38820 Roo.log("no change in with or X or Y");
38824 this.currentSize = cs;
38830 layout : function()
38832 this._resetLayout();
38834 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38836 this.layoutItems( isInstant );
38838 this._isLayoutInited = true;
38840 this.fireEvent('layout', this);
38844 _resetLayout : function()
38846 if(this.isHorizontal){
38847 this.horizontalMeasureColumns();
38851 this.verticalMeasureColumns();
38855 verticalMeasureColumns : function()
38857 this.getContainerWidth();
38859 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38860 // this.colWidth = Math.floor(this.containerWidth * 0.8);
38864 var boxWidth = this.boxWidth + this.padWidth;
38866 if(this.containerWidth < this.boxWidth){
38867 boxWidth = this.containerWidth
38870 var containerWidth = this.containerWidth;
38872 var cols = Math.floor(containerWidth / boxWidth);
38874 this.cols = Math.max( cols, 1 );
38876 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38878 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38880 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38882 this.colWidth = boxWidth + avail - this.padWidth;
38884 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38885 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
38888 horizontalMeasureColumns : function()
38890 this.getContainerWidth();
38892 var boxWidth = this.boxWidth;
38894 if(this.containerWidth < boxWidth){
38895 boxWidth = this.containerWidth;
38898 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38900 this.el.setHeight(boxWidth);
38904 getContainerWidth : function()
38906 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38909 layoutItems : function( isInstant )
38911 Roo.log(this.bricks);
38913 var items = Roo.apply([], this.bricks);
38915 if(this.isHorizontal){
38916 this._horizontalLayoutItems( items , isInstant );
38920 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38921 // this._verticalAlternativeLayoutItems( items , isInstant );
38925 this._verticalLayoutItems( items , isInstant );
38929 _verticalLayoutItems : function ( items , isInstant)
38931 if ( !items || !items.length ) {
38936 ['xs', 'xs', 'xs', 'tall'],
38937 ['xs', 'xs', 'tall'],
38938 ['xs', 'xs', 'sm'],
38939 ['xs', 'xs', 'xs'],
38945 ['sm', 'xs', 'xs'],
38949 ['tall', 'xs', 'xs', 'xs'],
38950 ['tall', 'xs', 'xs'],
38962 Roo.each(items, function(item, k){
38964 switch (item.size) {
38965 // these layouts take up a full box,
38976 boxes.push([item]);
38999 var filterPattern = function(box, length)
39007 var pattern = box.slice(0, length);
39011 Roo.each(pattern, function(i){
39012 format.push(i.size);
39015 Roo.each(standard, function(s){
39017 if(String(s) != String(format)){
39026 if(!match && length == 1){
39031 filterPattern(box, length - 1);
39035 queue.push(pattern);
39037 box = box.slice(length, box.length);
39039 filterPattern(box, 4);
39045 Roo.each(boxes, function(box, k){
39051 if(box.length == 1){
39056 filterPattern(box, 4);
39060 this._processVerticalLayoutQueue( queue, isInstant );
39064 // _verticalAlternativeLayoutItems : function( items , isInstant )
39066 // if ( !items || !items.length ) {
39070 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39074 _horizontalLayoutItems : function ( items , isInstant)
39076 if ( !items || !items.length || items.length < 3) {
39082 var eItems = items.slice(0, 3);
39084 items = items.slice(3, items.length);
39087 ['xs', 'xs', 'xs', 'wide'],
39088 ['xs', 'xs', 'wide'],
39089 ['xs', 'xs', 'sm'],
39090 ['xs', 'xs', 'xs'],
39096 ['sm', 'xs', 'xs'],
39100 ['wide', 'xs', 'xs', 'xs'],
39101 ['wide', 'xs', 'xs'],
39114 Roo.each(items, function(item, k){
39116 switch (item.size) {
39127 boxes.push([item]);
39151 var filterPattern = function(box, length)
39159 var pattern = box.slice(0, length);
39163 Roo.each(pattern, function(i){
39164 format.push(i.size);
39167 Roo.each(standard, function(s){
39169 if(String(s) != String(format)){
39178 if(!match && length == 1){
39183 filterPattern(box, length - 1);
39187 queue.push(pattern);
39189 box = box.slice(length, box.length);
39191 filterPattern(box, 4);
39197 Roo.each(boxes, function(box, k){
39203 if(box.length == 1){
39208 filterPattern(box, 4);
39215 var pos = this.el.getBox(true);
39219 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39221 var hit_end = false;
39223 Roo.each(queue, function(box){
39227 Roo.each(box, function(b){
39229 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39239 Roo.each(box, function(b){
39241 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39244 mx = Math.max(mx, b.x);
39248 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39252 Roo.each(box, function(b){
39254 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39268 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39271 /** Sets position of item in DOM
39272 * @param {Element} item
39273 * @param {Number} x - horizontal position
39274 * @param {Number} y - vertical position
39275 * @param {Boolean} isInstant - disables transitions
39277 _processVerticalLayoutQueue : function( queue, isInstant )
39279 var pos = this.el.getBox(true);
39284 for (var i = 0; i < this.cols; i++){
39288 Roo.each(queue, function(box, k){
39290 var col = k % this.cols;
39292 Roo.each(box, function(b,kk){
39294 b.el.position('absolute');
39296 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39297 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39299 if(b.size == 'md-left' || b.size == 'md-right'){
39300 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39301 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39304 b.el.setWidth(width);
39305 b.el.setHeight(height);
39307 b.el.select('iframe',true).setSize(width,height);
39311 for (var i = 0; i < this.cols; i++){
39313 if(maxY[i] < maxY[col]){
39318 col = Math.min(col, i);
39322 x = pos.x + col * (this.colWidth + this.padWidth);
39326 var positions = [];
39328 switch (box.length){
39330 positions = this.getVerticalOneBoxColPositions(x, y, box);
39333 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39336 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39339 positions = this.getVerticalFourBoxColPositions(x, y, box);
39345 Roo.each(box, function(b,kk){
39347 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39349 var sz = b.el.getSize();
39351 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39359 for (var i = 0; i < this.cols; i++){
39360 mY = Math.max(mY, maxY[i]);
39363 this.el.setHeight(mY - pos.y);
39367 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39369 // var pos = this.el.getBox(true);
39372 // var maxX = pos.right;
39374 // var maxHeight = 0;
39376 // Roo.each(items, function(item, k){
39380 // item.el.position('absolute');
39382 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39384 // item.el.setWidth(width);
39386 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39388 // item.el.setHeight(height);
39391 // item.el.setXY([x, y], isInstant ? false : true);
39393 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39396 // y = y + height + this.alternativePadWidth;
39398 // maxHeight = maxHeight + height + this.alternativePadWidth;
39402 // this.el.setHeight(maxHeight);
39406 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39408 var pos = this.el.getBox(true);
39413 var maxX = pos.right;
39415 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39417 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39419 Roo.each(queue, function(box, k){
39421 Roo.each(box, function(b, kk){
39423 b.el.position('absolute');
39425 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39426 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39428 if(b.size == 'md-left' || b.size == 'md-right'){
39429 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39430 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39433 b.el.setWidth(width);
39434 b.el.setHeight(height);
39442 var positions = [];
39444 switch (box.length){
39446 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39449 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39452 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39455 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39461 Roo.each(box, function(b,kk){
39463 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39465 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39473 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39475 Roo.each(eItems, function(b,k){
39477 b.size = (k == 0) ? 'sm' : 'xs';
39478 b.x = (k == 0) ? 2 : 1;
39479 b.y = (k == 0) ? 2 : 1;
39481 b.el.position('absolute');
39483 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39485 b.el.setWidth(width);
39487 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39489 b.el.setHeight(height);
39493 var positions = [];
39496 x : maxX - this.unitWidth * 2 - this.gutter,
39501 x : maxX - this.unitWidth,
39502 y : minY + (this.unitWidth + this.gutter) * 2
39506 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39510 Roo.each(eItems, function(b,k){
39512 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39518 getVerticalOneBoxColPositions : function(x, y, box)
39522 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39524 if(box[0].size == 'md-left'){
39528 if(box[0].size == 'md-right'){
39533 x : x + (this.unitWidth + this.gutter) * rand,
39540 getVerticalTwoBoxColPositions : function(x, y, box)
39544 if(box[0].size == 'xs'){
39548 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39552 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39566 x : x + (this.unitWidth + this.gutter) * 2,
39567 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39574 getVerticalThreeBoxColPositions : function(x, y, box)
39578 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39586 x : x + (this.unitWidth + this.gutter) * 1,
39591 x : x + (this.unitWidth + this.gutter) * 2,
39599 if(box[0].size == 'xs' && box[1].size == 'xs'){
39608 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39612 x : x + (this.unitWidth + this.gutter) * 1,
39626 x : x + (this.unitWidth + this.gutter) * 2,
39631 x : x + (this.unitWidth + this.gutter) * 2,
39632 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39639 getVerticalFourBoxColPositions : function(x, y, box)
39643 if(box[0].size == 'xs'){
39652 y : y + (this.unitHeight + this.gutter) * 1
39657 y : y + (this.unitHeight + this.gutter) * 2
39661 x : x + (this.unitWidth + this.gutter) * 1,
39675 x : x + (this.unitWidth + this.gutter) * 2,
39680 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39681 y : y + (this.unitHeight + this.gutter) * 1
39685 x : x + (this.unitWidth + this.gutter) * 2,
39686 y : y + (this.unitWidth + this.gutter) * 2
39693 getHorizontalOneBoxColPositions : function(maxX, minY, box)
39697 if(box[0].size == 'md-left'){
39699 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39706 if(box[0].size == 'md-right'){
39708 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39709 y : minY + (this.unitWidth + this.gutter) * 1
39715 var rand = Math.floor(Math.random() * (4 - box[0].y));
39718 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39719 y : minY + (this.unitWidth + this.gutter) * rand
39726 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39730 if(box[0].size == 'xs'){
39733 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39738 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39739 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39747 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39752 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39753 y : minY + (this.unitWidth + this.gutter) * 2
39760 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39764 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39767 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39772 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39773 y : minY + (this.unitWidth + this.gutter) * 1
39777 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39778 y : minY + (this.unitWidth + this.gutter) * 2
39785 if(box[0].size == 'xs' && box[1].size == 'xs'){
39788 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39793 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39798 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39799 y : minY + (this.unitWidth + this.gutter) * 1
39807 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39812 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39813 y : minY + (this.unitWidth + this.gutter) * 2
39817 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39818 y : minY + (this.unitWidth + this.gutter) * 2
39825 getHorizontalFourBoxColPositions : function(maxX, minY, box)
39829 if(box[0].size == 'xs'){
39832 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39837 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39842 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),
39847 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39848 y : minY + (this.unitWidth + this.gutter) * 1
39856 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39861 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39862 y : minY + (this.unitWidth + this.gutter) * 2
39866 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39867 y : minY + (this.unitWidth + this.gutter) * 2
39871 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),
39872 y : minY + (this.unitWidth + this.gutter) * 2
39880 * remove a Masonry Brick
39881 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39883 removeBrick : function(brick_id)
39889 for (var i = 0; i<this.bricks.length; i++) {
39890 if (this.bricks[i].id == brick_id) {
39891 this.bricks.splice(i,1);
39892 this.el.dom.removeChild(Roo.get(brick_id).dom);
39899 * adds a Masonry Brick
39900 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39902 addBrick : function(cfg)
39904 var cn = new Roo.bootstrap.MasonryBrick(cfg);
39905 //this.register(cn);
39906 cn.parentId = this.id;
39907 cn.render(this.el);
39912 * register a Masonry Brick
39913 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39916 register : function(brick)
39918 this.bricks.push(brick);
39919 brick.masonryId = this.id;
39923 * clear all the Masonry Brick
39925 clearAll : function()
39928 //this.getChildContainer().dom.innerHTML = "";
39929 this.el.dom.innerHTML = '';
39932 getSelected : function()
39934 if (!this.selectedBrick) {
39938 return this.selectedBrick;
39942 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39946 * register a Masonry Layout
39947 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39950 register : function(layout)
39952 this.groups[layout.id] = layout;
39955 * fetch a Masonry Layout based on the masonry layout ID
39956 * @param {string} the masonry layout to add
39957 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39960 get: function(layout_id) {
39961 if (typeof(this.groups[layout_id]) == 'undefined') {
39964 return this.groups[layout_id] ;
39976 * http://masonry.desandro.com
39978 * The idea is to render all the bricks based on vertical width...
39980 * The original code extends 'outlayer' - we might need to use that....
39986 * @class Roo.bootstrap.LayoutMasonryAuto
39987 * @extends Roo.bootstrap.Component
39988 * Bootstrap Layout Masonry class
39991 * Create a new Element
39992 * @param {Object} config The config object
39995 Roo.bootstrap.LayoutMasonryAuto = function(config){
39996 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39999 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40002 * @cfg {Boolean} isFitWidth - resize the width..
40004 isFitWidth : false, // options..
40006 * @cfg {Boolean} isOriginLeft = left align?
40008 isOriginLeft : true,
40010 * @cfg {Boolean} isOriginTop = top align?
40012 isOriginTop : false,
40014 * @cfg {Boolean} isLayoutInstant = no animation?
40016 isLayoutInstant : false, // needed?
40018 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40020 isResizingContainer : true,
40022 * @cfg {Number} columnWidth width of the columns
40028 * @cfg {Number} maxCols maximum number of columns
40033 * @cfg {Number} padHeight padding below box..
40039 * @cfg {Boolean} isAutoInitial defalut true
40042 isAutoInitial : true,
40048 initialColumnWidth : 0,
40049 currentSize : null,
40051 colYs : null, // array.
40058 bricks: null, //CompositeElement
40059 cols : 0, // array?
40060 // element : null, // wrapped now this.el
40061 _isLayoutInited : null,
40064 getAutoCreate : function(){
40068 cls: 'blog-masonary-wrapper ' + this.cls,
40070 cls : 'mas-boxes masonary'
40077 getChildContainer: function( )
40079 if (this.boxesEl) {
40080 return this.boxesEl;
40083 this.boxesEl = this.el.select('.mas-boxes').first();
40085 return this.boxesEl;
40089 initEvents : function()
40093 if(this.isAutoInitial){
40094 Roo.log('hook children rendered');
40095 this.on('childrenrendered', function() {
40096 Roo.log('children rendered');
40103 initial : function()
40105 this.reloadItems();
40107 this.currentSize = this.el.getBox(true);
40109 /// was window resize... - let's see if this works..
40110 Roo.EventManager.onWindowResize(this.resize, this);
40112 if(!this.isAutoInitial){
40117 this.layout.defer(500,this);
40120 reloadItems: function()
40122 this.bricks = this.el.select('.masonry-brick', true);
40124 this.bricks.each(function(b) {
40125 //Roo.log(b.getSize());
40126 if (!b.attr('originalwidth')) {
40127 b.attr('originalwidth', b.getSize().width);
40132 Roo.log(this.bricks.elements.length);
40135 resize : function()
40138 var cs = this.el.getBox(true);
40140 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40141 Roo.log("no change in with or X");
40144 this.currentSize = cs;
40148 layout : function()
40151 this._resetLayout();
40152 //this._manageStamps();
40154 // don't animate first layout
40155 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40156 this.layoutItems( isInstant );
40158 // flag for initalized
40159 this._isLayoutInited = true;
40162 layoutItems : function( isInstant )
40164 //var items = this._getItemsForLayout( this.items );
40165 // original code supports filtering layout items.. we just ignore it..
40167 this._layoutItems( this.bricks , isInstant );
40169 this._postLayout();
40171 _layoutItems : function ( items , isInstant)
40173 //this.fireEvent( 'layout', this, items );
40176 if ( !items || !items.elements.length ) {
40177 // no items, emit event with empty array
40182 items.each(function(item) {
40183 Roo.log("layout item");
40185 // get x/y object from method
40186 var position = this._getItemLayoutPosition( item );
40188 position.item = item;
40189 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40190 queue.push( position );
40193 this._processLayoutQueue( queue );
40195 /** Sets position of item in DOM
40196 * @param {Element} item
40197 * @param {Number} x - horizontal position
40198 * @param {Number} y - vertical position
40199 * @param {Boolean} isInstant - disables transitions
40201 _processLayoutQueue : function( queue )
40203 for ( var i=0, len = queue.length; i < len; i++ ) {
40204 var obj = queue[i];
40205 obj.item.position('absolute');
40206 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40212 * Any logic you want to do after each layout,
40213 * i.e. size the container
40215 _postLayout : function()
40217 this.resizeContainer();
40220 resizeContainer : function()
40222 if ( !this.isResizingContainer ) {
40225 var size = this._getContainerSize();
40227 this.el.setSize(size.width,size.height);
40228 this.boxesEl.setSize(size.width,size.height);
40234 _resetLayout : function()
40236 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40237 this.colWidth = this.el.getWidth();
40238 //this.gutter = this.el.getWidth();
40240 this.measureColumns();
40246 this.colYs.push( 0 );
40252 measureColumns : function()
40254 this.getContainerWidth();
40255 // if columnWidth is 0, default to outerWidth of first item
40256 if ( !this.columnWidth ) {
40257 var firstItem = this.bricks.first();
40258 Roo.log(firstItem);
40259 this.columnWidth = this.containerWidth;
40260 if (firstItem && firstItem.attr('originalwidth') ) {
40261 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40263 // columnWidth fall back to item of first element
40264 Roo.log("set column width?");
40265 this.initialColumnWidth = this.columnWidth ;
40267 // if first elem has no width, default to size of container
40272 if (this.initialColumnWidth) {
40273 this.columnWidth = this.initialColumnWidth;
40278 // column width is fixed at the top - however if container width get's smaller we should
40281 // this bit calcs how man columns..
40283 var columnWidth = this.columnWidth += this.gutter;
40285 // calculate columns
40286 var containerWidth = this.containerWidth + this.gutter;
40288 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40289 // fix rounding errors, typically with gutters
40290 var excess = columnWidth - containerWidth % columnWidth;
40293 // if overshoot is less than a pixel, round up, otherwise floor it
40294 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40295 cols = Math[ mathMethod ]( cols );
40296 this.cols = Math.max( cols, 1 );
40297 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40299 // padding positioning..
40300 var totalColWidth = this.cols * this.columnWidth;
40301 var padavail = this.containerWidth - totalColWidth;
40302 // so for 2 columns - we need 3 'pads'
40304 var padNeeded = (1+this.cols) * this.padWidth;
40306 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40308 this.columnWidth += padExtra
40309 //this.padWidth = Math.floor(padavail / ( this.cols));
40311 // adjust colum width so that padding is fixed??
40313 // we have 3 columns ... total = width * 3
40314 // we have X left over... that should be used by
40316 //if (this.expandC) {
40324 getContainerWidth : function()
40326 /* // container is parent if fit width
40327 var container = this.isFitWidth ? this.element.parentNode : this.element;
40328 // check that this.size and size are there
40329 // IE8 triggers resize on body size change, so they might not be
40331 var size = getSize( container ); //FIXME
40332 this.containerWidth = size && size.innerWidth; //FIXME
40335 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40339 _getItemLayoutPosition : function( item ) // what is item?
40341 // we resize the item to our columnWidth..
40343 item.setWidth(this.columnWidth);
40344 item.autoBoxAdjust = false;
40346 var sz = item.getSize();
40348 // how many columns does this brick span
40349 var remainder = this.containerWidth % this.columnWidth;
40351 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40352 // round if off by 1 pixel, otherwise use ceil
40353 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40354 colSpan = Math.min( colSpan, this.cols );
40356 // normally this should be '1' as we dont' currently allow multi width columns..
40358 var colGroup = this._getColGroup( colSpan );
40359 // get the minimum Y value from the columns
40360 var minimumY = Math.min.apply( Math, colGroup );
40361 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40363 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40365 // position the brick
40367 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40368 y: this.currentSize.y + minimumY + this.padHeight
40372 // apply setHeight to necessary columns
40373 var setHeight = minimumY + sz.height + this.padHeight;
40374 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40376 var setSpan = this.cols + 1 - colGroup.length;
40377 for ( var i = 0; i < setSpan; i++ ) {
40378 this.colYs[ shortColIndex + i ] = setHeight ;
40385 * @param {Number} colSpan - number of columns the element spans
40386 * @returns {Array} colGroup
40388 _getColGroup : function( colSpan )
40390 if ( colSpan < 2 ) {
40391 // if brick spans only one column, use all the column Ys
40396 // how many different places could this brick fit horizontally
40397 var groupCount = this.cols + 1 - colSpan;
40398 // for each group potential horizontal position
40399 for ( var i = 0; i < groupCount; i++ ) {
40400 // make an array of colY values for that one group
40401 var groupColYs = this.colYs.slice( i, i + colSpan );
40402 // and get the max value of the array
40403 colGroup[i] = Math.max.apply( Math, groupColYs );
40408 _manageStamp : function( stamp )
40410 var stampSize = stamp.getSize();
40411 var offset = stamp.getBox();
40412 // get the columns that this stamp affects
40413 var firstX = this.isOriginLeft ? offset.x : offset.right;
40414 var lastX = firstX + stampSize.width;
40415 var firstCol = Math.floor( firstX / this.columnWidth );
40416 firstCol = Math.max( 0, firstCol );
40418 var lastCol = Math.floor( lastX / this.columnWidth );
40419 // lastCol should not go over if multiple of columnWidth #425
40420 lastCol -= lastX % this.columnWidth ? 0 : 1;
40421 lastCol = Math.min( this.cols - 1, lastCol );
40423 // set colYs to bottom of the stamp
40424 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40427 for ( var i = firstCol; i <= lastCol; i++ ) {
40428 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40433 _getContainerSize : function()
40435 this.maxY = Math.max.apply( Math, this.colYs );
40440 if ( this.isFitWidth ) {
40441 size.width = this._getContainerFitWidth();
40447 _getContainerFitWidth : function()
40449 var unusedCols = 0;
40450 // count unused columns
40453 if ( this.colYs[i] !== 0 ) {
40458 // fit container to columns that have been used
40459 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40462 needsResizeLayout : function()
40464 var previousWidth = this.containerWidth;
40465 this.getContainerWidth();
40466 return previousWidth !== this.containerWidth;
40481 * @class Roo.bootstrap.MasonryBrick
40482 * @extends Roo.bootstrap.Component
40483 * Bootstrap MasonryBrick class
40486 * Create a new MasonryBrick
40487 * @param {Object} config The config object
40490 Roo.bootstrap.MasonryBrick = function(config){
40492 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40494 Roo.bootstrap.MasonryBrick.register(this);
40500 * When a MasonryBrick is clcik
40501 * @param {Roo.bootstrap.MasonryBrick} this
40502 * @param {Roo.EventObject} e
40508 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40511 * @cfg {String} title
40515 * @cfg {String} html
40519 * @cfg {String} bgimage
40523 * @cfg {String} videourl
40527 * @cfg {String} cls
40531 * @cfg {String} href
40535 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40540 * @cfg {String} placetitle (center|bottom)
40545 * @cfg {Boolean} isFitContainer defalut true
40547 isFitContainer : true,
40550 * @cfg {Boolean} preventDefault defalut false
40552 preventDefault : false,
40555 * @cfg {Boolean} inverse defalut false
40557 maskInverse : false,
40559 getAutoCreate : function()
40561 if(!this.isFitContainer){
40562 return this.getSplitAutoCreate();
40565 var cls = 'masonry-brick masonry-brick-full';
40567 if(this.href.length){
40568 cls += ' masonry-brick-link';
40571 if(this.bgimage.length){
40572 cls += ' masonry-brick-image';
40575 if(this.maskInverse){
40576 cls += ' mask-inverse';
40579 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40580 cls += ' enable-mask';
40584 cls += ' masonry-' + this.size + '-brick';
40587 if(this.placetitle.length){
40589 switch (this.placetitle) {
40591 cls += ' masonry-center-title';
40594 cls += ' masonry-bottom-title';
40601 if(!this.html.length && !this.bgimage.length){
40602 cls += ' masonry-center-title';
40605 if(!this.html.length && this.bgimage.length){
40606 cls += ' masonry-bottom-title';
40611 cls += ' ' + this.cls;
40615 tag: (this.href.length) ? 'a' : 'div',
40620 cls: 'masonry-brick-mask'
40624 cls: 'masonry-brick-paragraph',
40630 if(this.href.length){
40631 cfg.href = this.href;
40634 var cn = cfg.cn[1].cn;
40636 if(this.title.length){
40639 cls: 'masonry-brick-title',
40644 if(this.html.length){
40647 cls: 'masonry-brick-text',
40652 if (!this.title.length && !this.html.length) {
40653 cfg.cn[1].cls += ' hide';
40656 if(this.bgimage.length){
40659 cls: 'masonry-brick-image-view',
40664 if(this.videourl.length){
40665 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40666 // youtube support only?
40669 cls: 'masonry-brick-image-view',
40672 allowfullscreen : true
40680 getSplitAutoCreate : function()
40682 var cls = 'masonry-brick masonry-brick-split';
40684 if(this.href.length){
40685 cls += ' masonry-brick-link';
40688 if(this.bgimage.length){
40689 cls += ' masonry-brick-image';
40693 cls += ' masonry-' + this.size + '-brick';
40696 switch (this.placetitle) {
40698 cls += ' masonry-center-title';
40701 cls += ' masonry-bottom-title';
40704 if(!this.bgimage.length){
40705 cls += ' masonry-center-title';
40708 if(this.bgimage.length){
40709 cls += ' masonry-bottom-title';
40715 cls += ' ' + this.cls;
40719 tag: (this.href.length) ? 'a' : 'div',
40724 cls: 'masonry-brick-split-head',
40728 cls: 'masonry-brick-paragraph',
40735 cls: 'masonry-brick-split-body',
40741 if(this.href.length){
40742 cfg.href = this.href;
40745 if(this.title.length){
40746 cfg.cn[0].cn[0].cn.push({
40748 cls: 'masonry-brick-title',
40753 if(this.html.length){
40754 cfg.cn[1].cn.push({
40756 cls: 'masonry-brick-text',
40761 if(this.bgimage.length){
40762 cfg.cn[0].cn.push({
40764 cls: 'masonry-brick-image-view',
40769 if(this.videourl.length){
40770 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40771 // youtube support only?
40772 cfg.cn[0].cn.cn.push({
40774 cls: 'masonry-brick-image-view',
40777 allowfullscreen : true
40784 initEvents: function()
40786 switch (this.size) {
40819 this.el.on('touchstart', this.onTouchStart, this);
40820 this.el.on('touchmove', this.onTouchMove, this);
40821 this.el.on('touchend', this.onTouchEnd, this);
40822 this.el.on('contextmenu', this.onContextMenu, this);
40824 this.el.on('mouseenter' ,this.enter, this);
40825 this.el.on('mouseleave', this.leave, this);
40826 this.el.on('click', this.onClick, this);
40829 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40830 this.parent().bricks.push(this);
40835 onClick: function(e, el)
40837 var time = this.endTimer - this.startTimer;
40838 // Roo.log(e.preventDefault());
40841 e.preventDefault();
40846 if(!this.preventDefault){
40850 e.preventDefault();
40852 if (this.activeClass != '') {
40853 this.selectBrick();
40856 this.fireEvent('click', this, e);
40859 enter: function(e, el)
40861 e.preventDefault();
40863 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40867 if(this.bgimage.length && this.html.length){
40868 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40872 leave: function(e, el)
40874 e.preventDefault();
40876 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40880 if(this.bgimage.length && this.html.length){
40881 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40885 onTouchStart: function(e, el)
40887 // e.preventDefault();
40889 this.touchmoved = false;
40891 if(!this.isFitContainer){
40895 if(!this.bgimage.length || !this.html.length){
40899 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40901 this.timer = new Date().getTime();
40905 onTouchMove: function(e, el)
40907 this.touchmoved = true;
40910 onContextMenu : function(e,el)
40912 e.preventDefault();
40913 e.stopPropagation();
40917 onTouchEnd: function(e, el)
40919 // e.preventDefault();
40921 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40928 if(!this.bgimage.length || !this.html.length){
40930 if(this.href.length){
40931 window.location.href = this.href;
40937 if(!this.isFitContainer){
40941 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40943 window.location.href = this.href;
40946 //selection on single brick only
40947 selectBrick : function() {
40949 if (!this.parentId) {
40953 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40954 var index = m.selectedBrick.indexOf(this.id);
40957 m.selectedBrick.splice(index,1);
40958 this.el.removeClass(this.activeClass);
40962 for(var i = 0; i < m.selectedBrick.length; i++) {
40963 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40964 b.el.removeClass(b.activeClass);
40967 m.selectedBrick = [];
40969 m.selectedBrick.push(this.id);
40970 this.el.addClass(this.activeClass);
40974 isSelected : function(){
40975 return this.el.hasClass(this.activeClass);
40980 Roo.apply(Roo.bootstrap.MasonryBrick, {
40983 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40985 * register a Masonry Brick
40986 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40989 register : function(brick)
40991 //this.groups[brick.id] = brick;
40992 this.groups.add(brick.id, brick);
40995 * fetch a masonry brick based on the masonry brick ID
40996 * @param {string} the masonry brick to add
40997 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41000 get: function(brick_id)
41002 // if (typeof(this.groups[brick_id]) == 'undefined') {
41005 // return this.groups[brick_id] ;
41007 if(this.groups.key(brick_id)) {
41008 return this.groups.key(brick_id);
41026 * @class Roo.bootstrap.Brick
41027 * @extends Roo.bootstrap.Component
41028 * Bootstrap Brick class
41031 * Create a new Brick
41032 * @param {Object} config The config object
41035 Roo.bootstrap.Brick = function(config){
41036 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41042 * When a Brick is click
41043 * @param {Roo.bootstrap.Brick} this
41044 * @param {Roo.EventObject} e
41050 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41053 * @cfg {String} title
41057 * @cfg {String} html
41061 * @cfg {String} bgimage
41065 * @cfg {String} cls
41069 * @cfg {String} href
41073 * @cfg {String} video
41077 * @cfg {Boolean} square
41081 getAutoCreate : function()
41083 var cls = 'roo-brick';
41085 if(this.href.length){
41086 cls += ' roo-brick-link';
41089 if(this.bgimage.length){
41090 cls += ' roo-brick-image';
41093 if(!this.html.length && !this.bgimage.length){
41094 cls += ' roo-brick-center-title';
41097 if(!this.html.length && this.bgimage.length){
41098 cls += ' roo-brick-bottom-title';
41102 cls += ' ' + this.cls;
41106 tag: (this.href.length) ? 'a' : 'div',
41111 cls: 'roo-brick-paragraph',
41117 if(this.href.length){
41118 cfg.href = this.href;
41121 var cn = cfg.cn[0].cn;
41123 if(this.title.length){
41126 cls: 'roo-brick-title',
41131 if(this.html.length){
41134 cls: 'roo-brick-text',
41141 if(this.bgimage.length){
41144 cls: 'roo-brick-image-view',
41152 initEvents: function()
41154 if(this.title.length || this.html.length){
41155 this.el.on('mouseenter' ,this.enter, this);
41156 this.el.on('mouseleave', this.leave, this);
41159 Roo.EventManager.onWindowResize(this.resize, this);
41161 if(this.bgimage.length){
41162 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41163 this.imageEl.on('load', this.onImageLoad, this);
41170 onImageLoad : function()
41175 resize : function()
41177 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41179 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41181 if(this.bgimage.length){
41182 var image = this.el.select('.roo-brick-image-view', true).first();
41184 image.setWidth(paragraph.getWidth());
41187 image.setHeight(paragraph.getWidth());
41190 this.el.setHeight(image.getHeight());
41191 paragraph.setHeight(image.getHeight());
41197 enter: function(e, el)
41199 e.preventDefault();
41201 if(this.bgimage.length){
41202 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41203 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41207 leave: function(e, el)
41209 e.preventDefault();
41211 if(this.bgimage.length){
41212 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41213 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41228 * @class Roo.bootstrap.form.NumberField
41229 * @extends Roo.bootstrap.form.Input
41230 * Bootstrap NumberField class
41236 * Create a new NumberField
41237 * @param {Object} config The config object
41240 Roo.bootstrap.form.NumberField = function(config){
41241 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41244 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41247 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41249 allowDecimals : true,
41251 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41253 decimalSeparator : ".",
41255 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41257 decimalPrecision : 2,
41259 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41261 allowNegative : true,
41264 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41268 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41270 minValue : Number.NEGATIVE_INFINITY,
41272 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41274 maxValue : Number.MAX_VALUE,
41276 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41278 minText : "The minimum value for this field is {0}",
41280 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41282 maxText : "The maximum value for this field is {0}",
41284 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41285 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41287 nanText : "{0} is not a valid number",
41289 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41291 thousandsDelimiter : false,
41293 * @cfg {String} valueAlign alignment of value
41295 valueAlign : "left",
41297 getAutoCreate : function()
41299 var hiddenInput = {
41303 cls: 'hidden-number-input'
41307 hiddenInput.name = this.name;
41312 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41314 this.name = hiddenInput.name;
41316 if(cfg.cn.length > 0) {
41317 cfg.cn.push(hiddenInput);
41324 initEvents : function()
41326 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41328 var allowed = "0123456789";
41330 if(this.allowDecimals){
41331 allowed += this.decimalSeparator;
41334 if(this.allowNegative){
41338 if(this.thousandsDelimiter) {
41342 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41344 var keyPress = function(e){
41346 var k = e.getKey();
41348 var c = e.getCharCode();
41351 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41352 allowed.indexOf(String.fromCharCode(c)) === -1
41358 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41362 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41367 this.el.on("keypress", keyPress, this);
41370 validateValue : function(value)
41373 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41377 var num = this.parseValue(value);
41380 this.markInvalid(String.format(this.nanText, value));
41384 if(num < this.minValue){
41385 this.markInvalid(String.format(this.minText, this.minValue));
41389 if(num > this.maxValue){
41390 this.markInvalid(String.format(this.maxText, this.maxValue));
41397 getValue : function()
41399 var v = this.hiddenEl().getValue();
41401 return this.fixPrecision(this.parseValue(v));
41404 parseValue : function(value)
41406 if(this.thousandsDelimiter) {
41408 r = new RegExp(",", "g");
41409 value = value.replace(r, "");
41412 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41413 return isNaN(value) ? '' : value;
41416 fixPrecision : function(value)
41418 if(this.thousandsDelimiter) {
41420 r = new RegExp(",", "g");
41421 value = value.replace(r, "");
41424 var nan = isNaN(value);
41426 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41427 return nan ? '' : value;
41429 return parseFloat(value).toFixed(this.decimalPrecision);
41432 setValue : function(v)
41434 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41440 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41442 this.inputEl().dom.value = (v == '') ? '' :
41443 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41445 if(!this.allowZero && v === '0') {
41446 this.hiddenEl().dom.value = '';
41447 this.inputEl().dom.value = '';
41454 decimalPrecisionFcn : function(v)
41456 return Math.floor(v);
41459 beforeBlur : function()
41461 var v = this.parseValue(this.getRawValue());
41463 if(v || v === 0 || v === ''){
41468 hiddenEl : function()
41470 return this.el.select('input.hidden-number-input',true).first();
41482 * @class Roo.bootstrap.DocumentSlider
41483 * @extends Roo.bootstrap.Component
41484 * Bootstrap DocumentSlider class
41487 * Create a new DocumentViewer
41488 * @param {Object} config The config object
41491 Roo.bootstrap.DocumentSlider = function(config){
41492 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41499 * Fire after initEvent
41500 * @param {Roo.bootstrap.DocumentSlider} this
41505 * Fire after update
41506 * @param {Roo.bootstrap.DocumentSlider} this
41512 * @param {Roo.bootstrap.DocumentSlider} this
41518 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41524 getAutoCreate : function()
41528 cls : 'roo-document-slider',
41532 cls : 'roo-document-slider-header',
41536 cls : 'roo-document-slider-header-title'
41542 cls : 'roo-document-slider-body',
41546 cls : 'roo-document-slider-prev',
41550 cls : 'fa fa-chevron-left'
41556 cls : 'roo-document-slider-thumb',
41560 cls : 'roo-document-slider-image'
41566 cls : 'roo-document-slider-next',
41570 cls : 'fa fa-chevron-right'
41582 initEvents : function()
41584 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41585 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41587 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41588 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41590 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41591 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41593 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41594 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41596 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41597 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41599 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41600 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41602 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41603 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41605 this.thumbEl.on('click', this.onClick, this);
41607 this.prevIndicator.on('click', this.prev, this);
41609 this.nextIndicator.on('click', this.next, this);
41613 initial : function()
41615 if(this.files.length){
41616 this.indicator = 1;
41620 this.fireEvent('initial', this);
41623 update : function()
41625 this.imageEl.attr('src', this.files[this.indicator - 1]);
41627 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41629 this.prevIndicator.show();
41631 if(this.indicator == 1){
41632 this.prevIndicator.hide();
41635 this.nextIndicator.show();
41637 if(this.indicator == this.files.length){
41638 this.nextIndicator.hide();
41641 this.thumbEl.scrollTo('top');
41643 this.fireEvent('update', this);
41646 onClick : function(e)
41648 e.preventDefault();
41650 this.fireEvent('click', this);
41655 e.preventDefault();
41657 this.indicator = Math.max(1, this.indicator - 1);
41664 e.preventDefault();
41666 this.indicator = Math.min(this.files.length, this.indicator + 1);
41680 * @class Roo.bootstrap.form.RadioSet
41681 * @extends Roo.bootstrap.form.Input
41682 * @children Roo.bootstrap.form.Radio
41683 * Bootstrap RadioSet class
41684 * @cfg {String} indicatorpos (left|right) default left
41685 * @cfg {Boolean} inline (true|false) inline the element (default true)
41686 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41688 * Create a new RadioSet
41689 * @param {Object} config The config object
41692 Roo.bootstrap.form.RadioSet = function(config){
41694 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41698 Roo.bootstrap.form.RadioSet.register(this);
41703 * Fires when the element is checked or unchecked.
41704 * @param {Roo.bootstrap.form.RadioSet} this This radio
41705 * @param {Roo.bootstrap.form.Radio} item The checked item
41710 * Fires when the element is click.
41711 * @param {Roo.bootstrap.form.RadioSet} this This radio set
41712 * @param {Roo.bootstrap.form.Radio} item The checked item
41713 * @param {Roo.EventObject} e The event object
41720 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
41728 indicatorpos : 'left',
41730 getAutoCreate : function()
41734 cls : 'roo-radio-set-label',
41738 html : this.fieldLabel
41742 if (Roo.bootstrap.version == 3) {
41745 if(this.indicatorpos == 'left'){
41748 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41749 tooltip : 'This field is required'
41754 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41755 tooltip : 'This field is required'
41761 cls : 'roo-radio-set-items'
41764 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41766 if (align === 'left' && this.fieldLabel.length) {
41769 cls : "roo-radio-set-right",
41775 if(this.labelWidth > 12){
41776 label.style = "width: " + this.labelWidth + 'px';
41779 if(this.labelWidth < 13 && this.labelmd == 0){
41780 this.labelmd = this.labelWidth;
41783 if(this.labellg > 0){
41784 label.cls += ' col-lg-' + this.labellg;
41785 items.cls += ' col-lg-' + (12 - this.labellg);
41788 if(this.labelmd > 0){
41789 label.cls += ' col-md-' + this.labelmd;
41790 items.cls += ' col-md-' + (12 - this.labelmd);
41793 if(this.labelsm > 0){
41794 label.cls += ' col-sm-' + this.labelsm;
41795 items.cls += ' col-sm-' + (12 - this.labelsm);
41798 if(this.labelxs > 0){
41799 label.cls += ' col-xs-' + this.labelxs;
41800 items.cls += ' col-xs-' + (12 - this.labelxs);
41806 cls : 'roo-radio-set',
41810 cls : 'roo-radio-set-input',
41813 value : this.value ? this.value : ''
41820 if(this.weight.length){
41821 cfg.cls += ' roo-radio-' + this.weight;
41825 cfg.cls += ' roo-radio-set-inline';
41829 ['xs','sm','md','lg'].map(function(size){
41830 if (settings[size]) {
41831 cfg.cls += ' col-' + size + '-' + settings[size];
41839 initEvents : function()
41841 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41842 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41844 if(!this.fieldLabel.length){
41845 this.labelEl.hide();
41848 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41849 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41851 this.indicator = this.indicatorEl();
41853 if(this.indicator){
41854 this.indicator.addClass('invisible');
41857 this.originalValue = this.getValue();
41861 inputEl: function ()
41863 return this.el.select('.roo-radio-set-input', true).first();
41866 getChildContainer : function()
41868 return this.itemsEl;
41871 register : function(item)
41873 this.radioes.push(item);
41877 validate : function()
41879 if(this.getVisibilityEl().hasClass('hidden')){
41885 Roo.each(this.radioes, function(i){
41894 if(this.allowBlank) {
41898 if(this.disabled || valid){
41903 this.markInvalid();
41908 markValid : function()
41910 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41911 this.indicatorEl().removeClass('visible');
41912 this.indicatorEl().addClass('invisible');
41916 if (Roo.bootstrap.version == 3) {
41917 this.el.removeClass([this.invalidClass, this.validClass]);
41918 this.el.addClass(this.validClass);
41920 this.el.removeClass(['is-invalid','is-valid']);
41921 this.el.addClass(['is-valid']);
41923 this.fireEvent('valid', this);
41926 markInvalid : function(msg)
41928 if(this.allowBlank || this.disabled){
41932 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41933 this.indicatorEl().removeClass('invisible');
41934 this.indicatorEl().addClass('visible');
41936 if (Roo.bootstrap.version == 3) {
41937 this.el.removeClass([this.invalidClass, this.validClass]);
41938 this.el.addClass(this.invalidClass);
41940 this.el.removeClass(['is-invalid','is-valid']);
41941 this.el.addClass(['is-invalid']);
41944 this.fireEvent('invalid', this, msg);
41948 setValue : function(v, suppressEvent)
41950 if(this.value === v){
41957 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41960 Roo.each(this.radioes, function(i){
41962 i.el.removeClass('checked');
41965 Roo.each(this.radioes, function(i){
41967 if(i.value === v || i.value.toString() === v.toString()){
41969 i.el.addClass('checked');
41971 if(suppressEvent !== true){
41972 this.fireEvent('check', this, i);
41983 clearInvalid : function(){
41985 if(!this.el || this.preventMark){
41989 this.el.removeClass([this.invalidClass]);
41991 this.fireEvent('valid', this);
41996 Roo.apply(Roo.bootstrap.form.RadioSet, {
42000 register : function(set)
42002 this.groups[set.name] = set;
42005 get: function(name)
42007 if (typeof(this.groups[name]) == 'undefined') {
42011 return this.groups[name] ;
42017 * Ext JS Library 1.1.1
42018 * Copyright(c) 2006-2007, Ext JS, LLC.
42020 * Originally Released Under LGPL - original licence link has changed is not relivant.
42023 * <script type="text/javascript">
42028 * @class Roo.bootstrap.SplitBar
42029 * @extends Roo.util.Observable
42030 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42034 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42035 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42036 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42037 split.minSize = 100;
42038 split.maxSize = 600;
42039 split.animate = true;
42040 split.on('moved', splitterMoved);
42043 * Create a new SplitBar
42044 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42045 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42046 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42047 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42048 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42049 position of the SplitBar).
42051 Roo.bootstrap.SplitBar = function(cfg){
42056 // dragElement : elm
42057 // resizingElement: el,
42059 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42060 // placement : Roo.bootstrap.SplitBar.LEFT ,
42061 // existingProxy ???
42064 this.el = Roo.get(cfg.dragElement, true);
42065 this.el.dom.unselectable = "on";
42067 this.resizingEl = Roo.get(cfg.resizingElement, true);
42071 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42072 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42075 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42078 * The minimum size of the resizing element. (Defaults to 0)
42084 * The maximum size of the resizing element. (Defaults to 2000)
42087 this.maxSize = 2000;
42090 * Whether to animate the transition to the new size
42093 this.animate = false;
42096 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42099 this.useShim = false;
42104 if(!cfg.existingProxy){
42106 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42108 this.proxy = Roo.get(cfg.existingProxy).dom;
42111 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42114 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42117 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42120 this.dragSpecs = {};
42123 * @private The adapter to use to positon and resize elements
42125 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42126 this.adapter.init(this);
42128 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42130 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42131 this.el.addClass("roo-splitbar-h");
42134 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42135 this.el.addClass("roo-splitbar-v");
42141 * Fires when the splitter is moved (alias for {@link #event-moved})
42142 * @param {Roo.bootstrap.SplitBar} this
42143 * @param {Number} newSize the new width or height
42148 * Fires when the splitter is moved
42149 * @param {Roo.bootstrap.SplitBar} this
42150 * @param {Number} newSize the new width or height
42154 * @event beforeresize
42155 * Fires before the splitter is dragged
42156 * @param {Roo.bootstrap.SplitBar} this
42158 "beforeresize" : true,
42160 "beforeapply" : true
42163 Roo.util.Observable.call(this);
42166 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42167 onStartProxyDrag : function(x, y){
42168 this.fireEvent("beforeresize", this);
42170 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42172 o.enableDisplayMode("block");
42173 // all splitbars share the same overlay
42174 Roo.bootstrap.SplitBar.prototype.overlay = o;
42176 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42177 this.overlay.show();
42178 Roo.get(this.proxy).setDisplayed("block");
42179 var size = this.adapter.getElementSize(this);
42180 this.activeMinSize = this.getMinimumSize();;
42181 this.activeMaxSize = this.getMaximumSize();;
42182 var c1 = size - this.activeMinSize;
42183 var c2 = Math.max(this.activeMaxSize - size, 0);
42184 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42185 this.dd.resetConstraints();
42186 this.dd.setXConstraint(
42187 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42188 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42190 this.dd.setYConstraint(0, 0);
42192 this.dd.resetConstraints();
42193 this.dd.setXConstraint(0, 0);
42194 this.dd.setYConstraint(
42195 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42196 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42199 this.dragSpecs.startSize = size;
42200 this.dragSpecs.startPoint = [x, y];
42201 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42205 * @private Called after the drag operation by the DDProxy
42207 onEndProxyDrag : function(e){
42208 Roo.get(this.proxy).setDisplayed(false);
42209 var endPoint = Roo.lib.Event.getXY(e);
42211 this.overlay.hide();
42214 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42215 newSize = this.dragSpecs.startSize +
42216 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42217 endPoint[0] - this.dragSpecs.startPoint[0] :
42218 this.dragSpecs.startPoint[0] - endPoint[0]
42221 newSize = this.dragSpecs.startSize +
42222 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42223 endPoint[1] - this.dragSpecs.startPoint[1] :
42224 this.dragSpecs.startPoint[1] - endPoint[1]
42227 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42228 if(newSize != this.dragSpecs.startSize){
42229 if(this.fireEvent('beforeapply', this, newSize) !== false){
42230 this.adapter.setElementSize(this, newSize);
42231 this.fireEvent("moved", this, newSize);
42232 this.fireEvent("resize", this, newSize);
42238 * Get the adapter this SplitBar uses
42239 * @return The adapter object
42241 getAdapter : function(){
42242 return this.adapter;
42246 * Set the adapter this SplitBar uses
42247 * @param {Object} adapter A SplitBar adapter object
42249 setAdapter : function(adapter){
42250 this.adapter = adapter;
42251 this.adapter.init(this);
42255 * Gets the minimum size for the resizing element
42256 * @return {Number} The minimum size
42258 getMinimumSize : function(){
42259 return this.minSize;
42263 * Sets the minimum size for the resizing element
42264 * @param {Number} minSize The minimum size
42266 setMinimumSize : function(minSize){
42267 this.minSize = minSize;
42271 * Gets the maximum size for the resizing element
42272 * @return {Number} The maximum size
42274 getMaximumSize : function(){
42275 return this.maxSize;
42279 * Sets the maximum size for the resizing element
42280 * @param {Number} maxSize The maximum size
42282 setMaximumSize : function(maxSize){
42283 this.maxSize = maxSize;
42287 * Sets the initialize size for the resizing element
42288 * @param {Number} size The initial size
42290 setCurrentSize : function(size){
42291 var oldAnimate = this.animate;
42292 this.animate = false;
42293 this.adapter.setElementSize(this, size);
42294 this.animate = oldAnimate;
42298 * Destroy this splitbar.
42299 * @param {Boolean} removeEl True to remove the element
42301 destroy : function(removeEl){
42303 this.shim.remove();
42306 this.proxy.parentNode.removeChild(this.proxy);
42314 * @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.
42316 Roo.bootstrap.SplitBar.createProxy = function(dir){
42317 var proxy = new Roo.Element(document.createElement("div"));
42318 proxy.unselectable();
42319 var cls = 'roo-splitbar-proxy';
42320 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42321 document.body.appendChild(proxy.dom);
42326 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42327 * Default Adapter. It assumes the splitter and resizing element are not positioned
42328 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42330 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42333 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42334 // do nothing for now
42335 init : function(s){
42339 * Called before drag operations to get the current size of the resizing element.
42340 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42342 getElementSize : function(s){
42343 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42344 return s.resizingEl.getWidth();
42346 return s.resizingEl.getHeight();
42351 * Called after drag operations to set the size of the resizing element.
42352 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42353 * @param {Number} newSize The new size to set
42354 * @param {Function} onComplete A function to be invoked when resizing is complete
42356 setElementSize : function(s, newSize, onComplete){
42357 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42359 s.resizingEl.setWidth(newSize);
42361 onComplete(s, newSize);
42364 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42369 s.resizingEl.setHeight(newSize);
42371 onComplete(s, newSize);
42374 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42381 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42382 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42383 * Adapter that moves the splitter element to align with the resized sizing element.
42384 * Used with an absolute positioned SplitBar.
42385 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42386 * document.body, make sure you assign an id to the body element.
42388 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42389 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42390 this.container = Roo.get(container);
42393 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42394 init : function(s){
42395 this.basic.init(s);
42398 getElementSize : function(s){
42399 return this.basic.getElementSize(s);
42402 setElementSize : function(s, newSize, onComplete){
42403 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42406 moveSplitter : function(s){
42407 var yes = Roo.bootstrap.SplitBar;
42408 switch(s.placement){
42410 s.el.setX(s.resizingEl.getRight());
42413 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42416 s.el.setY(s.resizingEl.getBottom());
42419 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42426 * Orientation constant - Create a vertical SplitBar
42430 Roo.bootstrap.SplitBar.VERTICAL = 1;
42433 * Orientation constant - Create a horizontal SplitBar
42437 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42440 * Placement constant - The resizing element is to the left of the splitter element
42444 Roo.bootstrap.SplitBar.LEFT = 1;
42447 * Placement constant - The resizing element is to the right of the splitter element
42451 Roo.bootstrap.SplitBar.RIGHT = 2;
42454 * Placement constant - The resizing element is positioned above the splitter element
42458 Roo.bootstrap.SplitBar.TOP = 3;
42461 * Placement constant - The resizing element is positioned under splitter element
42465 Roo.bootstrap.SplitBar.BOTTOM = 4;
42468 * Ext JS Library 1.1.1
42469 * Copyright(c) 2006-2007, Ext JS, LLC.
42471 * Originally Released Under LGPL - original licence link has changed is not relivant.
42474 * <script type="text/javascript">
42478 * @class Roo.bootstrap.layout.Manager
42479 * @extends Roo.bootstrap.Component
42481 * Base class for layout managers.
42483 Roo.bootstrap.layout.Manager = function(config)
42485 this.monitorWindowResize = true; // do this before we apply configuration.
42487 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42493 /** false to disable window resize monitoring @type Boolean */
42499 * Fires when a layout is performed.
42500 * @param {Roo.LayoutManager} this
42504 * @event regionresized
42505 * Fires when the user resizes a region.
42506 * @param {Roo.LayoutRegion} region The resized region
42507 * @param {Number} newSize The new size (width for east/west, height for north/south)
42509 "regionresized" : true,
42511 * @event regioncollapsed
42512 * Fires when a region is collapsed.
42513 * @param {Roo.LayoutRegion} region The collapsed region
42515 "regioncollapsed" : true,
42517 * @event regionexpanded
42518 * Fires when a region is expanded.
42519 * @param {Roo.LayoutRegion} region The expanded region
42521 "regionexpanded" : true
42523 this.updating = false;
42526 this.el = Roo.get(config.el);
42532 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42537 monitorWindowResize : true,
42543 onRender : function(ct, position)
42546 this.el = Roo.get(ct);
42549 //this.fireEvent('render',this);
42553 initEvents: function()
42557 // ie scrollbar fix
42558 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42559 document.body.scroll = "no";
42560 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42561 this.el.position('relative');
42563 this.id = this.el.id;
42564 this.el.addClass("roo-layout-container");
42565 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42566 if(this.el.dom != document.body ) {
42567 this.el.on('resize', this.layout,this);
42568 this.el.on('show', this.layout,this);
42574 * Returns true if this layout is currently being updated
42575 * @return {Boolean}
42577 isUpdating : function(){
42578 return this.updating;
42582 * Suspend the LayoutManager from doing auto-layouts while
42583 * making multiple add or remove calls
42585 beginUpdate : function(){
42586 this.updating = true;
42590 * Restore auto-layouts and optionally disable the manager from performing a layout
42591 * @param {Boolean} noLayout true to disable a layout update
42593 endUpdate : function(noLayout){
42594 this.updating = false;
42600 layout: function(){
42604 onRegionResized : function(region, newSize){
42605 this.fireEvent("regionresized", region, newSize);
42609 onRegionCollapsed : function(region){
42610 this.fireEvent("regioncollapsed", region);
42613 onRegionExpanded : function(region){
42614 this.fireEvent("regionexpanded", region);
42618 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42619 * performs box-model adjustments.
42620 * @return {Object} The size as an object {width: (the width), height: (the height)}
42622 getViewSize : function()
42625 if(this.el.dom != document.body){
42626 size = this.el.getSize();
42628 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42630 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42631 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42636 * Returns the Element this layout is bound to.
42637 * @return {Roo.Element}
42639 getEl : function(){
42644 * Returns the specified region.
42645 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42646 * @return {Roo.LayoutRegion}
42648 getRegion : function(target){
42649 return this.regions[target.toLowerCase()];
42652 onWindowResize : function(){
42653 if(this.monitorWindowResize){
42660 * Ext JS Library 1.1.1
42661 * Copyright(c) 2006-2007, Ext JS, LLC.
42663 * Originally Released Under LGPL - original licence link has changed is not relivant.
42666 * <script type="text/javascript">
42669 * @class Roo.bootstrap.layout.Border
42670 * @extends Roo.bootstrap.layout.Manager
42671 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42672 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42673 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42674 * please see: examples/bootstrap/nested.html<br><br>
42676 <b>The container the layout is rendered into can be either the body element or any other element.
42677 If it is not the body element, the container needs to either be an absolute positioned element,
42678 or you will need to add "position:relative" to the css of the container. You will also need to specify
42679 the container size if it is not the body element.</b>
42682 * Create a new Border
42683 * @param {Object} config Configuration options
42685 Roo.bootstrap.layout.Border = function(config){
42686 config = config || {};
42687 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42691 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42692 if(config[region]){
42693 config[region].region = region;
42694 this.addRegion(config[region]);
42700 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
42702 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42705 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42708 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42711 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42714 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42717 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42723 parent : false, // this might point to a 'nest' or a ???
42726 * Creates and adds a new region if it doesn't already exist.
42727 * @param {String} target The target region key (north, south, east, west or center).
42728 * @param {Object} config The regions config object
42729 * @return {BorderLayoutRegion} The new region
42731 addRegion : function(config)
42733 if(!this.regions[config.region]){
42734 var r = this.factory(config);
42735 this.bindRegion(r);
42737 return this.regions[config.region];
42741 bindRegion : function(r){
42742 this.regions[r.config.region] = r;
42744 r.on("visibilitychange", this.layout, this);
42745 r.on("paneladded", this.layout, this);
42746 r.on("panelremoved", this.layout, this);
42747 r.on("invalidated", this.layout, this);
42748 r.on("resized", this.onRegionResized, this);
42749 r.on("collapsed", this.onRegionCollapsed, this);
42750 r.on("expanded", this.onRegionExpanded, this);
42754 * Performs a layout update.
42756 layout : function()
42758 if(this.updating) {
42762 // render all the rebions if they have not been done alreayd?
42763 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42764 if(this.regions[region] && !this.regions[region].bodyEl){
42765 this.regions[region].onRender(this.el)
42769 var size = this.getViewSize();
42770 var w = size.width;
42771 var h = size.height;
42776 //var x = 0, y = 0;
42778 var rs = this.regions;
42779 var north = rs["north"];
42780 var south = rs["south"];
42781 var west = rs["west"];
42782 var east = rs["east"];
42783 var center = rs["center"];
42784 //if(this.hideOnLayout){ // not supported anymore
42785 //c.el.setStyle("display", "none");
42787 if(north && north.isVisible()){
42788 var b = north.getBox();
42789 var m = north.getMargins();
42790 b.width = w - (m.left+m.right);
42793 centerY = b.height + b.y + m.bottom;
42794 centerH -= centerY;
42795 north.updateBox(this.safeBox(b));
42797 if(south && south.isVisible()){
42798 var b = south.getBox();
42799 var m = south.getMargins();
42800 b.width = w - (m.left+m.right);
42802 var totalHeight = (b.height + m.top + m.bottom);
42803 b.y = h - totalHeight + m.top;
42804 centerH -= totalHeight;
42805 south.updateBox(this.safeBox(b));
42807 if(west && west.isVisible()){
42808 var b = west.getBox();
42809 var m = west.getMargins();
42810 b.height = centerH - (m.top+m.bottom);
42812 b.y = centerY + m.top;
42813 var totalWidth = (b.width + m.left + m.right);
42814 centerX += totalWidth;
42815 centerW -= totalWidth;
42816 west.updateBox(this.safeBox(b));
42818 if(east && east.isVisible()){
42819 var b = east.getBox();
42820 var m = east.getMargins();
42821 b.height = centerH - (m.top+m.bottom);
42822 var totalWidth = (b.width + m.left + m.right);
42823 b.x = w - totalWidth + m.left;
42824 b.y = centerY + m.top;
42825 centerW -= totalWidth;
42826 east.updateBox(this.safeBox(b));
42829 var m = center.getMargins();
42831 x: centerX + m.left,
42832 y: centerY + m.top,
42833 width: centerW - (m.left+m.right),
42834 height: centerH - (m.top+m.bottom)
42836 //if(this.hideOnLayout){
42837 //center.el.setStyle("display", "block");
42839 center.updateBox(this.safeBox(centerBox));
42842 this.fireEvent("layout", this);
42846 safeBox : function(box){
42847 box.width = Math.max(0, box.width);
42848 box.height = Math.max(0, box.height);
42853 * Adds a ContentPanel (or subclass) to this layout.
42854 * @param {String} target The target region key (north, south, east, west or center).
42855 * @param {Roo.ContentPanel} panel The panel to add
42856 * @return {Roo.ContentPanel} The added panel
42858 add : function(target, panel){
42860 target = target.toLowerCase();
42861 return this.regions[target].add(panel);
42865 * Remove a ContentPanel (or subclass) to this layout.
42866 * @param {String} target The target region key (north, south, east, west or center).
42867 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42868 * @return {Roo.ContentPanel} The removed panel
42870 remove : function(target, panel){
42871 target = target.toLowerCase();
42872 return this.regions[target].remove(panel);
42876 * Searches all regions for a panel with the specified id
42877 * @param {String} panelId
42878 * @return {Roo.ContentPanel} The panel or null if it wasn't found
42880 findPanel : function(panelId){
42881 var rs = this.regions;
42882 for(var target in rs){
42883 if(typeof rs[target] != "function"){
42884 var p = rs[target].getPanel(panelId);
42894 * Searches all regions for a panel with the specified id and activates (shows) it.
42895 * @param {String/ContentPanel} panelId The panels id or the panel itself
42896 * @return {Roo.ContentPanel} The shown panel or null
42898 showPanel : function(panelId) {
42899 var rs = this.regions;
42900 for(var target in rs){
42901 var r = rs[target];
42902 if(typeof r != "function"){
42903 if(r.hasPanel(panelId)){
42904 return r.showPanel(panelId);
42912 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42913 * @param {Roo.state.Provider} provider (optional) An alternate state provider
42916 restoreState : function(provider){
42918 provider = Roo.state.Manager;
42920 var sm = new Roo.LayoutStateManager();
42921 sm.init(this, provider);
42927 * Adds a xtype elements to the layout.
42931 xtype : 'ContentPanel',
42938 xtype : 'NestedLayoutPanel',
42944 items : [ ... list of content panels or nested layout panels.. ]
42948 * @param {Object} cfg Xtype definition of item to add.
42950 addxtype : function(cfg)
42952 // basically accepts a pannel...
42953 // can accept a layout region..!?!?
42954 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42957 // theory? children can only be panels??
42959 //if (!cfg.xtype.match(/Panel$/)) {
42964 if (typeof(cfg.region) == 'undefined') {
42965 Roo.log("Failed to add Panel, region was not set");
42969 var region = cfg.region;
42975 xitems = cfg.items;
42980 if ( region == 'center') {
42981 Roo.log("Center: " + cfg.title);
42987 case 'Content': // ContentPanel (el, cfg)
42988 case 'Scroll': // ContentPanel (el, cfg)
42990 cfg.autoCreate = cfg.autoCreate || true;
42991 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42993 // var el = this.el.createChild();
42994 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42997 this.add(region, ret);
43001 case 'TreePanel': // our new panel!
43002 cfg.el = this.el.createChild();
43003 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43004 this.add(region, ret);
43009 // create a new Layout (which is a Border Layout...
43011 var clayout = cfg.layout;
43012 clayout.el = this.el.createChild();
43013 clayout.items = clayout.items || [];
43017 // replace this exitems with the clayout ones..
43018 xitems = clayout.items;
43020 // force background off if it's in center...
43021 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43022 cfg.background = false;
43024 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43027 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43028 //console.log('adding nested layout panel ' + cfg.toSource());
43029 this.add(region, ret);
43030 nb = {}; /// find first...
43035 // needs grid and region
43037 //var el = this.getRegion(region).el.createChild();
43039 *var el = this.el.createChild();
43040 // create the grid first...
43041 cfg.grid.container = el;
43042 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43045 if (region == 'center' && this.active ) {
43046 cfg.background = false;
43049 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43051 this.add(region, ret);
43053 if (cfg.background) {
43054 // render grid on panel activation (if panel background)
43055 ret.on('activate', function(gp) {
43056 if (!gp.grid.rendered) {
43057 // gp.grid.render(el);
43061 // cfg.grid.render(el);
43067 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43068 // it was the old xcomponent building that caused this before.
43069 // espeically if border is the top element in the tree.
43079 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43081 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43082 this.add(region, ret);
43086 throw "Can not add '" + cfg.xtype + "' to Border";
43092 this.beginUpdate();
43096 Roo.each(xitems, function(i) {
43097 region = nb && i.region ? i.region : false;
43099 var add = ret.addxtype(i);
43102 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43103 if (!i.background) {
43104 abn[region] = nb[region] ;
43111 // make the last non-background panel active..
43112 //if (nb) { Roo.log(abn); }
43115 for(var r in abn) {
43116 region = this.getRegion(r);
43118 // tried using nb[r], but it does not work..
43120 region.showPanel(abn[r]);
43131 factory : function(cfg)
43134 var validRegions = Roo.bootstrap.layout.Border.regions;
43136 var target = cfg.region;
43139 var r = Roo.bootstrap.layout;
43143 return new r.North(cfg);
43145 return new r.South(cfg);
43147 return new r.East(cfg);
43149 return new r.West(cfg);
43151 return new r.Center(cfg);
43153 throw 'Layout region "'+target+'" not supported.';
43160 * Ext JS Library 1.1.1
43161 * Copyright(c) 2006-2007, Ext JS, LLC.
43163 * Originally Released Under LGPL - original licence link has changed is not relivant.
43166 * <script type="text/javascript">
43170 * @class Roo.bootstrap.layout.Basic
43171 * @extends Roo.util.Observable
43172 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43173 * and does not have a titlebar, tabs or any other features. All it does is size and position
43174 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43175 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43176 * @cfg {string} region the region that it inhabits..
43177 * @cfg {bool} skipConfig skip config?
43181 Roo.bootstrap.layout.Basic = function(config){
43183 this.mgr = config.mgr;
43185 this.position = config.region;
43187 var skipConfig = config.skipConfig;
43191 * @scope Roo.BasicLayoutRegion
43195 * @event beforeremove
43196 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43197 * @param {Roo.LayoutRegion} this
43198 * @param {Roo.ContentPanel} panel The panel
43199 * @param {Object} e The cancel event object
43201 "beforeremove" : true,
43203 * @event invalidated
43204 * Fires when the layout for this region is changed.
43205 * @param {Roo.LayoutRegion} this
43207 "invalidated" : true,
43209 * @event visibilitychange
43210 * Fires when this region is shown or hidden
43211 * @param {Roo.LayoutRegion} this
43212 * @param {Boolean} visibility true or false
43214 "visibilitychange" : true,
43216 * @event paneladded
43217 * Fires when a panel is added.
43218 * @param {Roo.LayoutRegion} this
43219 * @param {Roo.ContentPanel} panel The panel
43221 "paneladded" : true,
43223 * @event panelremoved
43224 * Fires when a panel is removed.
43225 * @param {Roo.LayoutRegion} this
43226 * @param {Roo.ContentPanel} panel The panel
43228 "panelremoved" : true,
43230 * @event beforecollapse
43231 * Fires when this region before collapse.
43232 * @param {Roo.LayoutRegion} this
43234 "beforecollapse" : true,
43237 * Fires when this region is collapsed.
43238 * @param {Roo.LayoutRegion} this
43240 "collapsed" : true,
43243 * Fires when this region is expanded.
43244 * @param {Roo.LayoutRegion} this
43249 * Fires when this region is slid into view.
43250 * @param {Roo.LayoutRegion} this
43252 "slideshow" : true,
43255 * Fires when this region slides out of view.
43256 * @param {Roo.LayoutRegion} this
43258 "slidehide" : true,
43260 * @event panelactivated
43261 * Fires when a panel is activated.
43262 * @param {Roo.LayoutRegion} this
43263 * @param {Roo.ContentPanel} panel The activated panel
43265 "panelactivated" : true,
43268 * Fires when the user resizes this region.
43269 * @param {Roo.LayoutRegion} this
43270 * @param {Number} newSize The new size (width for east/west, height for north/south)
43274 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43275 this.panels = new Roo.util.MixedCollection();
43276 this.panels.getKey = this.getPanelId.createDelegate(this);
43278 this.activePanel = null;
43279 // ensure listeners are added...
43281 if (config.listeners || config.events) {
43282 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43283 listeners : config.listeners || {},
43284 events : config.events || {}
43288 if(skipConfig !== true){
43289 this.applyConfig(config);
43293 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43295 getPanelId : function(p){
43299 applyConfig : function(config){
43300 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43301 this.config = config;
43306 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43307 * the width, for horizontal (north, south) the height.
43308 * @param {Number} newSize The new width or height
43310 resizeTo : function(newSize){
43311 var el = this.el ? this.el :
43312 (this.activePanel ? this.activePanel.getEl() : null);
43314 switch(this.position){
43317 el.setWidth(newSize);
43318 this.fireEvent("resized", this, newSize);
43322 el.setHeight(newSize);
43323 this.fireEvent("resized", this, newSize);
43329 getBox : function(){
43330 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43333 getMargins : function(){
43334 return this.margins;
43337 updateBox : function(box){
43339 var el = this.activePanel.getEl();
43340 el.dom.style.left = box.x + "px";
43341 el.dom.style.top = box.y + "px";
43342 this.activePanel.setSize(box.width, box.height);
43346 * Returns the container element for this region.
43347 * @return {Roo.Element}
43349 getEl : function(){
43350 return this.activePanel;
43354 * Returns true if this region is currently visible.
43355 * @return {Boolean}
43357 isVisible : function(){
43358 return this.activePanel ? true : false;
43361 setActivePanel : function(panel){
43362 panel = this.getPanel(panel);
43363 if(this.activePanel && this.activePanel != panel){
43364 this.activePanel.setActiveState(false);
43365 this.activePanel.getEl().setLeftTop(-10000,-10000);
43367 this.activePanel = panel;
43368 panel.setActiveState(true);
43370 panel.setSize(this.box.width, this.box.height);
43372 this.fireEvent("panelactivated", this, panel);
43373 this.fireEvent("invalidated");
43377 * Show the specified panel.
43378 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43379 * @return {Roo.ContentPanel} The shown panel or null
43381 showPanel : function(panel){
43382 panel = this.getPanel(panel);
43384 this.setActivePanel(panel);
43390 * Get the active panel for this region.
43391 * @return {Roo.ContentPanel} The active panel or null
43393 getActivePanel : function(){
43394 return this.activePanel;
43398 * Add the passed ContentPanel(s)
43399 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43400 * @return {Roo.ContentPanel} The panel added (if only one was added)
43402 add : function(panel){
43403 if(arguments.length > 1){
43404 for(var i = 0, len = arguments.length; i < len; i++) {
43405 this.add(arguments[i]);
43409 if(this.hasPanel(panel)){
43410 this.showPanel(panel);
43413 var el = panel.getEl();
43414 if(el.dom.parentNode != this.mgr.el.dom){
43415 this.mgr.el.dom.appendChild(el.dom);
43417 if(panel.setRegion){
43418 panel.setRegion(this);
43420 this.panels.add(panel);
43421 el.setStyle("position", "absolute");
43422 if(!panel.background){
43423 this.setActivePanel(panel);
43424 if(this.config.initialSize && this.panels.getCount()==1){
43425 this.resizeTo(this.config.initialSize);
43428 this.fireEvent("paneladded", this, panel);
43433 * Returns true if the panel is in this region.
43434 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43435 * @return {Boolean}
43437 hasPanel : function(panel){
43438 if(typeof panel == "object"){ // must be panel obj
43439 panel = panel.getId();
43441 return this.getPanel(panel) ? true : false;
43445 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43446 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43447 * @param {Boolean} preservePanel Overrides the config preservePanel option
43448 * @return {Roo.ContentPanel} The panel that was removed
43450 remove : function(panel, preservePanel){
43451 panel = this.getPanel(panel);
43456 this.fireEvent("beforeremove", this, panel, e);
43457 if(e.cancel === true){
43460 var panelId = panel.getId();
43461 this.panels.removeKey(panelId);
43466 * Returns the panel specified or null if it's not in this region.
43467 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43468 * @return {Roo.ContentPanel}
43470 getPanel : function(id){
43471 if(typeof id == "object"){ // must be panel obj
43474 return this.panels.get(id);
43478 * Returns this regions position (north/south/east/west/center).
43481 getPosition: function(){
43482 return this.position;
43486 * Ext JS Library 1.1.1
43487 * Copyright(c) 2006-2007, Ext JS, LLC.
43489 * Originally Released Under LGPL - original licence link has changed is not relivant.
43492 * <script type="text/javascript">
43496 * @class Roo.bootstrap.layout.Region
43497 * @extends Roo.bootstrap.layout.Basic
43498 * This class represents a region in a layout manager.
43500 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43501 * @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})
43502 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43503 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43504 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43505 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43506 * @cfg {String} title The title for the region (overrides panel titles)
43507 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43508 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43509 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43510 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43511 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43512 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43513 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43514 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43515 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43516 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43518 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43519 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43520 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43521 * @cfg {Number} width For East/West panels
43522 * @cfg {Number} height For North/South panels
43523 * @cfg {Boolean} split To show the splitter
43524 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43526 * @cfg {string} cls Extra CSS classes to add to region
43528 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43529 * @cfg {string} region the region that it inhabits..
43532 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43533 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43535 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43536 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43537 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43539 Roo.bootstrap.layout.Region = function(config)
43541 this.applyConfig(config);
43543 var mgr = config.mgr;
43544 var pos = config.region;
43545 config.skipConfig = true;
43546 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43549 this.onRender(mgr.el);
43552 this.visible = true;
43553 this.collapsed = false;
43554 this.unrendered_panels = [];
43557 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43559 position: '', // set by wrapper (eg. north/south etc..)
43560 unrendered_panels : null, // unrendered panels.
43562 tabPosition : false,
43564 mgr: false, // points to 'Border'
43567 createBody : function(){
43568 /** This region's body element
43569 * @type Roo.Element */
43570 this.bodyEl = this.el.createChild({
43572 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43576 onRender: function(ctr, pos)
43578 var dh = Roo.DomHelper;
43579 /** This region's container element
43580 * @type Roo.Element */
43581 this.el = dh.append(ctr.dom, {
43583 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43585 /** This region's title element
43586 * @type Roo.Element */
43588 this.titleEl = dh.append(this.el.dom, {
43590 unselectable: "on",
43591 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43593 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43594 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43598 this.titleEl.enableDisplayMode();
43599 /** This region's title text element
43600 * @type HTMLElement */
43601 this.titleTextEl = this.titleEl.dom.firstChild;
43602 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43604 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43605 this.closeBtn.enableDisplayMode();
43606 this.closeBtn.on("click", this.closeClicked, this);
43607 this.closeBtn.hide();
43609 this.createBody(this.config);
43610 if(this.config.hideWhenEmpty){
43612 this.on("paneladded", this.validateVisibility, this);
43613 this.on("panelremoved", this.validateVisibility, this);
43615 if(this.autoScroll){
43616 this.bodyEl.setStyle("overflow", "auto");
43618 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43620 //if(c.titlebar !== false){
43621 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43622 this.titleEl.hide();
43624 this.titleEl.show();
43625 if(this.config.title){
43626 this.titleTextEl.innerHTML = this.config.title;
43630 if(this.config.collapsed){
43631 this.collapse(true);
43633 if(this.config.hidden){
43637 if (this.unrendered_panels && this.unrendered_panels.length) {
43638 for (var i =0;i< this.unrendered_panels.length; i++) {
43639 this.add(this.unrendered_panels[i]);
43641 this.unrendered_panels = null;
43647 applyConfig : function(c)
43650 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43651 var dh = Roo.DomHelper;
43652 if(c.titlebar !== false){
43653 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43654 this.collapseBtn.on("click", this.collapse, this);
43655 this.collapseBtn.enableDisplayMode();
43657 if(c.showPin === true || this.showPin){
43658 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43659 this.stickBtn.enableDisplayMode();
43660 this.stickBtn.on("click", this.expand, this);
43661 this.stickBtn.hide();
43666 /** This region's collapsed element
43667 * @type Roo.Element */
43670 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43671 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43674 if(c.floatable !== false){
43675 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43676 this.collapsedEl.on("click", this.collapseClick, this);
43679 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43680 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43681 id: "message", unselectable: "on", style:{"float":"left"}});
43682 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43684 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43685 this.expandBtn.on("click", this.expand, this);
43689 if(this.collapseBtn){
43690 this.collapseBtn.setVisible(c.collapsible == true);
43693 this.cmargins = c.cmargins || this.cmargins ||
43694 (this.position == "west" || this.position == "east" ?
43695 {top: 0, left: 2, right:2, bottom: 0} :
43696 {top: 2, left: 0, right:0, bottom: 2});
43698 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43701 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43703 this.autoScroll = c.autoScroll || false;
43708 this.duration = c.duration || .30;
43709 this.slideDuration = c.slideDuration || .45;
43714 * Returns true if this region is currently visible.
43715 * @return {Boolean}
43717 isVisible : function(){
43718 return this.visible;
43722 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43723 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
43725 //setCollapsedTitle : function(title){
43726 // title = title || " ";
43727 // if(this.collapsedTitleTextEl){
43728 // this.collapsedTitleTextEl.innerHTML = title;
43732 getBox : function(){
43734 // if(!this.collapsed){
43735 b = this.el.getBox(false, true);
43737 // b = this.collapsedEl.getBox(false, true);
43742 getMargins : function(){
43743 return this.margins;
43744 //return this.collapsed ? this.cmargins : this.margins;
43747 highlight : function(){
43748 this.el.addClass("x-layout-panel-dragover");
43751 unhighlight : function(){
43752 this.el.removeClass("x-layout-panel-dragover");
43755 updateBox : function(box)
43757 if (!this.bodyEl) {
43758 return; // not rendered yet..
43762 if(!this.collapsed){
43763 this.el.dom.style.left = box.x + "px";
43764 this.el.dom.style.top = box.y + "px";
43765 this.updateBody(box.width, box.height);
43767 this.collapsedEl.dom.style.left = box.x + "px";
43768 this.collapsedEl.dom.style.top = box.y + "px";
43769 this.collapsedEl.setSize(box.width, box.height);
43772 this.tabs.autoSizeTabs();
43776 updateBody : function(w, h)
43779 this.el.setWidth(w);
43780 w -= this.el.getBorderWidth("rl");
43781 if(this.config.adjustments){
43782 w += this.config.adjustments[0];
43785 if(h !== null && h > 0){
43786 this.el.setHeight(h);
43787 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43788 h -= this.el.getBorderWidth("tb");
43789 if(this.config.adjustments){
43790 h += this.config.adjustments[1];
43792 this.bodyEl.setHeight(h);
43794 h = this.tabs.syncHeight(h);
43797 if(this.panelSize){
43798 w = w !== null ? w : this.panelSize.width;
43799 h = h !== null ? h : this.panelSize.height;
43801 if(this.activePanel){
43802 var el = this.activePanel.getEl();
43803 w = w !== null ? w : el.getWidth();
43804 h = h !== null ? h : el.getHeight();
43805 this.panelSize = {width: w, height: h};
43806 this.activePanel.setSize(w, h);
43808 if(Roo.isIE && this.tabs){
43809 this.tabs.el.repaint();
43814 * Returns the container element for this region.
43815 * @return {Roo.Element}
43817 getEl : function(){
43822 * Hides this region.
43825 //if(!this.collapsed){
43826 this.el.dom.style.left = "-2000px";
43829 // this.collapsedEl.dom.style.left = "-2000px";
43830 // this.collapsedEl.hide();
43832 this.visible = false;
43833 this.fireEvent("visibilitychange", this, false);
43837 * Shows this region if it was previously hidden.
43840 //if(!this.collapsed){
43843 // this.collapsedEl.show();
43845 this.visible = true;
43846 this.fireEvent("visibilitychange", this, true);
43849 closeClicked : function(){
43850 if(this.activePanel){
43851 this.remove(this.activePanel);
43855 collapseClick : function(e){
43857 e.stopPropagation();
43860 e.stopPropagation();
43866 * Collapses this region.
43867 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43870 collapse : function(skipAnim, skipCheck = false){
43871 if(this.collapsed) {
43875 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43877 this.collapsed = true;
43879 this.split.el.hide();
43881 if(this.config.animate && skipAnim !== true){
43882 this.fireEvent("invalidated", this);
43883 this.animateCollapse();
43885 this.el.setLocation(-20000,-20000);
43887 this.collapsedEl.show();
43888 this.fireEvent("collapsed", this);
43889 this.fireEvent("invalidated", this);
43895 animateCollapse : function(){
43900 * Expands this region if it was previously collapsed.
43901 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43902 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43905 expand : function(e, skipAnim){
43907 e.stopPropagation();
43909 if(!this.collapsed || this.el.hasActiveFx()) {
43913 this.afterSlideIn();
43916 this.collapsed = false;
43917 if(this.config.animate && skipAnim !== true){
43918 this.animateExpand();
43922 this.split.el.show();
43924 this.collapsedEl.setLocation(-2000,-2000);
43925 this.collapsedEl.hide();
43926 this.fireEvent("invalidated", this);
43927 this.fireEvent("expanded", this);
43931 animateExpand : function(){
43935 initTabs : function()
43937 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43939 var ts = new Roo.bootstrap.panel.Tabs({
43940 el: this.bodyEl.dom,
43942 tabPosition: this.tabPosition ? this.tabPosition : 'top',
43943 disableTooltips: this.config.disableTabTips,
43944 toolbar : this.config.toolbar
43947 if(this.config.hideTabs){
43948 ts.stripWrap.setDisplayed(false);
43951 ts.resizeTabs = this.config.resizeTabs === true;
43952 ts.minTabWidth = this.config.minTabWidth || 40;
43953 ts.maxTabWidth = this.config.maxTabWidth || 250;
43954 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43955 ts.monitorResize = false;
43956 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43957 ts.bodyEl.addClass('roo-layout-tabs-body');
43958 this.panels.each(this.initPanelAsTab, this);
43961 initPanelAsTab : function(panel){
43962 var ti = this.tabs.addTab(
43966 this.config.closeOnTab && panel.isClosable(),
43969 if(panel.tabTip !== undefined){
43970 ti.setTooltip(panel.tabTip);
43972 ti.on("activate", function(){
43973 this.setActivePanel(panel);
43976 if(this.config.closeOnTab){
43977 ti.on("beforeclose", function(t, e){
43979 this.remove(panel);
43983 panel.tabItem = ti;
43988 updatePanelTitle : function(panel, title)
43990 if(this.activePanel == panel){
43991 this.updateTitle(title);
43994 var ti = this.tabs.getTab(panel.getEl().id);
43996 if(panel.tabTip !== undefined){
43997 ti.setTooltip(panel.tabTip);
44002 updateTitle : function(title){
44003 if(this.titleTextEl && !this.config.title){
44004 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44008 setActivePanel : function(panel)
44010 panel = this.getPanel(panel);
44011 if(this.activePanel && this.activePanel != panel){
44012 if(this.activePanel.setActiveState(false) === false){
44016 this.activePanel = panel;
44017 panel.setActiveState(true);
44018 if(this.panelSize){
44019 panel.setSize(this.panelSize.width, this.panelSize.height);
44022 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44024 this.updateTitle(panel.getTitle());
44026 this.fireEvent("invalidated", this);
44028 this.fireEvent("panelactivated", this, panel);
44032 * Shows the specified panel.
44033 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44034 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44036 showPanel : function(panel)
44038 panel = this.getPanel(panel);
44041 var tab = this.tabs.getTab(panel.getEl().id);
44042 if(tab.isHidden()){
44043 this.tabs.unhideTab(tab.id);
44047 this.setActivePanel(panel);
44054 * Get the active panel for this region.
44055 * @return {Roo.ContentPanel} The active panel or null
44057 getActivePanel : function(){
44058 return this.activePanel;
44061 validateVisibility : function(){
44062 if(this.panels.getCount() < 1){
44063 this.updateTitle(" ");
44064 this.closeBtn.hide();
44067 if(!this.isVisible()){
44074 * Adds the passed ContentPanel(s) to this region.
44075 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44076 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44078 add : function(panel)
44080 if(arguments.length > 1){
44081 for(var i = 0, len = arguments.length; i < len; i++) {
44082 this.add(arguments[i]);
44087 // if we have not been rendered yet, then we can not really do much of this..
44088 if (!this.bodyEl) {
44089 this.unrendered_panels.push(panel);
44096 if(this.hasPanel(panel)){
44097 this.showPanel(panel);
44100 panel.setRegion(this);
44101 this.panels.add(panel);
44102 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44103 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44104 // and hide them... ???
44105 this.bodyEl.dom.appendChild(panel.getEl().dom);
44106 if(panel.background !== true){
44107 this.setActivePanel(panel);
44109 this.fireEvent("paneladded", this, panel);
44116 this.initPanelAsTab(panel);
44120 if(panel.background !== true){
44121 this.tabs.activate(panel.getEl().id);
44123 this.fireEvent("paneladded", this, panel);
44128 * Hides the tab for the specified panel.
44129 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44131 hidePanel : function(panel){
44132 if(this.tabs && (panel = this.getPanel(panel))){
44133 this.tabs.hideTab(panel.getEl().id);
44138 * Unhides the tab for a previously hidden panel.
44139 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44141 unhidePanel : function(panel){
44142 if(this.tabs && (panel = this.getPanel(panel))){
44143 this.tabs.unhideTab(panel.getEl().id);
44147 clearPanels : function(){
44148 while(this.panels.getCount() > 0){
44149 this.remove(this.panels.first());
44154 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44155 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44156 * @param {Boolean} preservePanel Overrides the config preservePanel option
44157 * @return {Roo.ContentPanel} The panel that was removed
44159 remove : function(panel, preservePanel)
44161 panel = this.getPanel(panel);
44166 this.fireEvent("beforeremove", this, panel, e);
44167 if(e.cancel === true){
44170 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44171 var panelId = panel.getId();
44172 this.panels.removeKey(panelId);
44174 document.body.appendChild(panel.getEl().dom);
44177 this.tabs.removeTab(panel.getEl().id);
44178 }else if (!preservePanel){
44179 this.bodyEl.dom.removeChild(panel.getEl().dom);
44181 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44182 var p = this.panels.first();
44183 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44184 tempEl.appendChild(p.getEl().dom);
44185 this.bodyEl.update("");
44186 this.bodyEl.dom.appendChild(p.getEl().dom);
44188 this.updateTitle(p.getTitle());
44190 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44191 this.setActivePanel(p);
44193 panel.setRegion(null);
44194 if(this.activePanel == panel){
44195 this.activePanel = null;
44197 if(this.config.autoDestroy !== false && preservePanel !== true){
44198 try{panel.destroy();}catch(e){}
44200 this.fireEvent("panelremoved", this, panel);
44205 * Returns the TabPanel component used by this region
44206 * @return {Roo.TabPanel}
44208 getTabs : function(){
44212 createTool : function(parentEl, className){
44213 var btn = Roo.DomHelper.append(parentEl, {
44215 cls: "x-layout-tools-button",
44218 cls: "roo-layout-tools-button-inner " + className,
44222 btn.addClassOnOver("roo-layout-tools-button-over");
44227 * Ext JS Library 1.1.1
44228 * Copyright(c) 2006-2007, Ext JS, LLC.
44230 * Originally Released Under LGPL - original licence link has changed is not relivant.
44233 * <script type="text/javascript">
44239 * @class Roo.SplitLayoutRegion
44240 * @extends Roo.LayoutRegion
44241 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44243 Roo.bootstrap.layout.Split = function(config){
44244 this.cursor = config.cursor;
44245 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44248 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44250 splitTip : "Drag to resize.",
44251 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44252 useSplitTips : false,
44254 applyConfig : function(config){
44255 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44258 onRender : function(ctr,pos) {
44260 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44261 if(!this.config.split){
44266 var splitEl = Roo.DomHelper.append(ctr.dom, {
44268 id: this.el.id + "-split",
44269 cls: "roo-layout-split roo-layout-split-"+this.position,
44272 /** The SplitBar for this region
44273 * @type Roo.SplitBar */
44274 // does not exist yet...
44275 Roo.log([this.position, this.orientation]);
44277 this.split = new Roo.bootstrap.SplitBar({
44278 dragElement : splitEl,
44279 resizingElement: this.el,
44280 orientation : this.orientation
44283 this.split.on("moved", this.onSplitMove, this);
44284 this.split.useShim = this.config.useShim === true;
44285 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44286 if(this.useSplitTips){
44287 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44289 //if(config.collapsible){
44290 // this.split.el.on("dblclick", this.collapse, this);
44293 if(typeof this.config.minSize != "undefined"){
44294 this.split.minSize = this.config.minSize;
44296 if(typeof this.config.maxSize != "undefined"){
44297 this.split.maxSize = this.config.maxSize;
44299 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44300 this.hideSplitter();
44305 getHMaxSize : function(){
44306 var cmax = this.config.maxSize || 10000;
44307 var center = this.mgr.getRegion("center");
44308 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44311 getVMaxSize : function(){
44312 var cmax = this.config.maxSize || 10000;
44313 var center = this.mgr.getRegion("center");
44314 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44317 onSplitMove : function(split, newSize){
44318 this.fireEvent("resized", this, newSize);
44322 * Returns the {@link Roo.SplitBar} for this region.
44323 * @return {Roo.SplitBar}
44325 getSplitBar : function(){
44330 this.hideSplitter();
44331 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44334 hideSplitter : function(){
44336 this.split.el.setLocation(-2000,-2000);
44337 this.split.el.hide();
44343 this.split.el.show();
44345 Roo.bootstrap.layout.Split.superclass.show.call(this);
44348 beforeSlide: function(){
44349 if(Roo.isGecko){// firefox overflow auto bug workaround
44350 this.bodyEl.clip();
44352 this.tabs.bodyEl.clip();
44354 if(this.activePanel){
44355 this.activePanel.getEl().clip();
44357 if(this.activePanel.beforeSlide){
44358 this.activePanel.beforeSlide();
44364 afterSlide : function(){
44365 if(Roo.isGecko){// firefox overflow auto bug workaround
44366 this.bodyEl.unclip();
44368 this.tabs.bodyEl.unclip();
44370 if(this.activePanel){
44371 this.activePanel.getEl().unclip();
44372 if(this.activePanel.afterSlide){
44373 this.activePanel.afterSlide();
44379 initAutoHide : function(){
44380 if(this.autoHide !== false){
44381 if(!this.autoHideHd){
44382 var st = new Roo.util.DelayedTask(this.slideIn, this);
44383 this.autoHideHd = {
44384 "mouseout": function(e){
44385 if(!e.within(this.el, true)){
44389 "mouseover" : function(e){
44395 this.el.on(this.autoHideHd);
44399 clearAutoHide : function(){
44400 if(this.autoHide !== false){
44401 this.el.un("mouseout", this.autoHideHd.mouseout);
44402 this.el.un("mouseover", this.autoHideHd.mouseover);
44406 clearMonitor : function(){
44407 Roo.get(document).un("click", this.slideInIf, this);
44410 // these names are backwards but not changed for compat
44411 slideOut : function(){
44412 if(this.isSlid || this.el.hasActiveFx()){
44415 this.isSlid = true;
44416 if(this.collapseBtn){
44417 this.collapseBtn.hide();
44419 this.closeBtnState = this.closeBtn.getStyle('display');
44420 this.closeBtn.hide();
44422 this.stickBtn.show();
44425 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44426 this.beforeSlide();
44427 this.el.setStyle("z-index", 10001);
44428 this.el.slideIn(this.getSlideAnchor(), {
44429 callback: function(){
44431 this.initAutoHide();
44432 Roo.get(document).on("click", this.slideInIf, this);
44433 this.fireEvent("slideshow", this);
44440 afterSlideIn : function(){
44441 this.clearAutoHide();
44442 this.isSlid = false;
44443 this.clearMonitor();
44444 this.el.setStyle("z-index", "");
44445 if(this.collapseBtn){
44446 this.collapseBtn.show();
44448 this.closeBtn.setStyle('display', this.closeBtnState);
44450 this.stickBtn.hide();
44452 this.fireEvent("slidehide", this);
44455 slideIn : function(cb){
44456 if(!this.isSlid || this.el.hasActiveFx()){
44460 this.isSlid = false;
44461 this.beforeSlide();
44462 this.el.slideOut(this.getSlideAnchor(), {
44463 callback: function(){
44464 this.el.setLeftTop(-10000, -10000);
44466 this.afterSlideIn();
44474 slideInIf : function(e){
44475 if(!e.within(this.el)){
44480 animateCollapse : function(){
44481 this.beforeSlide();
44482 this.el.setStyle("z-index", 20000);
44483 var anchor = this.getSlideAnchor();
44484 this.el.slideOut(anchor, {
44485 callback : function(){
44486 this.el.setStyle("z-index", "");
44487 this.collapsedEl.slideIn(anchor, {duration:.3});
44489 this.el.setLocation(-10000,-10000);
44491 this.fireEvent("collapsed", this);
44498 animateExpand : function(){
44499 this.beforeSlide();
44500 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44501 this.el.setStyle("z-index", 20000);
44502 this.collapsedEl.hide({
44505 this.el.slideIn(this.getSlideAnchor(), {
44506 callback : function(){
44507 this.el.setStyle("z-index", "");
44510 this.split.el.show();
44512 this.fireEvent("invalidated", this);
44513 this.fireEvent("expanded", this);
44541 getAnchor : function(){
44542 return this.anchors[this.position];
44545 getCollapseAnchor : function(){
44546 return this.canchors[this.position];
44549 getSlideAnchor : function(){
44550 return this.sanchors[this.position];
44553 getAlignAdj : function(){
44554 var cm = this.cmargins;
44555 switch(this.position){
44571 getExpandAdj : function(){
44572 var c = this.collapsedEl, cm = this.cmargins;
44573 switch(this.position){
44575 return [-(cm.right+c.getWidth()+cm.left), 0];
44578 return [cm.right+c.getWidth()+cm.left, 0];
44581 return [0, -(cm.top+cm.bottom+c.getHeight())];
44584 return [0, cm.top+cm.bottom+c.getHeight()];
44590 * Ext JS Library 1.1.1
44591 * Copyright(c) 2006-2007, Ext JS, LLC.
44593 * Originally Released Under LGPL - original licence link has changed is not relivant.
44596 * <script type="text/javascript">
44599 * These classes are private internal classes
44601 Roo.bootstrap.layout.Center = function(config){
44602 config.region = "center";
44603 Roo.bootstrap.layout.Region.call(this, config);
44604 this.visible = true;
44605 this.minWidth = config.minWidth || 20;
44606 this.minHeight = config.minHeight || 20;
44609 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44611 // center panel can't be hidden
44615 // center panel can't be hidden
44618 getMinWidth: function(){
44619 return this.minWidth;
44622 getMinHeight: function(){
44623 return this.minHeight;
44637 Roo.bootstrap.layout.North = function(config)
44639 config.region = 'north';
44640 config.cursor = 'n-resize';
44642 Roo.bootstrap.layout.Split.call(this, config);
44646 this.split.placement = Roo.bootstrap.SplitBar.TOP;
44647 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44648 this.split.el.addClass("roo-layout-split-v");
44650 //var size = config.initialSize || config.height;
44651 //if(this.el && typeof size != "undefined"){
44652 // this.el.setHeight(size);
44655 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44657 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44660 onRender : function(ctr, pos)
44662 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44663 var size = this.config.initialSize || this.config.height;
44664 if(this.el && typeof size != "undefined"){
44665 this.el.setHeight(size);
44670 getBox : function(){
44671 if(this.collapsed){
44672 return this.collapsedEl.getBox();
44674 var box = this.el.getBox();
44676 box.height += this.split.el.getHeight();
44681 updateBox : function(box){
44682 if(this.split && !this.collapsed){
44683 box.height -= this.split.el.getHeight();
44684 this.split.el.setLeft(box.x);
44685 this.split.el.setTop(box.y+box.height);
44686 this.split.el.setWidth(box.width);
44688 if(this.collapsed){
44689 this.updateBody(box.width, null);
44691 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44699 Roo.bootstrap.layout.South = function(config){
44700 config.region = 'south';
44701 config.cursor = 's-resize';
44702 Roo.bootstrap.layout.Split.call(this, config);
44704 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44705 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44706 this.split.el.addClass("roo-layout-split-v");
44711 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44712 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44714 onRender : function(ctr, pos)
44716 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44717 var size = this.config.initialSize || this.config.height;
44718 if(this.el && typeof size != "undefined"){
44719 this.el.setHeight(size);
44724 getBox : function(){
44725 if(this.collapsed){
44726 return this.collapsedEl.getBox();
44728 var box = this.el.getBox();
44730 var sh = this.split.el.getHeight();
44737 updateBox : function(box){
44738 if(this.split && !this.collapsed){
44739 var sh = this.split.el.getHeight();
44742 this.split.el.setLeft(box.x);
44743 this.split.el.setTop(box.y-sh);
44744 this.split.el.setWidth(box.width);
44746 if(this.collapsed){
44747 this.updateBody(box.width, null);
44749 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44753 Roo.bootstrap.layout.East = function(config){
44754 config.region = "east";
44755 config.cursor = "e-resize";
44756 Roo.bootstrap.layout.Split.call(this, config);
44758 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44759 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44760 this.split.el.addClass("roo-layout-split-h");
44764 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44765 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44767 onRender : function(ctr, pos)
44769 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44770 var size = this.config.initialSize || this.config.width;
44771 if(this.el && typeof size != "undefined"){
44772 this.el.setWidth(size);
44777 getBox : function(){
44778 if(this.collapsed){
44779 return this.collapsedEl.getBox();
44781 var box = this.el.getBox();
44783 var sw = this.split.el.getWidth();
44790 updateBox : function(box){
44791 if(this.split && !this.collapsed){
44792 var sw = this.split.el.getWidth();
44794 this.split.el.setLeft(box.x);
44795 this.split.el.setTop(box.y);
44796 this.split.el.setHeight(box.height);
44799 if(this.collapsed){
44800 this.updateBody(null, box.height);
44802 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44806 Roo.bootstrap.layout.West = function(config){
44807 config.region = "west";
44808 config.cursor = "w-resize";
44810 Roo.bootstrap.layout.Split.call(this, config);
44812 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44813 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44814 this.split.el.addClass("roo-layout-split-h");
44818 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44819 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44821 onRender: function(ctr, pos)
44823 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44824 var size = this.config.initialSize || this.config.width;
44825 if(typeof size != "undefined"){
44826 this.el.setWidth(size);
44830 getBox : function(){
44831 if(this.collapsed){
44832 return this.collapsedEl.getBox();
44834 var box = this.el.getBox();
44835 if (box.width == 0) {
44836 box.width = this.config.width; // kludge?
44839 box.width += this.split.el.getWidth();
44844 updateBox : function(box){
44845 if(this.split && !this.collapsed){
44846 var sw = this.split.el.getWidth();
44848 this.split.el.setLeft(box.x+box.width);
44849 this.split.el.setTop(box.y);
44850 this.split.el.setHeight(box.height);
44852 if(this.collapsed){
44853 this.updateBody(null, box.height);
44855 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44859 * Ext JS Library 1.1.1
44860 * Copyright(c) 2006-2007, Ext JS, LLC.
44862 * Originally Released Under LGPL - original licence link has changed is not relivant.
44865 * <script type="text/javascript">
44868 * @class Roo.bootstrap.paenl.Content
44869 * @extends Roo.util.Observable
44870 * @children Roo.bootstrap.Component
44871 * @parent builder Roo.bootstrap.layout.Border
44872 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44873 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
44874 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
44875 * @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
44876 * @cfg {Boolean} closable True if the panel can be closed/removed
44877 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44878 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44879 * @cfg {Toolbar} toolbar A toolbar for this panel
44880 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44881 * @cfg {String} title The title for this panel
44882 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44883 * @cfg {String} url Calls {@link #setUrl} with this value
44884 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44885 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44886 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44887 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
44888 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
44889 * @cfg {Boolean} badges render the badges
44890 * @cfg {String} cls extra classes to use
44891 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44894 * Create a new ContentPanel.
44895 * @param {String/Object} config A string to set only the title or a config object
44898 Roo.bootstrap.panel.Content = function( config){
44900 this.tpl = config.tpl || false;
44902 var el = config.el;
44903 var content = config.content;
44905 if(config.autoCreate){ // xtype is available if this is called from factory
44908 this.el = Roo.get(el);
44909 if(!this.el && config && config.autoCreate){
44910 if(typeof config.autoCreate == "object"){
44911 if(!config.autoCreate.id){
44912 config.autoCreate.id = config.id||el;
44914 this.el = Roo.DomHelper.append(document.body,
44915 config.autoCreate, true);
44919 cls: (config.cls || '') +
44920 (config.background ? ' bg-' + config.background : '') +
44921 " roo-layout-inactive-content",
44924 if (config.iframe) {
44928 style : 'border: 0px',
44929 src : 'about:blank'
44935 elcfg.html = config.html;
44939 this.el = Roo.DomHelper.append(document.body, elcfg , true);
44940 if (config.iframe) {
44941 this.iframeEl = this.el.select('iframe',true).first();
44946 this.closable = false;
44947 this.loaded = false;
44948 this.active = false;
44951 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44953 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44955 this.wrapEl = this.el; //this.el.wrap();
44957 if (config.toolbar.items) {
44958 ti = config.toolbar.items ;
44959 delete config.toolbar.items ;
44963 this.toolbar.render(this.wrapEl, 'before');
44964 for(var i =0;i < ti.length;i++) {
44965 // Roo.log(['add child', items[i]]);
44966 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44968 this.toolbar.items = nitems;
44969 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44970 delete config.toolbar;
44974 // xtype created footer. - not sure if will work as we normally have to render first..
44975 if (this.footer && !this.footer.el && this.footer.xtype) {
44976 if (!this.wrapEl) {
44977 this.wrapEl = this.el.wrap();
44980 this.footer.container = this.wrapEl.createChild();
44982 this.footer = Roo.factory(this.footer, Roo);
44987 if(typeof config == "string"){
44988 this.title = config;
44990 Roo.apply(this, config);
44994 this.resizeEl = Roo.get(this.resizeEl, true);
44996 this.resizeEl = this.el;
44998 // handle view.xtype
45006 * Fires when this panel is activated.
45007 * @param {Roo.ContentPanel} this
45011 * @event deactivate
45012 * Fires when this panel is activated.
45013 * @param {Roo.ContentPanel} this
45015 "deactivate" : true,
45019 * Fires when this panel is resized if fitToFrame is true.
45020 * @param {Roo.ContentPanel} this
45021 * @param {Number} width The width after any component adjustments
45022 * @param {Number} height The height after any component adjustments
45028 * Fires when this tab is created
45029 * @param {Roo.ContentPanel} this
45035 * Fires when this content is scrolled
45036 * @param {Roo.ContentPanel} this
45037 * @param {Event} scrollEvent
45048 if(this.autoScroll && !this.iframe){
45049 this.resizeEl.setStyle("overflow", "auto");
45050 this.resizeEl.on('scroll', this.onScroll, this);
45052 // fix randome scrolling
45053 //this.el.on('scroll', function() {
45054 // Roo.log('fix random scolling');
45055 // this.scrollTo('top',0);
45058 content = content || this.content;
45060 this.setContent(content);
45062 if(config && config.url){
45063 this.setUrl(this.url, this.params, this.loadOnce);
45068 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45070 if (this.view && typeof(this.view.xtype) != 'undefined') {
45071 this.view.el = this.el.appendChild(document.createElement("div"));
45072 this.view = Roo.factory(this.view);
45073 this.view.render && this.view.render(false, '');
45077 this.fireEvent('render', this);
45080 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45090 /* Resize Element - use this to work out scroll etc. */
45093 setRegion : function(region){
45094 this.region = region;
45095 this.setActiveClass(region && !this.background);
45099 setActiveClass: function(state)
45102 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45103 this.el.setStyle('position','relative');
45105 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45106 this.el.setStyle('position', 'absolute');
45111 * Returns the toolbar for this Panel if one was configured.
45112 * @return {Roo.Toolbar}
45114 getToolbar : function(){
45115 return this.toolbar;
45118 setActiveState : function(active)
45120 this.active = active;
45121 this.setActiveClass(active);
45123 if(this.fireEvent("deactivate", this) === false){
45128 this.fireEvent("activate", this);
45132 * Updates this panel's element (not for iframe)
45133 * @param {String} content The new content
45134 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45136 setContent : function(content, loadScripts){
45141 this.el.update(content, loadScripts);
45144 ignoreResize : function(w, h)
45146 //return false; // always resize?
45147 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45150 this.lastSize = {width: w, height: h};
45155 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45156 * @return {Roo.UpdateManager} The UpdateManager
45158 getUpdateManager : function(){
45162 return this.el.getUpdateManager();
45165 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45166 * Does not work with IFRAME contents
45167 * @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:
45170 url: "your-url.php",
45171 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45172 callback: yourFunction,
45173 scope: yourObject, //(optional scope)
45176 text: "Loading...",
45182 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45183 * 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.
45184 * @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}
45185 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45186 * @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.
45187 * @return {Roo.ContentPanel} this
45195 var um = this.el.getUpdateManager();
45196 um.update.apply(um, arguments);
45202 * 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.
45203 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45204 * @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)
45205 * @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)
45206 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45208 setUrl : function(url, params, loadOnce){
45210 this.iframeEl.dom.src = url;
45214 if(this.refreshDelegate){
45215 this.removeListener("activate", this.refreshDelegate);
45217 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45218 this.on("activate", this.refreshDelegate);
45219 return this.el.getUpdateManager();
45222 _handleRefresh : function(url, params, loadOnce){
45223 if(!loadOnce || !this.loaded){
45224 var updater = this.el.getUpdateManager();
45225 updater.update(url, params, this._setLoaded.createDelegate(this));
45229 _setLoaded : function(){
45230 this.loaded = true;
45234 * Returns this panel's id
45237 getId : function(){
45242 * Returns this panel's element - used by regiosn to add.
45243 * @return {Roo.Element}
45245 getEl : function(){
45246 return this.wrapEl || this.el;
45251 adjustForComponents : function(width, height)
45253 //Roo.log('adjustForComponents ');
45254 if(this.resizeEl != this.el){
45255 width -= this.el.getFrameWidth('lr');
45256 height -= this.el.getFrameWidth('tb');
45259 var te = this.toolbar.getEl();
45260 te.setWidth(width);
45261 height -= te.getHeight();
45264 var te = this.footer.getEl();
45265 te.setWidth(width);
45266 height -= te.getHeight();
45270 if(this.adjustments){
45271 width += this.adjustments[0];
45272 height += this.adjustments[1];
45274 return {"width": width, "height": height};
45277 setSize : function(width, height){
45278 if(this.fitToFrame && !this.ignoreResize(width, height)){
45279 if(this.fitContainer && this.resizeEl != this.el){
45280 this.el.setSize(width, height);
45282 var size = this.adjustForComponents(width, height);
45284 this.iframeEl.setSize(width,height);
45287 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45288 this.fireEvent('resize', this, size.width, size.height);
45295 * Returns this panel's title
45298 getTitle : function(){
45300 if (typeof(this.title) != 'object') {
45305 for (var k in this.title) {
45306 if (!this.title.hasOwnProperty(k)) {
45310 if (k.indexOf('-') >= 0) {
45311 var s = k.split('-');
45312 for (var i = 0; i<s.length; i++) {
45313 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45316 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45323 * Set this panel's title
45324 * @param {String} title
45326 setTitle : function(title){
45327 this.title = title;
45329 this.region.updatePanelTitle(this, title);
45334 * Returns true is this panel was configured to be closable
45335 * @return {Boolean}
45337 isClosable : function(){
45338 return this.closable;
45341 beforeSlide : function(){
45343 this.resizeEl.clip();
45346 afterSlide : function(){
45348 this.resizeEl.unclip();
45352 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45353 * Will fail silently if the {@link #setUrl} method has not been called.
45354 * This does not activate the panel, just updates its content.
45356 refresh : function(){
45357 if(this.refreshDelegate){
45358 this.loaded = false;
45359 this.refreshDelegate();
45364 * Destroys this panel
45366 destroy : function(){
45367 this.el.removeAllListeners();
45368 var tempEl = document.createElement("span");
45369 tempEl.appendChild(this.el.dom);
45370 tempEl.innerHTML = "";
45376 * form - if the content panel contains a form - this is a reference to it.
45377 * @type {Roo.form.Form}
45381 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45382 * This contains a reference to it.
45388 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45398 * @param {Object} cfg Xtype definition of item to add.
45402 getChildContainer: function () {
45403 return this.getEl();
45407 onScroll : function(e)
45409 this.fireEvent('scroll', this, e);
45414 var ret = new Roo.factory(cfg);
45419 if (cfg.xtype.match(/^Form$/)) {
45422 //if (this.footer) {
45423 // el = this.footer.container.insertSibling(false, 'before');
45425 el = this.el.createChild();
45428 this.form = new Roo.form.Form(cfg);
45431 if ( this.form.allItems.length) {
45432 this.form.render(el.dom);
45436 // should only have one of theses..
45437 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45438 // views.. should not be just added - used named prop 'view''
45440 cfg.el = this.el.appendChild(document.createElement("div"));
45443 var ret = new Roo.factory(cfg);
45445 ret.render && ret.render(false, ''); // render blank..
45455 * @class Roo.bootstrap.panel.Grid
45456 * @extends Roo.bootstrap.panel.Content
45458 * Create a new GridPanel.
45459 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45460 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45461 * @param {Object} config A the config object
45467 Roo.bootstrap.panel.Grid = function(config)
45471 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45472 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45474 config.el = this.wrapper;
45475 //this.el = this.wrapper;
45477 if (config.container) {
45478 // ctor'ed from a Border/panel.grid
45481 this.wrapper.setStyle("overflow", "hidden");
45482 this.wrapper.addClass('roo-grid-container');
45487 if(config.toolbar){
45488 var tool_el = this.wrapper.createChild();
45489 this.toolbar = Roo.factory(config.toolbar);
45491 if (config.toolbar.items) {
45492 ti = config.toolbar.items ;
45493 delete config.toolbar.items ;
45497 this.toolbar.render(tool_el);
45498 for(var i =0;i < ti.length;i++) {
45499 // Roo.log(['add child', items[i]]);
45500 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45502 this.toolbar.items = nitems;
45504 delete config.toolbar;
45507 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45508 config.grid.scrollBody = true;;
45509 config.grid.monitorWindowResize = false; // turn off autosizing
45510 config.grid.autoHeight = false;
45511 config.grid.autoWidth = false;
45513 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45515 if (config.background) {
45516 // render grid on panel activation (if panel background)
45517 this.on('activate', function(gp) {
45518 if (!gp.grid.rendered) {
45519 gp.grid.render(this.wrapper);
45520 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45525 this.grid.render(this.wrapper);
45526 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45529 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45530 // ??? needed ??? config.el = this.wrapper;
45535 // xtype created footer. - not sure if will work as we normally have to render first..
45536 if (this.footer && !this.footer.el && this.footer.xtype) {
45538 var ctr = this.grid.getView().getFooterPanel(true);
45539 this.footer.dataSource = this.grid.dataSource;
45540 this.footer = Roo.factory(this.footer, Roo);
45541 this.footer.render(ctr);
45551 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45554 getId : function(){
45555 return this.grid.id;
45559 * Returns the grid for this panel
45560 * @return {Roo.bootstrap.Table}
45562 getGrid : function(){
45566 setSize : function(width, height)
45569 //if(!this.ignoreResize(width, height)){
45570 var grid = this.grid;
45571 var size = this.adjustForComponents(width, height);
45572 // tfoot is not a footer?
45575 var gridel = grid.getGridEl();
45576 gridel.setSize(size.width, size.height);
45578 var tbd = grid.getGridEl().select('tbody', true).first();
45579 var thd = grid.getGridEl().select('thead',true).first();
45580 var tbf= grid.getGridEl().select('tfoot', true).first();
45583 size.height -= tbf.getHeight();
45586 size.height -= thd.getHeight();
45589 tbd.setSize(size.width, size.height );
45590 // this is for the account management tab -seems to work there.
45591 var thd = grid.getGridEl().select('thead',true).first();
45593 // tbd.setSize(size.width, size.height - thd.getHeight());
45603 beforeSlide : function(){
45604 this.grid.getView().scroller.clip();
45607 afterSlide : function(){
45608 this.grid.getView().scroller.unclip();
45611 destroy : function(){
45612 this.grid.destroy();
45614 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45619 * @class Roo.bootstrap.panel.Nest
45620 * @extends Roo.bootstrap.panel.Content
45622 * Create a new Panel, that can contain a layout.Border.
45625 * @param {String/Object} config A string to set only the title or a config object
45627 Roo.bootstrap.panel.Nest = function(config)
45629 // construct with only one argument..
45630 /* FIXME - implement nicer consturctors
45631 if (layout.layout) {
45633 layout = config.layout;
45634 delete config.layout;
45636 if (layout.xtype && !layout.getEl) {
45637 // then layout needs constructing..
45638 layout = Roo.factory(layout, Roo);
45642 config.el = config.layout.getEl();
45644 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45646 config.layout.monitorWindowResize = false; // turn off autosizing
45647 this.layout = config.layout;
45648 this.layout.getEl().addClass("roo-layout-nested-layout");
45649 this.layout.parent = this;
45656 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45658 * @cfg {Roo.BorderLayout} layout The layout for this panel
45662 setSize : function(width, height){
45663 if(!this.ignoreResize(width, height)){
45664 var size = this.adjustForComponents(width, height);
45665 var el = this.layout.getEl();
45666 if (size.height < 1) {
45667 el.setWidth(size.width);
45669 el.setSize(size.width, size.height);
45671 var touch = el.dom.offsetWidth;
45672 this.layout.layout();
45673 // ie requires a double layout on the first pass
45674 if(Roo.isIE && !this.initialized){
45675 this.initialized = true;
45676 this.layout.layout();
45681 // activate all subpanels if not currently active..
45683 setActiveState : function(active){
45684 this.active = active;
45685 this.setActiveClass(active);
45688 this.fireEvent("deactivate", this);
45692 this.fireEvent("activate", this);
45693 // not sure if this should happen before or after..
45694 if (!this.layout) {
45695 return; // should not happen..
45698 for (var r in this.layout.regions) {
45699 reg = this.layout.getRegion(r);
45700 if (reg.getActivePanel()) {
45701 //reg.showPanel(reg.getActivePanel()); // force it to activate..
45702 reg.setActivePanel(reg.getActivePanel());
45705 if (!reg.panels.length) {
45708 reg.showPanel(reg.getPanel(0));
45717 * Returns the nested BorderLayout for this panel
45718 * @return {Roo.BorderLayout}
45720 getLayout : function(){
45721 return this.layout;
45725 * Adds a xtype elements to the layout of the nested panel
45729 xtype : 'ContentPanel',
45736 xtype : 'NestedLayoutPanel',
45742 items : [ ... list of content panels or nested layout panels.. ]
45746 * @param {Object} cfg Xtype definition of item to add.
45748 addxtype : function(cfg) {
45749 return this.layout.addxtype(cfg);
45754 * Ext JS Library 1.1.1
45755 * Copyright(c) 2006-2007, Ext JS, LLC.
45757 * Originally Released Under LGPL - original licence link has changed is not relivant.
45760 * <script type="text/javascript">
45763 * @class Roo.TabPanel
45764 * @extends Roo.util.Observable
45765 * A lightweight tab container.
45769 // basic tabs 1, built from existing content
45770 var tabs = new Roo.TabPanel("tabs1");
45771 tabs.addTab("script", "View Script");
45772 tabs.addTab("markup", "View Markup");
45773 tabs.activate("script");
45775 // more advanced tabs, built from javascript
45776 var jtabs = new Roo.TabPanel("jtabs");
45777 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45779 // set up the UpdateManager
45780 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45781 var updater = tab2.getUpdateManager();
45782 updater.setDefaultUrl("ajax1.htm");
45783 tab2.on('activate', updater.refresh, updater, true);
45785 // Use setUrl for Ajax loading
45786 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45787 tab3.setUrl("ajax2.htm", null, true);
45790 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45793 jtabs.activate("jtabs-1");
45796 * Create a new TabPanel.
45797 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45798 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45800 Roo.bootstrap.panel.Tabs = function(config){
45802 * The container element for this TabPanel.
45803 * @type Roo.Element
45805 this.el = Roo.get(config.el);
45808 if(typeof config == "boolean"){
45809 this.tabPosition = config ? "bottom" : "top";
45811 Roo.apply(this, config);
45815 if(this.tabPosition == "bottom"){
45816 // if tabs are at the bottom = create the body first.
45817 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45818 this.el.addClass("roo-tabs-bottom");
45820 // next create the tabs holders
45822 if (this.tabPosition == "west"){
45824 var reg = this.region; // fake it..
45826 if (!reg.mgr.parent) {
45829 reg = reg.mgr.parent.region;
45831 Roo.log("got nest?");
45833 if (reg.mgr.getRegion('west')) {
45834 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45835 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45836 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45837 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45838 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45846 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45847 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45848 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45849 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45854 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45857 // finally - if tabs are at the top, then create the body last..
45858 if(this.tabPosition != "bottom"){
45859 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45860 * @type Roo.Element
45862 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45863 this.el.addClass("roo-tabs-top");
45867 this.bodyEl.setStyle("position", "relative");
45869 this.active = null;
45870 this.activateDelegate = this.activate.createDelegate(this);
45875 * Fires when the active tab changes
45876 * @param {Roo.TabPanel} this
45877 * @param {Roo.TabPanelItem} activePanel The new active tab
45881 * @event beforetabchange
45882 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45883 * @param {Roo.TabPanel} this
45884 * @param {Object} e Set cancel to true on this object to cancel the tab change
45885 * @param {Roo.TabPanelItem} tab The tab being changed to
45887 "beforetabchange" : true
45890 Roo.EventManager.onWindowResize(this.onResize, this);
45891 this.cpad = this.el.getPadding("lr");
45892 this.hiddenCount = 0;
45895 // toolbar on the tabbar support...
45896 if (this.toolbar) {
45897 alert("no toolbar support yet");
45898 this.toolbar = false;
45900 var tcfg = this.toolbar;
45901 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
45902 this.toolbar = new Roo.Toolbar(tcfg);
45903 if (Roo.isSafari) {
45904 var tbl = tcfg.container.child('table', true);
45905 tbl.setAttribute('width', '100%');
45913 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45916 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45918 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45920 tabPosition : "top",
45922 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45924 currentTabWidth : 0,
45926 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45930 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45934 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45936 preferredTabWidth : 175,
45938 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45940 resizeTabs : false,
45942 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45944 monitorResize : true,
45946 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
45948 toolbar : false, // set by caller..
45950 region : false, /// set by caller
45952 disableTooltips : true, // not used yet...
45955 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45956 * @param {String} id The id of the div to use <b>or create</b>
45957 * @param {String} text The text for the tab
45958 * @param {String} content (optional) Content to put in the TabPanelItem body
45959 * @param {Boolean} closable (optional) True to create a close icon on the tab
45960 * @return {Roo.TabPanelItem} The created TabPanelItem
45962 addTab : function(id, text, content, closable, tpl)
45964 var item = new Roo.bootstrap.panel.TabItem({
45968 closable : closable,
45971 this.addTabItem(item);
45973 item.setContent(content);
45979 * Returns the {@link Roo.TabPanelItem} with the specified id/index
45980 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45981 * @return {Roo.TabPanelItem}
45983 getTab : function(id){
45984 return this.items[id];
45988 * Hides the {@link Roo.TabPanelItem} with the specified id/index
45989 * @param {String/Number} id The id or index of the TabPanelItem to hide.
45991 hideTab : function(id){
45992 var t = this.items[id];
45995 this.hiddenCount++;
45996 this.autoSizeTabs();
46001 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46002 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46004 unhideTab : function(id){
46005 var t = this.items[id];
46007 t.setHidden(false);
46008 this.hiddenCount--;
46009 this.autoSizeTabs();
46014 * Adds an existing {@link Roo.TabPanelItem}.
46015 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46017 addTabItem : function(item)
46019 this.items[item.id] = item;
46020 this.items.push(item);
46021 this.autoSizeTabs();
46022 // if(this.resizeTabs){
46023 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46024 // this.autoSizeTabs();
46026 // item.autoSize();
46031 * Removes a {@link Roo.TabPanelItem}.
46032 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46034 removeTab : function(id){
46035 var items = this.items;
46036 var tab = items[id];
46037 if(!tab) { return; }
46038 var index = items.indexOf(tab);
46039 if(this.active == tab && items.length > 1){
46040 var newTab = this.getNextAvailable(index);
46045 this.stripEl.dom.removeChild(tab.pnode.dom);
46046 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46047 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46049 items.splice(index, 1);
46050 delete this.items[tab.id];
46051 tab.fireEvent("close", tab);
46052 tab.purgeListeners();
46053 this.autoSizeTabs();
46056 getNextAvailable : function(start){
46057 var items = this.items;
46059 // look for a next tab that will slide over to
46060 // replace the one being removed
46061 while(index < items.length){
46062 var item = items[++index];
46063 if(item && !item.isHidden()){
46067 // if one isn't found select the previous tab (on the left)
46070 var item = items[--index];
46071 if(item && !item.isHidden()){
46079 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46080 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46082 disableTab : function(id){
46083 var tab = this.items[id];
46084 if(tab && this.active != tab){
46090 * Enables a {@link Roo.TabPanelItem} that is disabled.
46091 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46093 enableTab : function(id){
46094 var tab = this.items[id];
46099 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46100 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46101 * @return {Roo.TabPanelItem} The TabPanelItem.
46103 activate : function(id)
46105 //Roo.log('activite:' + id);
46107 var tab = this.items[id];
46111 if(tab == this.active || tab.disabled){
46115 this.fireEvent("beforetabchange", this, e, tab);
46116 if(e.cancel !== true && !tab.disabled){
46118 this.active.hide();
46120 this.active = this.items[id];
46121 this.active.show();
46122 this.fireEvent("tabchange", this, this.active);
46128 * Gets the active {@link Roo.TabPanelItem}.
46129 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46131 getActiveTab : function(){
46132 return this.active;
46136 * Updates the tab body element to fit the height of the container element
46137 * for overflow scrolling
46138 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46140 syncHeight : function(targetHeight){
46141 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46142 var bm = this.bodyEl.getMargins();
46143 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46144 this.bodyEl.setHeight(newHeight);
46148 onResize : function(){
46149 if(this.monitorResize){
46150 this.autoSizeTabs();
46155 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46157 beginUpdate : function(){
46158 this.updating = true;
46162 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46164 endUpdate : function(){
46165 this.updating = false;
46166 this.autoSizeTabs();
46170 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46172 autoSizeTabs : function()
46174 var count = this.items.length;
46175 var vcount = count - this.hiddenCount;
46178 this.stripEl.hide();
46180 this.stripEl.show();
46183 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46188 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46189 var availWidth = Math.floor(w / vcount);
46190 var b = this.stripBody;
46191 if(b.getWidth() > w){
46192 var tabs = this.items;
46193 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46194 if(availWidth < this.minTabWidth){
46195 /*if(!this.sleft){ // incomplete scrolling code
46196 this.createScrollButtons();
46199 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46202 if(this.currentTabWidth < this.preferredTabWidth){
46203 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46209 * Returns the number of tabs in this TabPanel.
46212 getCount : function(){
46213 return this.items.length;
46217 * Resizes all the tabs to the passed width
46218 * @param {Number} The new width
46220 setTabWidth : function(width){
46221 this.currentTabWidth = width;
46222 for(var i = 0, len = this.items.length; i < len; i++) {
46223 if(!this.items[i].isHidden()) {
46224 this.items[i].setWidth(width);
46230 * Destroys this TabPanel
46231 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46233 destroy : function(removeEl){
46234 Roo.EventManager.removeResizeListener(this.onResize, this);
46235 for(var i = 0, len = this.items.length; i < len; i++){
46236 this.items[i].purgeListeners();
46238 if(removeEl === true){
46239 this.el.update("");
46244 createStrip : function(container)
46246 var strip = document.createElement("nav");
46247 strip.className = Roo.bootstrap.version == 4 ?
46248 "navbar-light bg-light" :
46249 "navbar navbar-default"; //"x-tabs-wrap";
46250 container.appendChild(strip);
46254 createStripList : function(strip)
46256 // div wrapper for retard IE
46257 // returns the "tr" element.
46258 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46259 //'<div class="x-tabs-strip-wrap">'+
46260 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46261 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46262 return strip.firstChild; //.firstChild.firstChild.firstChild;
46264 createBody : function(container)
46266 var body = document.createElement("div");
46267 Roo.id(body, "tab-body");
46268 //Roo.fly(body).addClass("x-tabs-body");
46269 Roo.fly(body).addClass("tab-content");
46270 container.appendChild(body);
46273 createItemBody :function(bodyEl, id){
46274 var body = Roo.getDom(id);
46276 body = document.createElement("div");
46279 //Roo.fly(body).addClass("x-tabs-item-body");
46280 Roo.fly(body).addClass("tab-pane");
46281 bodyEl.insertBefore(body, bodyEl.firstChild);
46285 createStripElements : function(stripEl, text, closable, tpl)
46287 var td = document.createElement("li"); // was td..
46288 td.className = 'nav-item';
46290 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46293 stripEl.appendChild(td);
46295 td.className = "x-tabs-closable";
46296 if(!this.closeTpl){
46297 this.closeTpl = new Roo.Template(
46298 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46299 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46300 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46303 var el = this.closeTpl.overwrite(td, {"text": text});
46304 var close = el.getElementsByTagName("div")[0];
46305 var inner = el.getElementsByTagName("em")[0];
46306 return {"el": el, "close": close, "inner": inner};
46309 // not sure what this is..
46310 // if(!this.tabTpl){
46311 //this.tabTpl = new Roo.Template(
46312 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46313 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46315 // this.tabTpl = new Roo.Template(
46316 // '<a href="#">' +
46317 // '<span unselectable="on"' +
46318 // (this.disableTooltips ? '' : ' title="{text}"') +
46319 // ' >{text}</span></a>'
46325 var template = tpl || this.tabTpl || false;
46328 template = new Roo.Template(
46329 Roo.bootstrap.version == 4 ?
46331 '<a class="nav-link" href="#" unselectable="on"' +
46332 (this.disableTooltips ? '' : ' title="{text}"') +
46335 '<a class="nav-link" href="#">' +
46336 '<span unselectable="on"' +
46337 (this.disableTooltips ? '' : ' title="{text}"') +
46338 ' >{text}</span></a>'
46343 switch (typeof(template)) {
46347 template = new Roo.Template(template);
46353 var el = template.overwrite(td, {"text": text});
46355 var inner = el.getElementsByTagName("span")[0];
46357 return {"el": el, "inner": inner};
46365 * @class Roo.TabPanelItem
46366 * @extends Roo.util.Observable
46367 * Represents an individual item (tab plus body) in a TabPanel.
46368 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46369 * @param {String} id The id of this TabPanelItem
46370 * @param {String} text The text for the tab of this TabPanelItem
46371 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46373 Roo.bootstrap.panel.TabItem = function(config){
46375 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46376 * @type Roo.TabPanel
46378 this.tabPanel = config.panel;
46380 * The id for this TabPanelItem
46383 this.id = config.id;
46385 this.disabled = false;
46387 this.text = config.text;
46389 this.loaded = false;
46390 this.closable = config.closable;
46393 * The body element for this TabPanelItem.
46394 * @type Roo.Element
46396 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46397 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46398 this.bodyEl.setStyle("display", "block");
46399 this.bodyEl.setStyle("zoom", "1");
46400 //this.hideAction();
46402 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46404 this.el = Roo.get(els.el);
46405 this.inner = Roo.get(els.inner, true);
46406 this.textEl = Roo.bootstrap.version == 4 ?
46407 this.el : Roo.get(this.el.dom.firstChild, true);
46409 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46410 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46413 // this.el.on("mousedown", this.onTabMouseDown, this);
46414 this.el.on("click", this.onTabClick, this);
46416 if(config.closable){
46417 var c = Roo.get(els.close, true);
46418 c.dom.title = this.closeText;
46419 c.addClassOnOver("close-over");
46420 c.on("click", this.closeClick, this);
46426 * Fires when this tab becomes the active tab.
46427 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46428 * @param {Roo.TabPanelItem} this
46432 * @event beforeclose
46433 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46434 * @param {Roo.TabPanelItem} this
46435 * @param {Object} e Set cancel to true on this object to cancel the close.
46437 "beforeclose": true,
46440 * Fires when this tab is closed.
46441 * @param {Roo.TabPanelItem} this
46445 * @event deactivate
46446 * Fires when this tab is no longer the active tab.
46447 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46448 * @param {Roo.TabPanelItem} this
46450 "deactivate" : true
46452 this.hidden = false;
46454 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46457 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46459 purgeListeners : function(){
46460 Roo.util.Observable.prototype.purgeListeners.call(this);
46461 this.el.removeAllListeners();
46464 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46467 this.status_node.addClass("active");
46470 this.tabPanel.stripWrap.repaint();
46472 this.fireEvent("activate", this.tabPanel, this);
46476 * Returns true if this tab is the active tab.
46477 * @return {Boolean}
46479 isActive : function(){
46480 return this.tabPanel.getActiveTab() == this;
46484 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46487 this.status_node.removeClass("active");
46489 this.fireEvent("deactivate", this.tabPanel, this);
46492 hideAction : function(){
46493 this.bodyEl.hide();
46494 this.bodyEl.setStyle("position", "absolute");
46495 this.bodyEl.setLeft("-20000px");
46496 this.bodyEl.setTop("-20000px");
46499 showAction : function(){
46500 this.bodyEl.setStyle("position", "relative");
46501 this.bodyEl.setTop("");
46502 this.bodyEl.setLeft("");
46503 this.bodyEl.show();
46507 * Set the tooltip for the tab.
46508 * @param {String} tooltip The tab's tooltip
46510 setTooltip : function(text){
46511 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46512 this.textEl.dom.qtip = text;
46513 this.textEl.dom.removeAttribute('title');
46515 this.textEl.dom.title = text;
46519 onTabClick : function(e){
46520 e.preventDefault();
46521 this.tabPanel.activate(this.id);
46524 onTabMouseDown : function(e){
46525 e.preventDefault();
46526 this.tabPanel.activate(this.id);
46529 getWidth : function(){
46530 return this.inner.getWidth();
46533 setWidth : function(width){
46534 var iwidth = width - this.linode.getPadding("lr");
46535 this.inner.setWidth(iwidth);
46536 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46537 this.linode.setWidth(width);
46541 * Show or hide the tab
46542 * @param {Boolean} hidden True to hide or false to show.
46544 setHidden : function(hidden){
46545 this.hidden = hidden;
46546 this.linode.setStyle("display", hidden ? "none" : "");
46550 * Returns true if this tab is "hidden"
46551 * @return {Boolean}
46553 isHidden : function(){
46554 return this.hidden;
46558 * Returns the text for this tab
46561 getText : function(){
46565 autoSize : function(){
46566 //this.el.beginMeasure();
46567 this.textEl.setWidth(1);
46569 * #2804 [new] Tabs in Roojs
46570 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46572 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46573 //this.el.endMeasure();
46577 * Sets the text for the tab (Note: this also sets the tooltip text)
46578 * @param {String} text The tab's text and tooltip
46580 setText : function(text){
46582 this.textEl.update(text);
46583 this.setTooltip(text);
46584 //if(!this.tabPanel.resizeTabs){
46585 // this.autoSize();
46589 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46591 activate : function(){
46592 this.tabPanel.activate(this.id);
46596 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46598 disable : function(){
46599 if(this.tabPanel.active != this){
46600 this.disabled = true;
46601 this.status_node.addClass("disabled");
46606 * Enables this TabPanelItem if it was previously disabled.
46608 enable : function(){
46609 this.disabled = false;
46610 this.status_node.removeClass("disabled");
46614 * Sets the content for this TabPanelItem.
46615 * @param {String} content The content
46616 * @param {Boolean} loadScripts true to look for and load scripts
46618 setContent : function(content, loadScripts){
46619 this.bodyEl.update(content, loadScripts);
46623 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46624 * @return {Roo.UpdateManager} The UpdateManager
46626 getUpdateManager : function(){
46627 return this.bodyEl.getUpdateManager();
46631 * Set a URL to be used to load the content for this TabPanelItem.
46632 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46633 * @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)
46634 * @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)
46635 * @return {Roo.UpdateManager} The UpdateManager
46637 setUrl : function(url, params, loadOnce){
46638 if(this.refreshDelegate){
46639 this.un('activate', this.refreshDelegate);
46641 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46642 this.on("activate", this.refreshDelegate);
46643 return this.bodyEl.getUpdateManager();
46647 _handleRefresh : function(url, params, loadOnce){
46648 if(!loadOnce || !this.loaded){
46649 var updater = this.bodyEl.getUpdateManager();
46650 updater.update(url, params, this._setLoaded.createDelegate(this));
46655 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
46656 * Will fail silently if the setUrl method has not been called.
46657 * This does not activate the panel, just updates its content.
46659 refresh : function(){
46660 if(this.refreshDelegate){
46661 this.loaded = false;
46662 this.refreshDelegate();
46667 _setLoaded : function(){
46668 this.loaded = true;
46672 closeClick : function(e){
46675 this.fireEvent("beforeclose", this, o);
46676 if(o.cancel !== true){
46677 this.tabPanel.removeTab(this.id);
46681 * The text displayed in the tooltip for the close icon.
46684 closeText : "Close this tab"
46687 * This script refer to:
46688 * Title: International Telephone Input
46689 * Author: Jack O'Connor
46690 * Code version: v12.1.12
46691 * Availability: https://github.com/jackocnr/intl-tel-input.git
46694 Roo.bootstrap.form.PhoneInputData = function() {
46697 "Afghanistan (افغانستان)",
46702 "Albania (Shqipëri)",
46707 "Algeria (الجزائر)",
46732 "Antigua and Barbuda",
46742 "Armenia (Հայաստան)",
46758 "Austria (Österreich)",
46763 "Azerbaijan (Azərbaycan)",
46773 "Bahrain (البحرين)",
46778 "Bangladesh (বাংলাদেশ)",
46788 "Belarus (Беларусь)",
46793 "Belgium (België)",
46823 "Bosnia and Herzegovina (Босна и Херцеговина)",
46838 "British Indian Ocean Territory",
46843 "British Virgin Islands",
46853 "Bulgaria (България)",
46863 "Burundi (Uburundi)",
46868 "Cambodia (កម្ពុជា)",
46873 "Cameroon (Cameroun)",
46882 ["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"]
46885 "Cape Verde (Kabu Verdi)",
46890 "Caribbean Netherlands",
46901 "Central African Republic (République centrafricaine)",
46921 "Christmas Island",
46927 "Cocos (Keeling) Islands",
46938 "Comoros (جزر القمر)",
46943 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46948 "Congo (Republic) (Congo-Brazzaville)",
46968 "Croatia (Hrvatska)",
46989 "Czech Republic (Česká republika)",
46994 "Denmark (Danmark)",
47009 "Dominican Republic (República Dominicana)",
47013 ["809", "829", "849"]
47031 "Equatorial Guinea (Guinea Ecuatorial)",
47051 "Falkland Islands (Islas Malvinas)",
47056 "Faroe Islands (Føroyar)",
47077 "French Guiana (Guyane française)",
47082 "French Polynesia (Polynésie française)",
47097 "Georgia (საქართველო)",
47102 "Germany (Deutschland)",
47122 "Greenland (Kalaallit Nunaat)",
47159 "Guinea-Bissau (Guiné Bissau)",
47184 "Hungary (Magyarország)",
47189 "Iceland (Ísland)",
47209 "Iraq (العراق)",
47225 "Israel (ישראל)",
47252 "Jordan (الأردن)",
47257 "Kazakhstan (Казахстан)",
47278 "Kuwait (الكويت)",
47283 "Kyrgyzstan (Кыргызстан)",
47293 "Latvia (Latvija)",
47298 "Lebanon (لبنان)",
47313 "Libya (ليبيا)",
47323 "Lithuania (Lietuva)",
47338 "Macedonia (FYROM) (Македонија)",
47343 "Madagascar (Madagasikara)",
47373 "Marshall Islands",
47383 "Mauritania (موريتانيا)",
47388 "Mauritius (Moris)",
47409 "Moldova (Republica Moldova)",
47419 "Mongolia (Монгол)",
47424 "Montenegro (Crna Gora)",
47434 "Morocco (المغرب)",
47440 "Mozambique (Moçambique)",
47445 "Myanmar (Burma) (မြန်မာ)",
47450 "Namibia (Namibië)",
47465 "Netherlands (Nederland)",
47470 "New Caledonia (Nouvelle-Calédonie)",
47505 "North Korea (조선 민주주의 인민 공화국)",
47510 "Northern Mariana Islands",
47526 "Pakistan (پاکستان)",
47536 "Palestine (فلسطين)",
47546 "Papua New Guinea",
47588 "Réunion (La Réunion)",
47594 "Romania (România)",
47610 "Saint Barthélemy",
47621 "Saint Kitts and Nevis",
47631 "Saint Martin (Saint-Martin (partie française))",
47637 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47642 "Saint Vincent and the Grenadines",
47657 "São Tomé and Príncipe (São Tomé e Príncipe)",
47662 "Saudi Arabia (المملكة العربية السعودية)",
47667 "Senegal (Sénégal)",
47697 "Slovakia (Slovensko)",
47702 "Slovenia (Slovenija)",
47712 "Somalia (Soomaaliya)",
47722 "South Korea (대한민국)",
47727 "South Sudan (جنوب السودان)",
47737 "Sri Lanka (ශ්රී ලංකාව)",
47742 "Sudan (السودان)",
47752 "Svalbard and Jan Mayen",
47763 "Sweden (Sverige)",
47768 "Switzerland (Schweiz)",
47773 "Syria (سوريا)",
47818 "Trinidad and Tobago",
47823 "Tunisia (تونس)",
47828 "Turkey (Türkiye)",
47838 "Turks and Caicos Islands",
47848 "U.S. Virgin Islands",
47858 "Ukraine (Україна)",
47863 "United Arab Emirates (الإمارات العربية المتحدة)",
47885 "Uzbekistan (Oʻzbekiston)",
47895 "Vatican City (Città del Vaticano)",
47906 "Vietnam (Việt Nam)",
47911 "Wallis and Futuna (Wallis-et-Futuna)",
47916 "Western Sahara (الصحراء الغربية)",
47922 "Yemen (اليمن)",
47946 * This script refer to:
47947 * Title: International Telephone Input
47948 * Author: Jack O'Connor
47949 * Code version: v12.1.12
47950 * Availability: https://github.com/jackocnr/intl-tel-input.git
47954 * @class Roo.bootstrap.form.PhoneInput
47955 * @extends Roo.bootstrap.form.TriggerField
47956 * An input with International dial-code selection
47958 * @cfg {String} defaultDialCode default '+852'
47959 * @cfg {Array} preferedCountries default []
47962 * Create a new PhoneInput.
47963 * @param {Object} config Configuration options
47966 Roo.bootstrap.form.PhoneInput = function(config) {
47967 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47970 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47972 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47974 listWidth: undefined,
47976 selectedClass: 'active',
47978 invalidClass : "has-warning",
47980 validClass: 'has-success',
47982 allowed: '0123456789',
47987 * @cfg {String} defaultDialCode The default dial code when initializing the input
47989 defaultDialCode: '+852',
47992 * @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
47994 preferedCountries: false,
47996 getAutoCreate : function()
47998 var data = Roo.bootstrap.form.PhoneInputData();
47999 var align = this.labelAlign || this.parentLabelAlign();
48002 this.allCountries = [];
48003 this.dialCodeMapping = [];
48005 for (var i = 0; i < data.length; i++) {
48007 this.allCountries[i] = {
48011 priority: c[3] || 0,
48012 areaCodes: c[4] || null
48014 this.dialCodeMapping[c[2]] = {
48017 priority: c[3] || 0,
48018 areaCodes: c[4] || null
48030 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48031 maxlength: this.max_length,
48032 cls : 'form-control tel-input',
48033 autocomplete: 'new-password'
48036 var hiddenInput = {
48039 cls: 'hidden-tel-input'
48043 hiddenInput.name = this.name;
48046 if (this.disabled) {
48047 input.disabled = true;
48050 var flag_container = {
48067 cls: this.hasFeedback ? 'has-feedback' : '',
48073 cls: 'dial-code-holder',
48080 cls: 'roo-select2-container input-group',
48087 if (this.fieldLabel.length) {
48090 tooltip: 'This field is required'
48096 cls: 'control-label',
48102 html: this.fieldLabel
48105 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48111 if(this.indicatorpos == 'right') {
48112 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48119 if(align == 'left') {
48127 if(this.labelWidth > 12){
48128 label.style = "width: " + this.labelWidth + 'px';
48130 if(this.labelWidth < 13 && this.labelmd == 0){
48131 this.labelmd = this.labelWidth;
48133 if(this.labellg > 0){
48134 label.cls += ' col-lg-' + this.labellg;
48135 input.cls += ' col-lg-' + (12 - this.labellg);
48137 if(this.labelmd > 0){
48138 label.cls += ' col-md-' + this.labelmd;
48139 container.cls += ' col-md-' + (12 - this.labelmd);
48141 if(this.labelsm > 0){
48142 label.cls += ' col-sm-' + this.labelsm;
48143 container.cls += ' col-sm-' + (12 - this.labelsm);
48145 if(this.labelxs > 0){
48146 label.cls += ' col-xs-' + this.labelxs;
48147 container.cls += ' col-xs-' + (12 - this.labelxs);
48157 var settings = this;
48159 ['xs','sm','md','lg'].map(function(size){
48160 if (settings[size]) {
48161 cfg.cls += ' col-' + size + '-' + settings[size];
48165 this.store = new Roo.data.Store({
48166 proxy : new Roo.data.MemoryProxy({}),
48167 reader : new Roo.data.JsonReader({
48178 'name' : 'dialCode',
48182 'name' : 'priority',
48186 'name' : 'areaCodes',
48193 if(!this.preferedCountries) {
48194 this.preferedCountries = [
48201 var p = this.preferedCountries.reverse();
48204 for (var i = 0; i < p.length; i++) {
48205 for (var j = 0; j < this.allCountries.length; j++) {
48206 if(this.allCountries[j].iso2 == p[i]) {
48207 var t = this.allCountries[j];
48208 this.allCountries.splice(j,1);
48209 this.allCountries.unshift(t);
48215 this.store.proxy.data = {
48217 data: this.allCountries
48223 initEvents : function()
48226 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48228 this.indicator = this.indicatorEl();
48229 this.flag = this.flagEl();
48230 this.dialCodeHolder = this.dialCodeHolderEl();
48232 this.trigger = this.el.select('div.flag-box',true).first();
48233 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48238 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48239 _this.list.setWidth(lw);
48242 this.list.on('mouseover', this.onViewOver, this);
48243 this.list.on('mousemove', this.onViewMove, this);
48244 this.inputEl().on("keyup", this.onKeyUp, this);
48245 this.inputEl().on("keypress", this.onKeyPress, this);
48247 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48249 this.view = new Roo.View(this.list, this.tpl, {
48250 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48253 this.view.on('click', this.onViewClick, this);
48254 this.setValue(this.defaultDialCode);
48257 onTriggerClick : function(e)
48259 Roo.log('trigger click');
48264 if(this.isExpanded()){
48266 this.hasFocus = false;
48268 this.store.load({});
48269 this.hasFocus = true;
48274 isExpanded : function()
48276 return this.list.isVisible();
48279 collapse : function()
48281 if(!this.isExpanded()){
48285 Roo.get(document).un('mousedown', this.collapseIf, this);
48286 Roo.get(document).un('mousewheel', this.collapseIf, this);
48287 this.fireEvent('collapse', this);
48291 expand : function()
48295 if(this.isExpanded() || !this.hasFocus){
48299 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48300 this.list.setWidth(lw);
48303 this.restrictHeight();
48305 Roo.get(document).on('mousedown', this.collapseIf, this);
48306 Roo.get(document).on('mousewheel', this.collapseIf, this);
48308 this.fireEvent('expand', this);
48311 restrictHeight : function()
48313 this.list.alignTo(this.inputEl(), this.listAlign);
48314 this.list.alignTo(this.inputEl(), this.listAlign);
48317 onViewOver : function(e, t)
48319 if(this.inKeyMode){
48322 var item = this.view.findItemFromChild(t);
48325 var index = this.view.indexOf(item);
48326 this.select(index, false);
48331 onViewClick : function(view, doFocus, el, e)
48333 var index = this.view.getSelectedIndexes()[0];
48335 var r = this.store.getAt(index);
48338 this.onSelect(r, index);
48340 if(doFocus !== false && !this.blockFocus){
48341 this.inputEl().focus();
48345 onViewMove : function(e, t)
48347 this.inKeyMode = false;
48350 select : function(index, scrollIntoView)
48352 this.selectedIndex = index;
48353 this.view.select(index);
48354 if(scrollIntoView !== false){
48355 var el = this.view.getNode(index);
48357 this.list.scrollChildIntoView(el, false);
48362 createList : function()
48364 this.list = Roo.get(document.body).createChild({
48366 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48367 style: 'display:none'
48370 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48373 collapseIf : function(e)
48375 var in_combo = e.within(this.el);
48376 var in_list = e.within(this.list);
48377 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48379 if (in_combo || in_list || is_list) {
48385 onSelect : function(record, index)
48387 if(this.fireEvent('beforeselect', this, record, index) !== false){
48389 this.setFlagClass(record.data.iso2);
48390 this.setDialCode(record.data.dialCode);
48391 this.hasFocus = false;
48393 this.fireEvent('select', this, record, index);
48397 flagEl : function()
48399 var flag = this.el.select('div.flag',true).first();
48406 dialCodeHolderEl : function()
48408 var d = this.el.select('input.dial-code-holder',true).first();
48415 setDialCode : function(v)
48417 this.dialCodeHolder.dom.value = '+'+v;
48420 setFlagClass : function(n)
48422 this.flag.dom.className = 'flag '+n;
48425 getValue : function()
48427 var v = this.inputEl().getValue();
48428 if(this.dialCodeHolder) {
48429 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48434 setValue : function(v)
48436 var d = this.getDialCode(v);
48438 //invalid dial code
48439 if(v.length == 0 || !d || d.length == 0) {
48441 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48442 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48448 this.setFlagClass(this.dialCodeMapping[d].iso2);
48449 this.setDialCode(d);
48450 this.inputEl().dom.value = v.replace('+'+d,'');
48451 this.hiddenEl().dom.value = this.getValue();
48456 getDialCode : function(v)
48460 if (v.length == 0) {
48461 return this.dialCodeHolder.dom.value;
48465 if (v.charAt(0) != "+") {
48468 var numericChars = "";
48469 for (var i = 1; i < v.length; i++) {
48470 var c = v.charAt(i);
48473 if (this.dialCodeMapping[numericChars]) {
48474 dialCode = v.substr(1, i);
48476 if (numericChars.length == 4) {
48486 this.setValue(this.defaultDialCode);
48490 hiddenEl : function()
48492 return this.el.select('input.hidden-tel-input',true).first();
48495 // after setting val
48496 onKeyUp : function(e){
48497 this.setValue(this.getValue());
48500 onKeyPress : function(e){
48501 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48508 * @class Roo.bootstrap.form.MoneyField
48509 * @extends Roo.bootstrap.form.ComboBox
48510 * Bootstrap MoneyField class
48513 * Create a new MoneyField.
48514 * @param {Object} config Configuration options
48517 Roo.bootstrap.form.MoneyField = function(config) {
48519 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48523 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48526 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48528 allowDecimals : true,
48530 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48532 decimalSeparator : ".",
48534 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48536 decimalPrecision : 0,
48538 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48540 allowNegative : true,
48542 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48546 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48548 minValue : Number.NEGATIVE_INFINITY,
48550 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48552 maxValue : Number.MAX_VALUE,
48554 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48556 minText : "The minimum value for this field is {0}",
48558 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48560 maxText : "The maximum value for this field is {0}",
48562 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48563 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48565 nanText : "{0} is not a valid number",
48567 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48571 * @cfg {String} defaults currency of the MoneyField
48572 * value should be in lkey
48574 defaultCurrency : false,
48576 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48578 thousandsDelimiter : false,
48580 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48589 * @cfg {Roo.data.Store} store Store to lookup currency??
48593 getAutoCreate : function()
48595 var align = this.labelAlign || this.parentLabelAlign();
48607 cls : 'form-control roo-money-amount-input',
48608 autocomplete: 'new-password'
48611 var hiddenInput = {
48615 cls: 'hidden-number-input'
48618 if(this.max_length) {
48619 input.maxlength = this.max_length;
48623 hiddenInput.name = this.name;
48626 if (this.disabled) {
48627 input.disabled = true;
48630 var clg = 12 - this.inputlg;
48631 var cmd = 12 - this.inputmd;
48632 var csm = 12 - this.inputsm;
48633 var cxs = 12 - this.inputxs;
48637 cls : 'row roo-money-field',
48641 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48645 cls: 'roo-select2-container input-group',
48649 cls : 'form-control roo-money-currency-input',
48650 autocomplete: 'new-password',
48652 name : this.currencyName
48656 cls : 'input-group-addon',
48670 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48674 cls: this.hasFeedback ? 'has-feedback' : '',
48685 if (this.fieldLabel.length) {
48688 tooltip: 'This field is required'
48694 cls: 'control-label',
48700 html: this.fieldLabel
48703 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48709 if(this.indicatorpos == 'right') {
48710 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48717 if(align == 'left') {
48725 if(this.labelWidth > 12){
48726 label.style = "width: " + this.labelWidth + 'px';
48728 if(this.labelWidth < 13 && this.labelmd == 0){
48729 this.labelmd = this.labelWidth;
48731 if(this.labellg > 0){
48732 label.cls += ' col-lg-' + this.labellg;
48733 input.cls += ' col-lg-' + (12 - this.labellg);
48735 if(this.labelmd > 0){
48736 label.cls += ' col-md-' + this.labelmd;
48737 container.cls += ' col-md-' + (12 - this.labelmd);
48739 if(this.labelsm > 0){
48740 label.cls += ' col-sm-' + this.labelsm;
48741 container.cls += ' col-sm-' + (12 - this.labelsm);
48743 if(this.labelxs > 0){
48744 label.cls += ' col-xs-' + this.labelxs;
48745 container.cls += ' col-xs-' + (12 - this.labelxs);
48756 var settings = this;
48758 ['xs','sm','md','lg'].map(function(size){
48759 if (settings[size]) {
48760 cfg.cls += ' col-' + size + '-' + settings[size];
48767 initEvents : function()
48769 this.indicator = this.indicatorEl();
48771 this.initCurrencyEvent();
48773 this.initNumberEvent();
48776 initCurrencyEvent : function()
48779 throw "can not find store for combo";
48782 this.store = Roo.factory(this.store, Roo.data);
48783 this.store.parent = this;
48787 this.triggerEl = this.el.select('.input-group-addon', true).first();
48789 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48794 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48795 _this.list.setWidth(lw);
48798 this.list.on('mouseover', this.onViewOver, this);
48799 this.list.on('mousemove', this.onViewMove, this);
48800 this.list.on('scroll', this.onViewScroll, this);
48803 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48806 this.view = new Roo.View(this.list, this.tpl, {
48807 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48810 this.view.on('click', this.onViewClick, this);
48812 this.store.on('beforeload', this.onBeforeLoad, this);
48813 this.store.on('load', this.onLoad, this);
48814 this.store.on('loadexception', this.onLoadException, this);
48816 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48817 "up" : function(e){
48818 this.inKeyMode = true;
48822 "down" : function(e){
48823 if(!this.isExpanded()){
48824 this.onTriggerClick();
48826 this.inKeyMode = true;
48831 "enter" : function(e){
48834 if(this.fireEvent("specialkey", this, e)){
48835 this.onViewClick(false);
48841 "esc" : function(e){
48845 "tab" : function(e){
48848 if(this.fireEvent("specialkey", this, e)){
48849 this.onViewClick(false);
48857 doRelay : function(foo, bar, hname){
48858 if(hname == 'down' || this.scope.isExpanded()){
48859 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48867 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48871 initNumberEvent : function(e)
48873 this.inputEl().on("keydown" , this.fireKey, this);
48874 this.inputEl().on("focus", this.onFocus, this);
48875 this.inputEl().on("blur", this.onBlur, this);
48877 this.inputEl().relayEvent('keyup', this);
48879 if(this.indicator){
48880 this.indicator.addClass('invisible');
48883 this.originalValue = this.getValue();
48885 if(this.validationEvent == 'keyup'){
48886 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48887 this.inputEl().on('keyup', this.filterValidation, this);
48889 else if(this.validationEvent !== false){
48890 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48893 if(this.selectOnFocus){
48894 this.on("focus", this.preFocus, this);
48897 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48898 this.inputEl().on("keypress", this.filterKeys, this);
48900 this.inputEl().relayEvent('keypress', this);
48903 var allowed = "0123456789";
48905 if(this.allowDecimals){
48906 allowed += this.decimalSeparator;
48909 if(this.allowNegative){
48913 if(this.thousandsDelimiter) {
48917 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48919 var keyPress = function(e){
48921 var k = e.getKey();
48923 var c = e.getCharCode();
48926 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48927 allowed.indexOf(String.fromCharCode(c)) === -1
48933 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48937 if(allowed.indexOf(String.fromCharCode(c)) === -1){
48942 this.inputEl().on("keypress", keyPress, this);
48946 onTriggerClick : function(e)
48953 this.loadNext = false;
48955 if(this.isExpanded()){
48960 this.hasFocus = true;
48962 if(this.triggerAction == 'all') {
48963 this.doQuery(this.allQuery, true);
48967 this.doQuery(this.getRawValue());
48970 getCurrency : function()
48972 var v = this.currencyEl().getValue();
48977 restrictHeight : function()
48979 this.list.alignTo(this.currencyEl(), this.listAlign);
48980 this.list.alignTo(this.currencyEl(), this.listAlign);
48983 onViewClick : function(view, doFocus, el, e)
48985 var index = this.view.getSelectedIndexes()[0];
48987 var r = this.store.getAt(index);
48990 this.onSelect(r, index);
48994 onSelect : function(record, index){
48996 if(this.fireEvent('beforeselect', this, record, index) !== false){
48998 this.setFromCurrencyData(index > -1 ? record.data : false);
49002 this.fireEvent('select', this, record, index);
49006 setFromCurrencyData : function(o)
49010 this.lastCurrency = o;
49012 if (this.currencyField) {
49013 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49015 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49018 this.lastSelectionText = currency;
49020 //setting default currency
49021 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49022 this.setCurrency(this.defaultCurrency);
49026 this.setCurrency(currency);
49029 setFromData : function(o)
49033 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49035 this.setFromCurrencyData(c);
49040 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49042 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49045 this.setValue(value);
49049 setCurrency : function(v)
49051 this.currencyValue = v;
49054 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49059 setValue : function(v)
49061 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49067 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49069 this.inputEl().dom.value = (v == '') ? '' :
49070 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49072 if(!this.allowZero && v === '0') {
49073 this.hiddenEl().dom.value = '';
49074 this.inputEl().dom.value = '';
49081 getRawValue : function()
49083 var v = this.inputEl().getValue();
49088 getValue : function()
49090 return this.fixPrecision(this.parseValue(this.getRawValue()));
49093 parseValue : function(value)
49095 if(this.thousandsDelimiter) {
49097 r = new RegExp(",", "g");
49098 value = value.replace(r, "");
49101 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49102 return isNaN(value) ? '' : value;
49106 fixPrecision : function(value)
49108 if(this.thousandsDelimiter) {
49110 r = new RegExp(",", "g");
49111 value = value.replace(r, "");
49114 var nan = isNaN(value);
49116 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49117 return nan ? '' : value;
49119 return parseFloat(value).toFixed(this.decimalPrecision);
49122 decimalPrecisionFcn : function(v)
49124 return Math.floor(v);
49127 validateValue : function(value)
49129 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49133 var num = this.parseValue(value);
49136 this.markInvalid(String.format(this.nanText, value));
49140 if(num < this.minValue){
49141 this.markInvalid(String.format(this.minText, this.minValue));
49145 if(num > this.maxValue){
49146 this.markInvalid(String.format(this.maxText, this.maxValue));
49153 validate : function()
49155 if(this.disabled || this.allowBlank){
49160 var currency = this.getCurrency();
49162 if(this.validateValue(this.getRawValue()) && currency.length){
49167 this.markInvalid();
49171 getName: function()
49176 beforeBlur : function()
49182 var v = this.parseValue(this.getRawValue());
49189 onBlur : function()
49193 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49194 //this.el.removeClass(this.focusClass);
49197 this.hasFocus = false;
49199 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49203 var v = this.getValue();
49205 if(String(v) !== String(this.startValue)){
49206 this.fireEvent('change', this, v, this.startValue);
49209 this.fireEvent("blur", this);
49212 inputEl : function()
49214 return this.el.select('.roo-money-amount-input', true).first();
49217 currencyEl : function()
49219 return this.el.select('.roo-money-currency-input', true).first();
49222 hiddenEl : function()
49224 return this.el.select('input.hidden-number-input',true).first();
49228 * @class Roo.bootstrap.BezierSignature
49229 * @extends Roo.bootstrap.Component
49230 * Bootstrap BezierSignature class
49231 * This script refer to:
49232 * Title: Signature Pad
49234 * Availability: https://github.com/szimek/signature_pad
49237 * Create a new BezierSignature
49238 * @param {Object} config The config object
49241 Roo.bootstrap.BezierSignature = function(config){
49242 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49248 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49255 mouse_btn_down: true,
49258 * @cfg {int} canvas height
49260 canvas_height: '200px',
49263 * @cfg {float|function} Radius of a single dot.
49268 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49273 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49278 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49283 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49288 * @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.
49290 bg_color: 'rgba(0, 0, 0, 0)',
49293 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49295 dot_color: 'black',
49298 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49300 velocity_filter_weight: 0.7,
49303 * @cfg {function} Callback when stroke begin.
49308 * @cfg {function} Callback when stroke end.
49312 getAutoCreate : function()
49314 var cls = 'roo-signature column';
49317 cls += ' ' + this.cls;
49327 for(var i = 0; i < col_sizes.length; i++) {
49328 if(this[col_sizes[i]]) {
49329 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49339 cls: 'roo-signature-body',
49343 cls: 'roo-signature-body-canvas',
49344 height: this.canvas_height,
49345 width: this.canvas_width
49352 style: 'display: none'
49360 initEvents: function()
49362 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49364 var canvas = this.canvasEl();
49366 // mouse && touch event swapping...
49367 canvas.dom.style.touchAction = 'none';
49368 canvas.dom.style.msTouchAction = 'none';
49370 this.mouse_btn_down = false;
49371 canvas.on('mousedown', this._handleMouseDown, this);
49372 canvas.on('mousemove', this._handleMouseMove, this);
49373 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49375 if (window.PointerEvent) {
49376 canvas.on('pointerdown', this._handleMouseDown, this);
49377 canvas.on('pointermove', this._handleMouseMove, this);
49378 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49381 if ('ontouchstart' in window) {
49382 canvas.on('touchstart', this._handleTouchStart, this);
49383 canvas.on('touchmove', this._handleTouchMove, this);
49384 canvas.on('touchend', this._handleTouchEnd, this);
49387 Roo.EventManager.onWindowResize(this.resize, this, true);
49389 // file input event
49390 this.fileEl().on('change', this.uploadImage, this);
49397 resize: function(){
49399 var canvas = this.canvasEl().dom;
49400 var ctx = this.canvasElCtx();
49401 var img_data = false;
49403 if(canvas.width > 0) {
49404 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49406 // setting canvas width will clean img data
49409 var style = window.getComputedStyle ?
49410 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49412 var padding_left = parseInt(style.paddingLeft) || 0;
49413 var padding_right = parseInt(style.paddingRight) || 0;
49415 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49418 ctx.putImageData(img_data, 0, 0);
49422 _handleMouseDown: function(e)
49424 if (e.browserEvent.which === 1) {
49425 this.mouse_btn_down = true;
49426 this.strokeBegin(e);
49430 _handleMouseMove: function (e)
49432 if (this.mouse_btn_down) {
49433 this.strokeMoveUpdate(e);
49437 _handleMouseUp: function (e)
49439 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49440 this.mouse_btn_down = false;
49445 _handleTouchStart: function (e) {
49447 e.preventDefault();
49448 if (e.browserEvent.targetTouches.length === 1) {
49449 // var touch = e.browserEvent.changedTouches[0];
49450 // this.strokeBegin(touch);
49452 this.strokeBegin(e); // assume e catching the correct xy...
49456 _handleTouchMove: function (e) {
49457 e.preventDefault();
49458 // var touch = event.targetTouches[0];
49459 // _this._strokeMoveUpdate(touch);
49460 this.strokeMoveUpdate(e);
49463 _handleTouchEnd: function (e) {
49464 var wasCanvasTouched = e.target === this.canvasEl().dom;
49465 if (wasCanvasTouched) {
49466 e.preventDefault();
49467 // var touch = event.changedTouches[0];
49468 // _this._strokeEnd(touch);
49473 reset: function () {
49474 this._lastPoints = [];
49475 this._lastVelocity = 0;
49476 this._lastWidth = (this.min_width + this.max_width) / 2;
49477 this.canvasElCtx().fillStyle = this.dot_color;
49480 strokeMoveUpdate: function(e)
49482 this.strokeUpdate(e);
49484 if (this.throttle) {
49485 this.throttleStroke(this.strokeUpdate, this.throttle);
49488 this.strokeUpdate(e);
49492 strokeBegin: function(e)
49494 var newPointGroup = {
49495 color: this.dot_color,
49499 if (typeof this.onBegin === 'function') {
49503 this.curve_data.push(newPointGroup);
49505 this.strokeUpdate(e);
49508 strokeUpdate: function(e)
49510 var rect = this.canvasEl().dom.getBoundingClientRect();
49511 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49512 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49513 var lastPoints = lastPointGroup.points;
49514 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49515 var isLastPointTooClose = lastPoint
49516 ? point.distanceTo(lastPoint) <= this.min_distance
49518 var color = lastPointGroup.color;
49519 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49520 var curve = this.addPoint(point);
49522 this.drawDot({color: color, point: point});
49525 this.drawCurve({color: color, curve: curve});
49535 strokeEnd: function(e)
49537 this.strokeUpdate(e);
49538 if (typeof this.onEnd === 'function') {
49543 addPoint: function (point) {
49544 var _lastPoints = this._lastPoints;
49545 _lastPoints.push(point);
49546 if (_lastPoints.length > 2) {
49547 if (_lastPoints.length === 3) {
49548 _lastPoints.unshift(_lastPoints[0]);
49550 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49551 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49552 _lastPoints.shift();
49558 calculateCurveWidths: function (startPoint, endPoint) {
49559 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49560 (1 - this.velocity_filter_weight) * this._lastVelocity;
49562 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49565 start: this._lastWidth
49568 this._lastVelocity = velocity;
49569 this._lastWidth = newWidth;
49573 drawDot: function (_a) {
49574 var color = _a.color, point = _a.point;
49575 var ctx = this.canvasElCtx();
49576 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49578 this.drawCurveSegment(point.x, point.y, width);
49580 ctx.fillStyle = color;
49584 drawCurve: function (_a) {
49585 var color = _a.color, curve = _a.curve;
49586 var ctx = this.canvasElCtx();
49587 var widthDelta = curve.endWidth - curve.startWidth;
49588 var drawSteps = Math.floor(curve.length()) * 2;
49590 ctx.fillStyle = color;
49591 for (var i = 0; i < drawSteps; i += 1) {
49592 var t = i / drawSteps;
49598 var x = uuu * curve.startPoint.x;
49599 x += 3 * uu * t * curve.control1.x;
49600 x += 3 * u * tt * curve.control2.x;
49601 x += ttt * curve.endPoint.x;
49602 var y = uuu * curve.startPoint.y;
49603 y += 3 * uu * t * curve.control1.y;
49604 y += 3 * u * tt * curve.control2.y;
49605 y += ttt * curve.endPoint.y;
49606 var width = curve.startWidth + ttt * widthDelta;
49607 this.drawCurveSegment(x, y, width);
49613 drawCurveSegment: function (x, y, width) {
49614 var ctx = this.canvasElCtx();
49616 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49617 this.is_empty = false;
49622 var ctx = this.canvasElCtx();
49623 var canvas = this.canvasEl().dom;
49624 ctx.fillStyle = this.bg_color;
49625 ctx.clearRect(0, 0, canvas.width, canvas.height);
49626 ctx.fillRect(0, 0, canvas.width, canvas.height);
49627 this.curve_data = [];
49629 this.is_empty = true;
49634 return this.el.select('input',true).first();
49637 canvasEl: function()
49639 return this.el.select('canvas',true).first();
49642 canvasElCtx: function()
49644 return this.el.select('canvas',true).first().dom.getContext('2d');
49647 getImage: function(type)
49649 if(this.is_empty) {
49654 return this.canvasEl().dom.toDataURL('image/'+type, 1);
49657 drawFromImage: function(img_src)
49659 var img = new Image();
49661 img.onload = function(){
49662 this.canvasElCtx().drawImage(img, 0, 0);
49667 this.is_empty = false;
49670 selectImage: function()
49672 this.fileEl().dom.click();
49675 uploadImage: function(e)
49677 var reader = new FileReader();
49679 reader.onload = function(e){
49680 var img = new Image();
49681 img.onload = function(){
49683 this.canvasElCtx().drawImage(img, 0, 0);
49685 img.src = e.target.result;
49688 reader.readAsDataURL(e.target.files[0]);
49691 // Bezier Point Constructor
49692 Point: (function () {
49693 function Point(x, y, time) {
49696 this.time = time || Date.now();
49698 Point.prototype.distanceTo = function (start) {
49699 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49701 Point.prototype.equals = function (other) {
49702 return this.x === other.x && this.y === other.y && this.time === other.time;
49704 Point.prototype.velocityFrom = function (start) {
49705 return this.time !== start.time
49706 ? this.distanceTo(start) / (this.time - start.time)
49713 // Bezier Constructor
49714 Bezier: (function () {
49715 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49716 this.startPoint = startPoint;
49717 this.control2 = control2;
49718 this.control1 = control1;
49719 this.endPoint = endPoint;
49720 this.startWidth = startWidth;
49721 this.endWidth = endWidth;
49723 Bezier.fromPoints = function (points, widths, scope) {
49724 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49725 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49726 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49728 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49729 var dx1 = s1.x - s2.x;
49730 var dy1 = s1.y - s2.y;
49731 var dx2 = s2.x - s3.x;
49732 var dy2 = s2.y - s3.y;
49733 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49734 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49735 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49736 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49737 var dxm = m1.x - m2.x;
49738 var dym = m1.y - m2.y;
49739 var k = l2 / (l1 + l2);
49740 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49741 var tx = s2.x - cm.x;
49742 var ty = s2.y - cm.y;
49744 c1: new scope.Point(m1.x + tx, m1.y + ty),
49745 c2: new scope.Point(m2.x + tx, m2.y + ty)
49748 Bezier.prototype.length = function () {
49753 for (var i = 0; i <= steps; i += 1) {
49755 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49756 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49758 var xdiff = cx - px;
49759 var ydiff = cy - py;
49760 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49767 Bezier.prototype.point = function (t, start, c1, c2, end) {
49768 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49769 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49770 + (3.0 * c2 * (1.0 - t) * t * t)
49771 + (end * t * t * t);
49776 throttleStroke: function(fn, wait) {
49777 if (wait === void 0) { wait = 250; }
49779 var timeout = null;
49783 var later = function () {
49784 previous = Date.now();
49786 result = fn.apply(storedContext, storedArgs);
49788 storedContext = null;
49792 return function wrapper() {
49794 for (var _i = 0; _i < arguments.length; _i++) {
49795 args[_i] = arguments[_i];
49797 var now = Date.now();
49798 var remaining = wait - (now - previous);
49799 storedContext = this;
49801 if (remaining <= 0 || remaining > wait) {
49803 clearTimeout(timeout);
49807 result = fn.apply(storedContext, storedArgs);
49809 storedContext = null;
49813 else if (!timeout) {
49814 timeout = window.setTimeout(later, remaining);
49824 // old names for form elements
49825 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
49826 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
49827 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
49828 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
49829 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
49830 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
49831 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
49832 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
49833 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
49834 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
49835 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
49836 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
49837 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
49838 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
49839 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
49840 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
49841 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
49842 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
49843 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
49844 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
49845 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
49846 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
49847 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
49848 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
49849 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
49850 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
49852 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
49853 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49855 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
49856 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
49858 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
49859 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49860 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
49861 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator