2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
22 * Ext JS Library 1.1.1
23 * Copyright(c) 2006-2007, Ext JS, LLC.
25 * Originally Released Under LGPL - original licence link has changed is not relivant.
28 * <script type="text/javascript">
34 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
35 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
36 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
39 * @param {Object} config The config object
41 Roo.Shadow = function(config){
42 Roo.apply(this, config);
43 if(typeof this.mode != "string"){
44 this.mode = this.defaultMode;
46 var o = this.offset, a = {h: 0};
47 var rad = Math.floor(this.offset/2);
48 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
54 a.l -= this.offset + rad;
55 a.t -= this.offset + rad;
66 a.l -= (this.offset - rad);
67 a.t -= this.offset + rad;
69 a.w -= (this.offset - rad)*2;
80 a.l -= (this.offset - rad);
81 a.t -= (this.offset - rad);
83 a.w -= (this.offset + rad + 1);
84 a.h -= (this.offset + rad);
93 Roo.Shadow.prototype = {
96 * The shadow display mode. Supports the following options:<br />
97 * sides: Shadow displays on both sides and bottom only<br />
98 * frame: Shadow displays equally on all four sides<br />
99 * drop: Traditional bottom-right drop shadow (default)
103 * @cfg {String} offset
104 * The number of pixels to offset the shadow from the element (defaults to 4)
112 * Displays the shadow under the target element
113 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
115 show : function(target){
116 target = Roo.get(target);
118 this.el = Roo.Shadow.Pool.pull();
119 if(this.el.dom.nextSibling != target.dom){
120 this.el.insertBefore(target);
123 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
125 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
128 target.getLeft(true),
133 this.el.dom.style.display = "block";
137 * Returns true if the shadow is visible, else false
139 isVisible : function(){
140 return this.el ? true : false;
144 * Direct alignment when values are already available. Show must be called at least once before
145 * calling this method to ensure it is initialized.
146 * @param {Number} left The target element left position
147 * @param {Number} top The target element top position
148 * @param {Number} width The target element width
149 * @param {Number} height The target element height
151 realign : function(l, t, w, h){
155 var a = this.adjusts, d = this.el.dom, s = d.style;
157 s.left = (l+a.l)+"px";
158 s.top = (t+a.t)+"px";
159 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
161 if(s.width != sws || s.height != shs){
165 var cn = d.childNodes;
166 var sww = Math.max(0, (sw-12))+"px";
167 cn[0].childNodes[1].style.width = sww;
168 cn[1].childNodes[1].style.width = sww;
169 cn[2].childNodes[1].style.width = sww;
170 cn[1].style.height = Math.max(0, (sh-12))+"px";
180 this.el.dom.style.display = "none";
181 Roo.Shadow.Pool.push(this.el);
187 * Adjust the z-index of this shadow
188 * @param {Number} zindex The new z-index
190 setZIndex : function(z){
193 this.el.setStyle("z-index", z);
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
201 var markup = Roo.isIE ?
202 '<div class="x-ie-shadow"></div>' :
203 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
208 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209 sh.autoBoxAdjust = false;
221 * base class for bootstrap elements.
225 Roo.bootstrap = Roo.bootstrap || {};
227 * @class Roo.bootstrap.Component
228 * @extends Roo.Component
230 * @children Roo.bootstrap.Component
231 * Bootstrap Component base class
232 * @cfg {String} cls css class
233 * @cfg {String} style any extra css
234 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
236 * @cfg {string} dataId cutomer id
237 * @cfg {string} name Specifies name attribute
238 * @cfg {string} tooltip Text for the tooltip
239 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
240 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
243 * Do not use directly - it does not do anything..
244 * @param {Object} config The config object
249 Roo.bootstrap.Component = function(config){
250 Roo.bootstrap.Component.superclass.constructor.call(this, config);
254 * @event childrenrendered
255 * Fires when the children have been rendered..
256 * @param {Roo.bootstrap.Component} this
258 "childrenrendered" : true
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
270 allowDomMove : false, // to stop relocations in parent onRender...
280 * Initialize Events for the element
282 initEvents : function() { },
288 can_build_overlaid : true,
290 container_method : false,
297 // returns the parent component..
298 return Roo.ComponentMgr.get(this.parentId)
304 onRender : function(ct, position)
306 // Roo.log("Call onRender: " + this.xtype);
308 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311 if (this.el.attr('xtype')) {
312 this.el.attr('xtypex', this.el.attr('xtype'));
313 this.el.dom.removeAttribute('xtype');
323 var cfg = Roo.apply({}, this.getAutoCreate());
325 cfg.id = this.id || Roo.id();
327 // fill in the extra attributes
328 if (this.xattr && typeof(this.xattr) =='object') {
329 for (var i in this.xattr) {
330 cfg[i] = this.xattr[i];
335 cfg.dataId = this.dataId;
339 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342 if (this.style) { // fixme needs to support more complex style data.
343 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347 cfg.name = this.name;
350 this.el = ct.createChild(cfg, position);
353 this.tooltipEl().attr('tooltip', this.tooltip);
356 if(this.tabIndex !== undefined){
357 this.el.dom.setAttribute('tabIndex', this.tabIndex);
364 * Fetch the element to add children to
365 * @return {Roo.Element} defaults to this.el
367 getChildContainer : function()
371 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373 return Roo.get(document.body);
377 * Fetch the element to display the tooltip on.
378 * @return {Roo.Element} defaults to this.el
380 tooltipEl : function()
385 addxtype : function(tree,cntr)
389 cn = Roo.factory(tree);
390 //Roo.log(['addxtype', cn]);
392 cn.parentType = this.xtype; //??
393 cn.parentId = this.id;
395 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396 if (typeof(cn.container_method) == 'string') {
397 cntr = cn.container_method;
401 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
403 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
405 var build_from_html = Roo.XComponent.build_from_html;
407 var is_body = (tree.xtype == 'Body') ;
409 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411 var self_cntr_el = Roo.get(this[cntr](false));
413 // do not try and build conditional elements
414 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420 return this.addxtypeChild(tree,cntr, is_body);
423 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429 Roo.log('skipping render');
435 if (!build_from_html) {
439 // this i think handles overlaying multiple children of the same type
440 // with the sam eelement.. - which might be buggy..
442 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
448 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459 addxtypeChild : function (tree, cntr, is_body)
461 Roo.debug && Roo.log('addxtypeChild:' + cntr);
463 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467 (typeof(tree['flexy:foreach']) != 'undefined');
471 skip_children = false;
472 // render the element if it's not BODY.
475 // if parent was disabled, then do not try and create the children..
476 if(!this[cntr](true)){
481 cn = Roo.factory(tree);
483 cn.parentType = this.xtype; //??
484 cn.parentId = this.id;
486 var build_from_html = Roo.XComponent.build_from_html;
489 // does the container contain child eleemnts with 'xtype' attributes.
490 // that match this xtype..
491 // note - when we render we create these as well..
492 // so we should check to see if body has xtype set.
493 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495 var self_cntr_el = Roo.get(this[cntr](false));
496 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498 //Roo.log(Roo.XComponent.build_from_html);
499 //Roo.log("got echild:");
502 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503 // and are not displayed -this causes this to use up the wrong element when matching.
504 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
514 //echild.dom.removeAttribute('xtype');
516 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517 Roo.debug && Roo.log(self_cntr_el);
518 Roo.debug && Roo.log(echild);
519 Roo.debug && Roo.log(cn);
525 // if object has flexy:if - then it may or may not be rendered.
526 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
527 // skip a flexy if element.
528 Roo.debug && Roo.log('skipping render');
529 Roo.debug && Roo.log(tree);
531 Roo.debug && Roo.log('skipping all children');
532 skip_children = true;
537 // actually if flexy:foreach is found, we really want to create
538 // multiple copies here...
540 //Roo.log(this[cntr]());
541 // some elements do not have render methods.. like the layouts...
543 if(this[cntr](true) === false){
548 cn.render && cn.render(this[cntr](true));
551 // then add the element..
558 if (typeof (tree.menu) != 'undefined') {
559 tree.menu.parentType = cn.xtype;
560 tree.menu.triggerEl = cn.el;
561 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565 if (!tree.items || !tree.items.length) {
567 //Roo.log(["no children", this]);
572 var items = tree.items;
575 //Roo.log(items.length);
577 if (!skip_children) {
578 for(var i =0;i < items.length;i++) {
579 // Roo.log(['add child', items[i]]);
580 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
586 //Roo.log("fire childrenrendered");
588 cn.fireEvent('childrenrendered', this);
594 * Set the element that will be used to show or hide
596 setVisibilityEl : function(el)
598 this.visibilityEl = el;
602 * Get the element that will be used to show or hide
604 getVisibilityEl : function()
606 if (typeof(this.visibilityEl) == 'object') {
607 return this.visibilityEl;
610 if (typeof(this.visibilityEl) == 'string') {
611 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
618 * Show a component - removes 'hidden' class
622 if(!this.getVisibilityEl()){
626 this.getVisibilityEl().removeClass(['hidden','d-none']);
628 this.fireEvent('show', this);
633 * Hide a component - adds 'hidden' class
637 if(!this.getVisibilityEl()){
641 this.getVisibilityEl().addClass(['hidden','d-none']);
643 this.fireEvent('hide', this);
656 * @class Roo.bootstrap.Element
657 * @extends Roo.bootstrap.Component
658 * @children Roo.bootstrap.Component
659 * Bootstrap Element class (basically a DIV used to make random stuff )
661 * @cfg {String} html contents of the element
662 * @cfg {String} tag tag of the element
663 * @cfg {String} cls class of the element
664 * @cfg {Boolean} preventDefault (true|false) default false
665 * @cfg {Boolean} clickable (true|false) default false
666 * @cfg {String} role default blank - set to button to force cursor pointer
670 * Create a new Element
671 * @param {Object} config The config object
674 Roo.bootstrap.Element = function(config){
675 Roo.bootstrap.Element.superclass.constructor.call(this, config);
681 * When a element is chick
682 * @param {Roo.bootstrap.Element} this
683 * @param {Roo.EventObject} e
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
696 preventDefault: false,
701 getAutoCreate : function(){
705 // cls: this.cls, double assign in parent class Component.js :: onRender
708 if (this.role !== false) {
709 cfg.role = this.role;
715 initEvents: function()
717 Roo.bootstrap.Element.superclass.initEvents.call(this);
720 this.el.on('click', this.onClick, this);
726 onClick : function(e)
728 if(this.preventDefault){
732 this.fireEvent('click', this, e); // why was this double click before?
740 getValue : function()
742 return this.el.dom.innerHTML;
745 setValue : function(value)
747 this.el.dom.innerHTML = value;
762 * @class Roo.bootstrap.DropTarget
763 * @extends Roo.bootstrap.Element
764 * Bootstrap DropTarget class
766 * @cfg {string} name dropable name
769 * Create a new Dropable Area
770 * @param {Object} config The config object
773 Roo.bootstrap.DropTarget = function(config){
774 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
780 * When a element is chick
781 * @param {Roo.bootstrap.Element} this
782 * @param {Roo.EventObject} e
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
791 getAutoCreate : function(){
796 initEvents: function()
798 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802 drop : this.dragDrop.createDelegate(this),
803 enter : this.dragEnter.createDelegate(this),
804 out : this.dragOut.createDelegate(this),
805 over : this.dragOver.createDelegate(this)
809 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812 dragDrop : function(source,e,data)
814 // user has to decide how to impliment this.
817 //this.fireEvent('drop', this, source, e ,data);
821 dragEnter : function(n, dd, e, data)
823 // probably want to resize the element to match the dropped element..
825 this.originalSize = this.el.getSize();
826 this.el.setSize( n.el.getSize());
827 this.dropZone.DDM.refreshCache(this.name);
828 Roo.log([n, dd, e, data]);
831 dragOut : function(value)
833 // resize back to normal
835 this.el.setSize(this.originalSize);
836 this.dropZone.resetConstraints();
839 dragOver : function()
856 * @class Roo.bootstrap.Body
857 * @extends Roo.bootstrap.Component
858 * @children Roo.bootstrap.Component
859 * @parent none builder
860 * Bootstrap Body class
864 * @param {Object} config The config object
867 Roo.bootstrap.Body = function(config){
869 config = config || {};
871 Roo.bootstrap.Body.superclass.constructor.call(this, config);
872 this.el = Roo.get(config.el ? config.el : document.body );
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
880 is_body : true,// just to make sure it's constructed?
885 onRender : function(ct, position)
887 /* Roo.log("Roo.bootstrap.Body - onRender");
888 if (this.cls && this.cls.length) {
889 Roo.get(document.body).addClass(this.cls);
908 * @class Roo.bootstrap.ButtonGroup
909 * @extends Roo.bootstrap.Component
910 * Bootstrap ButtonGroup class
911 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913 * @cfg {String} size lg | sm | xs (default empty normal)
914 * @cfg {String} align vertical | justified (default none)
915 * @cfg {String} direction up | down (default down)
916 * @cfg {Boolean} toolbar false | true
917 * @cfg {Boolean} btn true | false
922 * @param {Object} config The config object
925 Roo.bootstrap.ButtonGroup = function(config){
926 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
937 getAutoCreate : function(){
943 cfg.html = this.html || cfg.html;
954 if (['vertical','justified'].indexOf(this.align)!==-1) {
955 cfg.cls = 'btn-group-' + this.align;
957 if (this.align == 'justified') {
958 console.log(this.items);
962 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963 cfg.cls += ' btn-group-' + this.size;
966 if (this.direction == 'up') {
967 cfg.cls += ' dropup' ;
973 * Add a button to the group (similar to NavItem API.)
975 addItem : function(cfg)
977 var cn = new Roo.bootstrap.Button(cfg);
979 cn.parentId = this.id;
980 cn.onRender(this.el, null);
994 * @class Roo.bootstrap.Button
995 * @extends Roo.bootstrap.Component
996 * Bootstrap Button class
997 * @cfg {String} html The button content
998 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001 * @cfg {String} size (lg|sm|xs)
1002 * @cfg {String} tag (a|input|submit)
1003 * @cfg {String} href empty or href
1004 * @cfg {Boolean} disabled default false;
1005 * @cfg {Boolean} isClose default false;
1006 * @cfg {String} glyphicon depricated - use fa
1007 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008 * @cfg {String} badge text for badge
1009 * @cfg {String} theme (default|glow)
1010 * @cfg {Boolean} inverse dark themed version
1011 * @cfg {Boolean} toggle is it a slidy toggle button
1012 * @cfg {Boolean} pressed default null - if the button ahs active state
1013 * @cfg {String} ontext text for on slidy toggle state
1014 * @cfg {String} offtext text for off slidy toggle state
1015 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1016 * @cfg {Boolean} removeClass remove the standard class..
1017 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1018 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1022 * Create a new button
1023 * @param {Object} config The config object
1027 Roo.bootstrap.Button = function(config){
1028 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1034 * When a button is pressed
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * When a button is double clicked
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1048 * After the button has been toggles
1049 * @param {Roo.bootstrap.Button} btn
1050 * @param {Roo.EventObject} e
1051 * @param {boolean} pressed (also available as button.pressed)
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1078 preventDefault: true,
1087 getAutoCreate : function(){
1095 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097 this.tag = 'button';
1101 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103 if (this.toggle == true) {
1106 cls: 'slider-frame roo-button',
1110 'data-on-text':'ON',
1111 'data-off-text':'OFF',
1112 cls: 'slider-button',
1117 // why are we validating the weights?
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119 cfg.cls += ' ' + this.weight;
1126 cfg.cls += ' close';
1128 cfg["aria-hidden"] = true;
1130 cfg.html = "×";
1136 if (this.theme==='default') {
1137 cfg.cls = 'btn roo-button';
1139 //if (this.parentType != 'Navbar') {
1140 this.weight = this.weight.length ? this.weight : 'default';
1142 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146 cfg.cls += ' btn-' + outline + weight;
1147 if (this.weight == 'default') {
1149 cfg.cls += ' btn-' + this.weight;
1152 } else if (this.theme==='glow') {
1155 cfg.cls = 'btn-glow roo-button';
1157 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159 cfg.cls += ' ' + this.weight;
1165 this.cls += ' inverse';
1169 if (this.active || this.pressed === true) {
1170 cfg.cls += ' active';
1173 if (this.disabled) {
1174 cfg.disabled = 'disabled';
1178 Roo.log('changing to ul' );
1180 this.glyphicon = 'caret';
1181 if (Roo.bootstrap.version == 4) {
1182 this.fa = 'caret-down';
1187 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189 //gsRoo.log(this.parentType);
1190 if (this.parentType === 'Navbar' && !this.parent().bar) {
1191 Roo.log('changing to li?');
1200 href : this.href || '#'
1203 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1204 cfg.cls += ' dropdown';
1211 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1213 if (this.glyphicon) {
1214 cfg.html = ' ' + cfg.html;
1219 cls: 'glyphicon glyphicon-' + this.glyphicon
1224 cfg.html = ' ' + cfg.html;
1229 cls: 'fa fas fa-' + this.fa
1239 // cfg.cls='btn roo-button';
1243 var value = cfg.html;
1248 cls: 'glyphicon glyphicon-' + this.glyphicon,
1255 cls: 'fa fas fa-' + this.fa,
1260 var bw = this.badge_weight.length ? this.badge_weight :
1261 (this.weight.length ? this.weight : 'secondary');
1262 bw = bw == 'default' ? 'secondary' : bw;
1268 cls: 'badge badge-' + bw,
1277 cfg.cls += ' dropdown';
1278 cfg.html = typeof(cfg.html) != 'undefined' ?
1279 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282 if (cfg.tag !== 'a' && this.href !== '') {
1283 throw "Tag must be a to set href.";
1284 } else if (this.href.length > 0) {
1285 cfg.href = this.href;
1288 if(this.removeClass){
1293 cfg.target = this.target;
1298 initEvents: function() {
1299 // Roo.log('init events?');
1300 // Roo.log(this.el.dom);
1303 if (typeof (this.menu) != 'undefined') {
1304 this.menu.parentType = this.xtype;
1305 this.menu.triggerEl = this.el;
1306 this.addxtype(Roo.apply({}, this.menu));
1310 if (this.el.hasClass('roo-button')) {
1311 this.el.on('click', this.onClick, this);
1312 this.el.on('dblclick', this.onDblClick, this);
1314 this.el.select('.roo-button').on('click', this.onClick, this);
1315 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319 if(this.removeClass){
1320 this.el.on('click', this.onClick, this);
1323 if (this.group === true) {
1324 if (this.pressed === false || this.pressed === true) {
1327 this.pressed = false;
1328 this.setActive(this.pressed);
1333 this.el.enableDisplayMode();
1336 onClick : function(e)
1338 if (this.disabled) {
1342 Roo.log('button on click ');
1343 if(this.href === '' || this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2943 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2951 initEvents : function()
2954 Roo.bootstrap.Button.prototype.initEvents.call(this);
2960 this.urlAPI = (window.createObjectURL && window) ||
2961 (window.URL && URL.revokeObjectURL && URL) ||
2962 (window.webkitURL && webkitURL);
2967 cls : 'd-none roo-card-upload-selector'
2970 if (this.multiple) {
2971 im.multiple = 'multiple';
2973 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2975 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * @children Roo.bootstrap.Component
3384 * Bootstrap Header class
3387 * @cfg {String} html content of header
3388 * @cfg {Number} level (1|2|3|4|5|6) default 1
3391 * Create a new Header
3392 * @param {Object} config The config object
3396 Roo.bootstrap.Header = function(config){
3397 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3408 getAutoCreate : function(){
3413 tag: 'h' + (1 *this.level),
3414 html: this.html || ''
3425 * @class Roo.bootstrap.MenuMgr
3427 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430 Roo.bootstrap.menu.Manager = function(){
3431 var menus, active, groups = {}, attached = false, lastShow = new Date();
3433 // private - called when first menu is created
3436 active = new Roo.util.MixedCollection();
3437 Roo.get(document).addKeyListener(27, function(){
3438 if(active.length > 0){
3446 if(active && active.length > 0){
3447 var c = active.clone();
3457 if(active.length < 1){
3458 Roo.get(document).un("mouseup", onMouseDown);
3466 var last = active.last();
3467 lastShow = new Date();
3470 Roo.get(document).on("mouseup", onMouseDown);
3475 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476 m.parentMenu.activeChild = m;
3477 }else if(last && last.isVisible()){
3478 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3483 function onBeforeHide(m){
3485 m.activeChild.hide();
3487 if(m.autoHideTimer){
3488 clearTimeout(m.autoHideTimer);
3489 delete m.autoHideTimer;
3494 function onBeforeShow(m){
3495 var pm = m.parentMenu;
3496 if(!pm && !m.allowOtherMenus){
3498 }else if(pm && pm.activeChild && active != m){
3499 pm.activeChild.hide();
3503 // private this should really trigger on mouseup..
3504 function onMouseDown(e){
3505 Roo.log("on Mouse Up");
3507 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508 Roo.log("MenuManager hideAll");
3517 function onBeforeCheck(mi, state){
3519 var g = groups[mi.group];
3520 for(var i = 0, l = g.length; i < l; i++){
3522 g[i].setChecked(false);
3531 * Hides all menus that are currently visible
3533 hideAll : function(){
3538 register : function(menu){
3542 menus[menu.id] = menu;
3543 menu.on("beforehide", onBeforeHide);
3544 menu.on("hide", onHide);
3545 menu.on("beforeshow", onBeforeShow);
3546 menu.on("show", onShow);
3548 if(g && menu.events["checkchange"]){
3552 groups[g].push(menu);
3553 menu.on("checkchange", onCheck);
3558 * Returns a {@link Roo.menu.Menu} object
3559 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560 * be used to generate and return a new Menu instance.
3562 get : function(menu){
3563 if(typeof menu == "string"){ // menu id
3565 }else if(menu.events){ // menu instance
3568 /*else if(typeof menu.length == 'number'){ // array of menu items?
3569 return new Roo.bootstrap.Menu({items:menu});
3570 }else{ // otherwise, must be a config
3571 return new Roo.bootstrap.Menu(menu);
3578 unregister : function(menu){
3579 delete menus[menu.id];
3580 menu.un("beforehide", onBeforeHide);
3581 menu.un("hide", onHide);
3582 menu.un("beforeshow", onBeforeShow);
3583 menu.un("show", onShow);
3585 if(g && menu.events["checkchange"]){
3586 groups[g].remove(menu);
3587 menu.un("checkchange", onCheck);
3592 registerCheckable : function(menuItem){
3593 var g = menuItem.group;
3598 groups[g].push(menuItem);
3599 menuItem.on("beforecheckchange", onBeforeCheck);
3604 unregisterCheckable : function(menuItem){
3605 var g = menuItem.group;
3607 groups[g].remove(menuItem);
3608 menuItem.un("beforecheckchange", onBeforeCheck);
3614 * @class Roo.bootstrap.menu.Menu
3615 * @extends Roo.bootstrap.Component
3617 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3619 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3621 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622 * @cfg {bool} hidden if the menu should be hidden when rendered.
3623 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3624 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3626 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3630 * @param {Object} config The config objectQ
3634 Roo.bootstrap.menu.Menu = function(config){
3636 if (config.type == 'treeview') {
3637 // normally menu's are drawn attached to the document to handle layering etc..
3638 // however treeview (used by the docs menu is drawn into the parent element)
3639 this.container_method = 'getChildContainer';
3642 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643 if (this.registerMenu && this.type != 'treeview') {
3644 Roo.bootstrap.menu.Manager.register(this);
3651 * Fires before this menu is displayed (return false to block)
3652 * @param {Roo.menu.Menu} this
3657 * Fires before this menu is hidden (return false to block)
3658 * @param {Roo.menu.Menu} this
3663 * Fires after this menu is displayed
3664 * @param {Roo.menu.Menu} this
3669 * Fires after this menu is hidden
3670 * @param {Roo.menu.Menu} this
3675 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678 * @param {Roo.EventObject} e
3683 * Fires when the mouse is hovering over this menu
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.EventObject} e
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691 * Fires when the mouse exits this menu
3692 * @param {Roo.menu.Menu} this
3693 * @param {Roo.EventObject} e
3694 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699 * Fires when a menu item contained in this menu is clicked
3700 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701 * @param {Roo.EventObject} e
3705 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3712 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3715 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717 registerMenu : true,
3719 menuItems :false, // stores the menu items..
3729 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731 hideTrigger : false,
3736 getChildContainer : function() {
3740 getAutoCreate : function(){
3742 //if (['right'].indexOf(this.align)!==-1) {
3743 // cfg.cn[1].cls += ' pull-right'
3748 cls : 'dropdown-menu shadow' ,
3749 style : 'z-index:1000'
3753 if (this.type === 'submenu') {
3754 cfg.cls = 'submenu active';
3756 if (this.type === 'treeview') {
3757 cfg.cls = 'treeview-menu';
3762 initEvents : function() {
3764 // Roo.log("ADD event");
3765 // Roo.log(this.triggerEl.dom);
3766 if (this.triggerEl) {
3768 this.triggerEl.on('click', this.onTriggerClick, this);
3770 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772 if (!this.hideTrigger) {
3773 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774 // dropdown toggle on the 'a' in BS4?
3775 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777 this.triggerEl.addClass('dropdown-toggle');
3783 this.el.on('touchstart' , this.onTouch, this);
3785 this.el.on('click' , this.onClick, this);
3787 this.el.on("mouseover", this.onMouseOver, this);
3788 this.el.on("mouseout", this.onMouseOut, this);
3792 findTargetItem : function(e)
3794 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3798 //Roo.log(t); Roo.log(t.id);
3800 //Roo.log(this.menuitems);
3801 return this.menuitems.get(t.id);
3803 //return this.items.get(t.menuItemId);
3809 onTouch : function(e)
3811 Roo.log("menu.onTouch");
3812 //e.stopEvent(); this make the user popdown broken
3816 onClick : function(e)
3818 Roo.log("menu.onClick");
3820 var t = this.findTargetItem(e);
3821 if(!t || t.isContainer){
3826 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3827 if(t == this.activeItem && t.shouldDeactivate(e)){
3828 this.activeItem.deactivate();
3829 delete this.activeItem;
3833 this.setActiveItem(t, true);
3841 Roo.log('pass click event');
3845 this.fireEvent("click", this, t, e);
3849 if(!t.href.length || t.href == '#'){
3850 (function() { _this.hide(); }).defer(100);
3855 onMouseOver : function(e){
3856 var t = this.findTargetItem(e);
3859 // if(t.canActivate && !t.disabled){
3860 // this.setActiveItem(t, true);
3864 this.fireEvent("mouseover", this, e, t);
3866 isVisible : function(){
3867 return !this.hidden;
3869 onMouseOut : function(e){
3870 var t = this.findTargetItem(e);
3873 // if(t == this.activeItem && t.shouldDeactivate(e)){
3874 // this.activeItem.deactivate();
3875 // delete this.activeItem;
3878 this.fireEvent("mouseout", this, e, t);
3883 * Displays this menu relative to another element
3884 * @param {String/HTMLElement/Roo.Element} element The element to align to
3885 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886 * the element (defaults to this.defaultAlign)
3887 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889 show : function(el, pos, parentMenu)
3891 if (false === this.fireEvent("beforeshow", this)) {
3892 Roo.log("show canceled");
3895 this.parentMenu = parentMenu;
3899 this.el.addClass('show'); // show otherwise we do not know how big we are..
3901 var xy = this.el.getAlignToXY(el, pos);
3903 // bl-tl << left align below
3904 // tl-bl << left align
3906 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907 // if it goes to far to the right.. -> align left.
3908 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911 // was left align - go right?
3912 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915 // goes down the bottom
3916 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918 var a = this.align.replace('?', '').split('-');
3919 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3923 this.showAt( xy , parentMenu, false);
3926 * Displays this menu at a specific xy position
3927 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930 showAt : function(xy, parentMenu, /* private: */_e){
3931 this.parentMenu = parentMenu;
3936 this.fireEvent("beforeshow", this);
3937 //xy = this.el.adjustForConstraints(xy);
3941 this.hideMenuItems();
3942 this.hidden = false;
3943 if (this.triggerEl) {
3944 this.triggerEl.addClass('open');
3947 this.el.addClass('show');
3951 // reassign x when hitting right
3953 // reassign y when hitting bottom
3955 // but the list may align on trigger left or trigger top... should it be a properity?
3957 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3962 this.fireEvent("show", this);
3968 this.doFocus.defer(50, this);
3972 doFocus : function(){
3974 this.focusEl.focus();
3979 * Hides this menu and optionally all parent menus
3980 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982 hide : function(deep)
3984 if (false === this.fireEvent("beforehide", this)) {
3985 Roo.log("hide canceled");
3988 this.hideMenuItems();
3989 if(this.el && this.isVisible()){
3991 if(this.activeItem){
3992 this.activeItem.deactivate();
3993 this.activeItem = null;
3995 if (this.triggerEl) {
3996 this.triggerEl.removeClass('open');
3999 this.el.removeClass('show');
4001 this.fireEvent("hide", this);
4003 if(deep === true && this.parentMenu){
4004 this.parentMenu.hide(true);
4008 onTriggerClick : function(e)
4010 Roo.log('trigger click');
4012 var target = e.getTarget();
4014 Roo.log(target.nodeName.toLowerCase());
4016 if(target.nodeName.toLowerCase() === 'i'){
4022 onTriggerPress : function(e)
4024 Roo.log('trigger press');
4025 //Roo.log(e.getTarget());
4026 // Roo.log(this.triggerEl.dom);
4028 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029 var pel = Roo.get(e.getTarget());
4030 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031 Roo.log('is treeview or dropdown?');
4035 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039 if (this.isVisible()) {
4045 this.show(this.triggerEl, this.align, false);
4048 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4055 hideMenuItems : function()
4057 Roo.log("hide Menu Items");
4062 this.el.select('.open',true).each(function(aa) {
4064 aa.removeClass('open');
4068 addxtypeChild : function (tree, cntr) {
4069 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071 this.menuitems.add(comp);
4083 this.getEl().dom.innerHTML = '';
4084 this.menuitems.clear();
4090 * @class Roo.bootstrap.menu.Item
4091 * @extends Roo.bootstrap.Component
4092 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093 * @parent Roo.bootstrap.menu.Menu
4095 * Bootstrap MenuItem class
4097 * @cfg {String} html the menu label
4098 * @cfg {String} href the link
4099 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101 * @cfg {Boolean} active used on sidebars to highlight active itesm
4102 * @cfg {String} fa favicon to show on left of menu item.
4103 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107 * Create a new MenuItem
4108 * @param {Object} config The config object
4112 Roo.bootstrap.menu.Item = function(config){
4113 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4118 * The raw click event for the entire grid.
4119 * @param {Roo.bootstrap.menu.Item} this
4120 * @param {Roo.EventObject} e
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4130 preventDefault: false,
4131 isContainer : false,
4135 getAutoCreate : function(){
4137 if(this.isContainer){
4140 cls: 'dropdown-menu-item '
4150 cls : 'dropdown-item',
4155 if (this.fa !== false) {
4158 cls : 'fa fa-' + this.fa
4167 cls: 'dropdown-menu-item',
4170 if (this.parent().type == 'treeview') {
4171 cfg.cls = 'treeview-menu';
4174 cfg.cls += ' active';
4179 anc.href = this.href || cfg.cn[0].href ;
4180 ctag.html = this.html || cfg.cn[0].html ;
4184 initEvents: function()
4186 if (this.parent().type == 'treeview') {
4187 this.el.select('a').on('click', this.onClick, this);
4191 this.menu.parentType = this.xtype;
4192 this.menu.triggerEl = this.el;
4193 this.menu = this.addxtype(Roo.apply({}, this.menu));
4197 onClick : function(e)
4199 //Roo.log('item on click ');
4201 if(this.href === false || this.preventDefault){
4204 //this.parent().hideMenuItems();
4206 this.fireEvent('click', this, e);
4220 * @class Roo.bootstrap.menu.Separator
4221 * @extends Roo.bootstrap.Component
4223 * @parent Roo.bootstrap.menu.Menu
4224 * Bootstrap Separator class
4227 * Create a new Separator
4228 * @param {Object} config The config object
4232 Roo.bootstrap.menu.Separator = function(config){
4233 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4238 getAutoCreate : function(){
4241 cls: 'dropdown-divider divider'
4257 * @class Roo.bootstrap.Modal
4258 * @extends Roo.bootstrap.Component
4259 * @parent none builder
4260 * @children Roo.bootstrap.Component
4261 * Bootstrap Modal class
4262 * @cfg {String} title Title of dialog
4263 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4265 * @cfg {Boolean} specificTitle default false
4266 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268 * @cfg {Boolean} animate default true
4269 * @cfg {Boolean} allow_close default true
4270 * @cfg {Boolean} fitwindow default false
4271 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274 * @cfg {String} size (sm|lg|xl) default empty
4275 * @cfg {Number} max_width set the max width of modal
4276 * @cfg {Boolean} editableTitle can the title be edited
4281 * Create a new Modal Dialog
4282 * @param {Object} config The config object
4285 Roo.bootstrap.Modal = function(config){
4286 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291 * The raw btnclick event for the button
4292 * @param {Roo.EventObject} e
4297 * Fire when dialog resize
4298 * @param {Roo.bootstrap.Modal} this
4299 * @param {Roo.EventObject} e
4303 * @event titlechanged
4304 * Fire when the editable title has been changed
4305 * @param {Roo.bootstrap.Modal} this
4306 * @param {Roo.EventObject} value
4308 "titlechanged" : true
4311 this.buttons = this.buttons || [];
4314 this.tmpl = Roo.factory(this.tmpl);
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4321 title : 'test dialog',
4331 specificTitle: false,
4333 buttonPosition: 'right',
4355 editableTitle : false,
4357 onRender : function(ct, position)
4359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362 var cfg = Roo.apply({}, this.getAutoCreate());
4365 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367 //if (!cfg.name.length) {
4371 cfg.cls += ' ' + this.cls;
4374 cfg.style = this.style;
4376 this.el = Roo.get(document.body).createChild(cfg, position);
4378 //var type = this.el.dom.type;
4381 if(this.tabIndex !== undefined){
4382 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385 this.dialogEl = this.el.select('.modal-dialog',true).first();
4386 this.bodyEl = this.el.select('.modal-body',true).first();
4387 this.closeEl = this.el.select('.modal-header .close', true).first();
4388 this.headerEl = this.el.select('.modal-header',true).first();
4389 this.titleEl = this.el.select('.modal-title',true).first();
4390 this.footerEl = this.el.select('.modal-footer',true).first();
4392 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394 //this.el.addClass("x-dlg-modal");
4396 if (this.buttons.length) {
4397 Roo.each(this.buttons, function(bb) {
4398 var b = Roo.apply({}, bb);
4399 b.xns = b.xns || Roo.bootstrap;
4400 b.xtype = b.xtype || 'Button';
4401 if (typeof(b.listeners) == 'undefined') {
4402 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4405 var btn = Roo.factory(b);
4407 btn.render(this.getButtonContainer());
4411 // render the children.
4414 if(typeof(this.items) != 'undefined'){
4415 var items = this.items;
4418 for(var i =0;i < items.length;i++) {
4419 // we force children not to montor widnow resize - as we do that for them.
4420 items[i].monitorWindowResize = false;
4421 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4548 initEvents : function()
4550 if (this.allow_close) {
4551 this.closeEl.on('click', this.hide, this);
4553 Roo.EventManager.onWindowResize(this.resize, this, true);
4554 if (this.editableTitle) {
4555 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4556 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4557 this.headerEditEl.on('keyup', function(e) {
4558 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4559 this.toggleHeaderInput(false)
4562 this.headerEditEl.on('blur', function(e) {
4563 this.toggleHeaderInput(false)
4572 this.maskEl.setSize(
4573 Roo.lib.Dom.getViewWidth(true),
4574 Roo.lib.Dom.getViewHeight(true)
4577 if (this.fitwindow) {
4579 this.dialogEl.setStyle( { 'max-width' : '100%' });
4581 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4582 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4587 if(this.max_width !== 0) {
4589 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4592 this.setSize(w, this.height);
4596 if(this.max_height) {
4597 this.setSize(w,Math.min(
4599 Roo.lib.Dom.getViewportHeight(true) - 60
4605 if(!this.fit_content) {
4606 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4610 this.setSize(w, Math.min(
4612 this.headerEl.getHeight() +
4613 this.footerEl.getHeight() +
4614 this.getChildHeight(this.bodyEl.dom.childNodes),
4615 Roo.lib.Dom.getViewportHeight(true) - 60)
4621 setSize : function(w,h)
4628 // any layout/border etc.. resize..
4630 this.items.forEach( function(e) {
4631 e.layout ? e.layout() : false;
4640 if (!this.rendered) {
4643 this.toggleHeaderInput(false);
4644 //this.el.setStyle('display', 'block');
4645 this.el.removeClass('hideing');
4646 this.el.dom.style.display='block';
4648 Roo.get(document.body).addClass('modal-open');
4650 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4653 this.el.addClass('show');
4654 this.el.addClass('in');
4657 this.el.addClass('show');
4658 this.el.addClass('in');
4661 // not sure how we can show data in here..
4663 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4666 Roo.get(document.body).addClass("x-body-masked");
4668 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4669 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4670 this.maskEl.dom.style.display = 'block';
4671 this.maskEl.addClass('show');
4676 this.fireEvent('show', this);
4678 // set zindex here - otherwise it appears to be ignored...
4679 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4682 // this is for children that are... layout.Border
4684 this.items.forEach( function(e) {
4685 e.layout ? e.layout() : false;
4693 if(this.fireEvent("beforehide", this) !== false){
4695 this.maskEl.removeClass('show');
4697 this.maskEl.dom.style.display = '';
4698 Roo.get(document.body).removeClass("x-body-masked");
4699 this.el.removeClass('in');
4700 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4702 if(this.animate){ // why
4703 this.el.addClass('hideing');
4704 this.el.removeClass('show');
4706 if (!this.el.hasClass('hideing')) {
4707 return; // it's been shown again...
4710 this.el.dom.style.display='';
4712 Roo.get(document.body).removeClass('modal-open');
4713 this.el.removeClass('hideing');
4717 this.el.removeClass('show');
4718 this.el.dom.style.display='';
4719 Roo.get(document.body).removeClass('modal-open');
4722 this.fireEvent('hide', this);
4725 isVisible : function()
4728 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4732 addButton : function(str, cb)
4736 var b = Roo.apply({}, { html : str } );
4737 b.xns = b.xns || Roo.bootstrap;
4738 b.xtype = b.xtype || 'Button';
4739 if (typeof(b.listeners) == 'undefined') {
4740 b.listeners = { click : cb.createDelegate(this) };
4743 var btn = Roo.factory(b);
4745 btn.render(this.getButtonContainer());
4751 setDefaultButton : function(btn)
4753 //this.el.select('.modal-footer').()
4756 resizeTo: function(w,h)
4758 this.dialogEl.setWidth(w);
4760 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4762 this.bodyEl.setHeight(h - diff);
4764 this.fireEvent('resize', this);
4767 setContentSize : function(w, h)
4771 onButtonClick: function(btn,e)
4774 this.fireEvent('btnclick', btn.name, e);
4777 * Set the title of the Dialog
4778 * @param {String} str new Title
4780 setTitle: function(str) {
4781 this.titleEl.dom.innerHTML = str;
4785 * Set the body of the Dialog
4786 * @param {String} str new Title
4788 setBody: function(str) {
4789 this.bodyEl.dom.innerHTML = str;
4792 * Set the body of the Dialog using the template
4793 * @param {Obj} data - apply this data to the template and replace the body contents.
4795 applyBody: function(obj)
4798 Roo.log("Error - using apply Body without a template");
4801 this.tmpl.overwrite(this.bodyEl, obj);
4804 getChildHeight : function(child_nodes)
4808 child_nodes.length == 0
4813 var child_height = 0;
4815 for(var i = 0; i < child_nodes.length; i++) {
4818 * for modal with tabs...
4819 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4821 var layout_childs = child_nodes[i].childNodes;
4823 for(var j = 0; j < layout_childs.length; j++) {
4825 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4827 var layout_body_childs = layout_childs[j].childNodes;
4829 for(var k = 0; k < layout_body_childs.length; k++) {
4831 if(layout_body_childs[k].classList.contains('navbar')) {
4832 child_height += layout_body_childs[k].offsetHeight;
4836 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4838 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4840 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4842 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4843 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4858 child_height += child_nodes[i].offsetHeight;
4859 // Roo.log(child_nodes[i].offsetHeight);
4862 return child_height;
4864 toggleHeaderInput : function(is_edit)
4866 if (!this.editableTitle) {
4867 return; // not editable.
4869 if (is_edit && this.is_header_editing) {
4870 return; // already editing..
4874 this.headerEditEl.dom.value = this.title;
4875 this.headerEditEl.removeClass('d-none');
4876 this.headerEditEl.dom.focus();
4877 this.titleEl.addClass('d-none');
4879 this.is_header_editing = true;
4882 // flip back to not editing.
4883 this.title = this.headerEditEl.dom.value;
4884 this.headerEditEl.addClass('d-none');
4885 this.titleEl.removeClass('d-none');
4886 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4887 this.is_header_editing = false;
4888 this.fireEvent('titlechanged', this, this.title);
4897 Roo.apply(Roo.bootstrap.Modal, {
4899 * Button config that displays a single OK button
4908 * Button config that displays Yes and No buttons
4924 * Button config that displays OK and Cancel buttons
4939 * Button config that displays Yes, No and Cancel buttons
4964 * messagebox - can be used as a replace
4968 * @class Roo.MessageBox
4969 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4973 Roo.Msg.alert('Status', 'Changes saved successfully.');
4975 // Prompt for user data:
4976 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4978 // process text value...
4982 // Show a dialog using config options:
4984 title:'Save Changes?',
4985 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4986 buttons: Roo.Msg.YESNOCANCEL,
4993 Roo.bootstrap.MessageBox = function(){
4994 var dlg, opt, mask, waitTimer;
4995 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4996 var buttons, activeTextEl, bwidth;
5000 var handleButton = function(button){
5002 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5006 var handleHide = function(){
5008 dlg.el.removeClass(opt.cls);
5011 // Roo.TaskMgr.stop(waitTimer);
5012 // waitTimer = null;
5017 var updateButtons = function(b){
5020 buttons["ok"].hide();
5021 buttons["cancel"].hide();
5022 buttons["yes"].hide();
5023 buttons["no"].hide();
5024 dlg.footerEl.hide();
5028 dlg.footerEl.show();
5029 for(var k in buttons){
5030 if(typeof buttons[k] != "function"){
5033 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5034 width += buttons[k].el.getWidth()+15;
5044 var handleEsc = function(d, k, e){
5045 if(opt && opt.closable !== false){
5055 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5056 * @return {Roo.BasicDialog} The BasicDialog element
5058 getDialog : function(){
5060 dlg = new Roo.bootstrap.Modal( {
5063 //constraintoviewport:false,
5065 //collapsible : false,
5070 //buttonAlign:"center",
5071 closeClick : function(){
5072 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5075 handleButton("cancel");
5080 dlg.on("hide", handleHide);
5082 //dlg.addKeyListener(27, handleEsc);
5084 this.buttons = buttons;
5085 var bt = this.buttonText;
5086 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5087 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5088 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5089 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5091 bodyEl = dlg.bodyEl.createChild({
5093 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5094 '<textarea class="roo-mb-textarea"></textarea>' +
5095 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5097 msgEl = bodyEl.dom.firstChild;
5098 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5099 textboxEl.enableDisplayMode();
5100 textboxEl.addKeyListener([10,13], function(){
5101 if(dlg.isVisible() && opt && opt.buttons){
5104 }else if(opt.buttons.yes){
5105 handleButton("yes");
5109 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5110 textareaEl.enableDisplayMode();
5111 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5112 progressEl.enableDisplayMode();
5114 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5115 var pf = progressEl.dom.firstChild;
5117 pp = Roo.get(pf.firstChild);
5118 pp.setHeight(pf.offsetHeight);
5126 * Updates the message box body text
5127 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5128 * the XHTML-compliant non-breaking space character '&#160;')
5129 * @return {Roo.MessageBox} This message box
5131 updateText : function(text)
5133 if(!dlg.isVisible() && !opt.width){
5134 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5135 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5137 msgEl.innerHTML = text || ' ';
5139 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5140 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5142 Math.min(opt.width || cw , this.maxWidth),
5143 Math.max(opt.minWidth || this.minWidth, bwidth)
5146 activeTextEl.setWidth(w);
5148 if(dlg.isVisible()){
5149 dlg.fixedcenter = false;
5151 // to big, make it scroll. = But as usual stupid IE does not support
5154 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5155 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5156 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5158 bodyEl.dom.style.height = '';
5159 bodyEl.dom.style.overflowY = '';
5162 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5164 bodyEl.dom.style.overflowX = '';
5167 dlg.setContentSize(w, bodyEl.getHeight());
5168 if(dlg.isVisible()){
5169 dlg.fixedcenter = true;
5175 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5176 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5177 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5178 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5179 * @return {Roo.MessageBox} This message box
5181 updateProgress : function(value, text){
5183 this.updateText(text);
5186 if (pp) { // weird bug on my firefox - for some reason this is not defined
5187 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5188 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5194 * Returns true if the message box is currently displayed
5195 * @return {Boolean} True if the message box is visible, else false
5197 isVisible : function(){
5198 return dlg && dlg.isVisible();
5202 * Hides the message box if it is displayed
5205 if(this.isVisible()){
5211 * Displays a new message box, or reinitializes an existing message box, based on the config options
5212 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5213 * The following config object properties are supported:
5215 Property Type Description
5216 ---------- --------------- ------------------------------------------------------------------------------------
5217 animEl String/Element An id or Element from which the message box should animate as it opens and
5218 closes (defaults to undefined)
5219 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5220 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5221 closable Boolean False to hide the top-right close button (defaults to true). Note that
5222 progress and wait dialogs will ignore this property and always hide the
5223 close button as they can only be closed programmatically.
5224 cls String A custom CSS class to apply to the message box element
5225 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5226 displayed (defaults to 75)
5227 fn Function A callback function to execute after closing the dialog. The arguments to the
5228 function will be btn (the name of the button that was clicked, if applicable,
5229 e.g. "ok"), and text (the value of the active text field, if applicable).
5230 Progress and wait dialogs will ignore this option since they do not respond to
5231 user actions and can only be closed programmatically, so any required function
5232 should be called by the same code after it closes the dialog.
5233 icon String A CSS class that provides a background image to be used as an icon for
5234 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5235 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5236 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5237 modal Boolean False to allow user interaction with the page while the message box is
5238 displayed (defaults to true)
5239 msg String A string that will replace the existing message box body text (defaults
5240 to the XHTML-compliant non-breaking space character ' ')
5241 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5242 progress Boolean True to display a progress bar (defaults to false)
5243 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5244 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5245 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5246 title String The title text
5247 value String The string value to set into the active textbox element if displayed
5248 wait Boolean True to display a progress bar (defaults to false)
5249 width Number The width of the dialog in pixels
5256 msg: 'Please enter your address:',
5258 buttons: Roo.MessageBox.OKCANCEL,
5261 animEl: 'addAddressBtn'
5264 * @param {Object} config Configuration options
5265 * @return {Roo.MessageBox} This message box
5267 show : function(options)
5270 // this causes nightmares if you show one dialog after another
5271 // especially on callbacks..
5273 if(this.isVisible()){
5276 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5277 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5278 Roo.log("New Dialog Message:" + options.msg )
5279 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5280 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5283 var d = this.getDialog();
5285 d.setTitle(opt.title || " ");
5286 d.closeEl.setDisplayed(opt.closable !== false);
5287 activeTextEl = textboxEl;
5288 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5293 textareaEl.setHeight(typeof opt.multiline == "number" ?
5294 opt.multiline : this.defaultTextHeight);
5295 activeTextEl = textareaEl;
5304 progressEl.setDisplayed(opt.progress === true);
5306 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5308 this.updateProgress(0);
5309 activeTextEl.dom.value = opt.value || "";
5311 dlg.setDefaultButton(activeTextEl);
5313 var bs = opt.buttons;
5317 }else if(bs && bs.yes){
5318 db = buttons["yes"];
5320 dlg.setDefaultButton(db);
5322 bwidth = updateButtons(opt.buttons);
5323 this.updateText(opt.msg);
5325 d.el.addClass(opt.cls);
5327 d.proxyDrag = opt.proxyDrag === true;
5328 d.modal = opt.modal !== false;
5329 d.mask = opt.modal !== false ? mask : false;
5331 // force it to the end of the z-index stack so it gets a cursor in FF
5332 document.body.appendChild(dlg.el.dom);
5333 d.animateTarget = null;
5334 d.show(options.animEl);
5340 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5341 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5342 * and closing the message box when the process is complete.
5343 * @param {String} title The title bar text
5344 * @param {String} msg The message box body text
5345 * @return {Roo.MessageBox} This message box
5347 progress : function(title, msg){
5354 minWidth: this.minProgressWidth,
5361 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5362 * If a callback function is passed it will be called after the user clicks the button, and the
5363 * id of the button that was clicked will be passed as the only parameter to the callback
5364 * (could also be the top-right close button).
5365 * @param {String} title The title bar text
5366 * @param {String} msg The message box body text
5367 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5368 * @param {Object} scope (optional) The scope of the callback function
5369 * @return {Roo.MessageBox} This message box
5371 alert : function(title, msg, fn, scope)
5386 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5387 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5388 * You are responsible for closing the message box when the process is complete.
5389 * @param {String} msg The message box body text
5390 * @param {String} title (optional) The title bar text
5391 * @return {Roo.MessageBox} This message box
5393 wait : function(msg, title){
5404 waitTimer = Roo.TaskMgr.start({
5406 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5414 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5415 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5416 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5417 * @param {String} title The title bar text
5418 * @param {String} msg The message box body text
5419 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5420 * @param {Object} scope (optional) The scope of the callback function
5421 * @return {Roo.MessageBox} This message box
5423 confirm : function(title, msg, fn, scope){
5427 buttons: this.YESNO,
5436 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5437 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5438 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5439 * (could also be the top-right close button) and the text that was entered will be passed as the two
5440 * parameters to the callback.
5441 * @param {String} title The title bar text
5442 * @param {String} msg The message box body text
5443 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5444 * @param {Object} scope (optional) The scope of the callback function
5445 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5446 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5447 * @return {Roo.MessageBox} This message box
5449 prompt : function(title, msg, fn, scope, multiline){
5453 buttons: this.OKCANCEL,
5458 multiline: multiline,
5465 * Button config that displays a single OK button
5470 * Button config that displays Yes and No buttons
5473 YESNO : {yes:true, no:true},
5475 * Button config that displays OK and Cancel buttons
5478 OKCANCEL : {ok:true, cancel:true},
5480 * Button config that displays Yes, No and Cancel buttons
5483 YESNOCANCEL : {yes:true, no:true, cancel:true},
5486 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5489 defaultTextHeight : 75,
5491 * The maximum width in pixels of the message box (defaults to 600)
5496 * The minimum width in pixels of the message box (defaults to 100)
5501 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5502 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5505 minProgressWidth : 250,
5507 * An object containing the default button text strings that can be overriden for localized language support.
5508 * Supported properties are: ok, cancel, yes and no.
5509 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5522 * Shorthand for {@link Roo.MessageBox}
5524 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5525 Roo.Msg = Roo.Msg || Roo.MessageBox;
5534 * @class Roo.bootstrap.nav.Bar
5535 * @extends Roo.bootstrap.Component
5537 * Bootstrap Navbar class
5540 * Create a new Navbar
5541 * @param {Object} config The config object
5545 Roo.bootstrap.nav.Bar = function(config){
5546 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5550 * @event beforetoggle
5551 * Fire before toggle the menu
5552 * @param {Roo.EventObject} e
5554 "beforetoggle" : true
5558 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5567 getAutoCreate : function(){
5570 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5574 initEvents :function ()
5576 //Roo.log(this.el.select('.navbar-toggle',true));
5577 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5584 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5586 var size = this.el.getSize();
5587 this.maskEl.setSize(size.width, size.height);
5588 this.maskEl.enableDisplayMode("block");
5597 getChildContainer : function()
5599 if (this.el && this.el.select('.collapse').getCount()) {
5600 return this.el.select('.collapse',true).first();
5615 onToggle : function()
5618 if(this.fireEvent('beforetoggle', this) === false){
5621 var ce = this.el.select('.navbar-collapse',true).first();
5623 if (!ce.hasClass('show')) {
5633 * Expand the navbar pulldown
5635 expand : function ()
5638 var ce = this.el.select('.navbar-collapse',true).first();
5639 if (ce.hasClass('collapsing')) {
5642 ce.dom.style.height = '';
5644 ce.addClass('in'); // old...
5645 ce.removeClass('collapse');
5646 ce.addClass('show');
5647 var h = ce.getHeight();
5649 ce.removeClass('show');
5650 // at this point we should be able to see it..
5651 ce.addClass('collapsing');
5653 ce.setHeight(0); // resize it ...
5654 ce.on('transitionend', function() {
5655 //Roo.log('done transition');
5656 ce.removeClass('collapsing');
5657 ce.addClass('show');
5658 ce.removeClass('collapse');
5660 ce.dom.style.height = '';
5661 }, this, { single: true} );
5663 ce.dom.scrollTop = 0;
5666 * Collapse the navbar pulldown
5668 collapse : function()
5670 var ce = this.el.select('.navbar-collapse',true).first();
5672 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5673 // it's collapsed or collapsing..
5676 ce.removeClass('in'); // old...
5677 ce.setHeight(ce.getHeight());
5678 ce.removeClass('show');
5679 ce.addClass('collapsing');
5681 ce.on('transitionend', function() {
5682 ce.dom.style.height = '';
5683 ce.removeClass('collapsing');
5684 ce.addClass('collapse');
5685 }, this, { single: true} );
5705 * @class Roo.bootstrap.nav.Simplebar
5706 * @extends Roo.bootstrap.nav.Bar
5707 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5708 * Bootstrap Sidebar class
5710 * @cfg {Boolean} inverse is inverted color
5712 * @cfg {String} type (nav | pills | tabs)
5713 * @cfg {Boolean} arrangement stacked | justified
5714 * @cfg {String} align (left | right) alignment
5716 * @cfg {Boolean} main (true|false) main nav bar? default false
5717 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5719 * @cfg {String} tag (header|footer|nav|div) default is nav
5721 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5725 * Create a new Sidebar
5726 * @param {Object} config The config object
5730 Roo.bootstrap.nav.Simplebar = function(config){
5731 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5734 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5750 getAutoCreate : function(){
5754 tag : this.tag || 'div',
5755 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5757 if (['light','white'].indexOf(this.weight) > -1) {
5758 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5760 cfg.cls += ' bg-' + this.weight;
5763 cfg.cls += ' navbar-inverse';
5767 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5769 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5778 cls: 'nav nav-' + this.xtype,
5784 this.type = this.type || 'nav';
5785 if (['tabs','pills'].indexOf(this.type) != -1) {
5786 cfg.cn[0].cls += ' nav-' + this.type
5790 if (this.type!=='nav') {
5791 Roo.log('nav type must be nav/tabs/pills')
5793 cfg.cn[0].cls += ' navbar-nav'
5799 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5800 cfg.cn[0].cls += ' nav-' + this.arrangement;
5804 if (this.align === 'right') {
5805 cfg.cn[0].cls += ' navbar-right';
5830 * navbar-expand-md fixed-top
5834 * @class Roo.bootstrap.nav.Headerbar
5835 * @extends Roo.bootstrap.nav.Simplebar
5836 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5837 * Bootstrap Sidebar class
5839 * @cfg {String} brand what is brand
5840 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5841 * @cfg {String} brand_href href of the brand
5842 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5843 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5844 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5845 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5848 * Create a new Sidebar
5849 * @param {Object} config The config object
5853 Roo.bootstrap.nav.Headerbar = function(config){
5854 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5858 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5865 desktopCenter : false,
5868 getAutoCreate : function(){
5871 tag: this.nav || 'nav',
5872 cls: 'navbar navbar-expand-md',
5878 if (this.desktopCenter) {
5879 cn.push({cls : 'container', cn : []});
5887 cls: 'navbar-toggle navbar-toggler',
5888 'data-toggle': 'collapse',
5893 html: 'Toggle navigation'
5897 cls: 'icon-bar navbar-toggler-icon'
5910 cn.push( Roo.bootstrap.version == 4 ? btn : {
5912 cls: 'navbar-header',
5921 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5925 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5927 if (['light','white'].indexOf(this.weight) > -1) {
5928 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5930 cfg.cls += ' bg-' + this.weight;
5933 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5934 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5936 // tag can override this..
5938 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5941 if (this.brand !== '') {
5942 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5943 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5945 href: this.brand_href ? this.brand_href : '#',
5946 cls: 'navbar-brand',
5954 cfg.cls += ' main-nav';
5962 getHeaderChildContainer : function()
5964 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5965 return this.el.select('.navbar-header',true).first();
5968 return this.getChildContainer();
5971 getChildContainer : function()
5974 return this.el.select('.roo-navbar-collapse',true).first();
5979 initEvents : function()
5981 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5983 if (this.autohide) {
5988 Roo.get(document).on('scroll',function(e) {
5989 var ns = Roo.get(document).getScroll().top;
5990 var os = prevScroll;
5994 ft.removeClass('slideDown');
5995 ft.addClass('slideUp');
5998 ft.removeClass('slideUp');
5999 ft.addClass('slideDown');
6020 * @class Roo.bootstrap.nav.Sidebar
6021 * @extends Roo.bootstrap.nav.Bar
6022 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6023 * Bootstrap Sidebar class
6026 * Create a new Sidebar
6027 * @param {Object} config The config object
6031 Roo.bootstrap.nav.Sidebar = function(config){
6032 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6035 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6037 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6039 getAutoCreate : function(){
6044 cls: 'sidebar sidebar-nav'
6066 * @class Roo.bootstrap.nav.Group
6067 * @extends Roo.bootstrap.Component
6068 * @children Roo.bootstrap.nav.Item
6069 * Bootstrap NavGroup class
6070 * @cfg {String} align (left|right)
6071 * @cfg {Boolean} inverse
6072 * @cfg {String} type (nav|pills|tab) default nav
6073 * @cfg {String} navId - reference Id for navbar.
6074 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6077 * Create a new nav group
6078 * @param {Object} config The config object
6081 Roo.bootstrap.nav.Group = function(config){
6082 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6085 Roo.bootstrap.nav.Group.register(this);
6089 * Fires when the active item changes
6090 * @param {Roo.bootstrap.nav.Group} this
6091 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6092 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6099 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6111 getAutoCreate : function()
6113 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6119 if (Roo.bootstrap.version == 4) {
6120 if (['tabs','pills'].indexOf(this.type) != -1) {
6121 cfg.cls += ' nav-' + this.type;
6123 // trying to remove so header bar can right align top?
6124 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6125 // do not use on header bar...
6126 cfg.cls += ' navbar-nav';
6131 if (['tabs','pills'].indexOf(this.type) != -1) {
6132 cfg.cls += ' nav-' + this.type
6134 if (this.type !== 'nav') {
6135 Roo.log('nav type must be nav/tabs/pills')
6137 cfg.cls += ' navbar-nav'
6141 if (this.parent() && this.parent().sidebar) {
6144 cls: 'dashboard-menu sidebar-menu'
6150 if (this.form === true) {
6153 cls: 'navbar-form form-inline'
6155 //nav navbar-right ml-md-auto
6156 if (this.align === 'right') {
6157 cfg.cls += ' navbar-right ml-md-auto';
6159 cfg.cls += ' navbar-left';
6163 if (this.align === 'right') {
6164 cfg.cls += ' navbar-right ml-md-auto';
6166 cfg.cls += ' mr-auto';
6170 cfg.cls += ' navbar-inverse';
6178 * sets the active Navigation item
6179 * @param {Roo.bootstrap.nav.Item} the new current navitem
6181 setActiveItem : function(item)
6184 Roo.each(this.navItems, function(v){
6189 v.setActive(false, true);
6196 item.setActive(true, true);
6197 this.fireEvent('changed', this, item, prev);
6202 * gets the active Navigation item
6203 * @return {Roo.bootstrap.nav.Item} the current navitem
6205 getActive : function()
6209 Roo.each(this.navItems, function(v){
6220 indexOfNav : function()
6224 Roo.each(this.navItems, function(v,i){
6235 * adds a Navigation item
6236 * @param {Roo.bootstrap.nav.Item} the navitem to add
6238 addItem : function(cfg)
6240 if (this.form && Roo.bootstrap.version == 4) {
6243 var cn = new Roo.bootstrap.nav.Item(cfg);
6245 cn.parentId = this.id;
6246 cn.onRender(this.el, null);
6250 * register a Navigation item
6251 * @param {Roo.bootstrap.nav.Item} the navitem to add
6253 register : function(item)
6255 this.navItems.push( item);
6256 item.navId = this.navId;
6261 * clear all the Navigation item
6264 clearAll : function()
6267 this.el.dom.innerHTML = '';
6270 getNavItem: function(tabId)
6273 Roo.each(this.navItems, function(e) {
6274 if (e.tabId == tabId) {
6284 setActiveNext : function()
6286 var i = this.indexOfNav(this.getActive());
6287 if (i > this.navItems.length) {
6290 this.setActiveItem(this.navItems[i+1]);
6292 setActivePrev : function()
6294 var i = this.indexOfNav(this.getActive());
6298 this.setActiveItem(this.navItems[i-1]);
6300 clearWasActive : function(except) {
6301 Roo.each(this.navItems, function(e) {
6302 if (e.tabId != except.tabId && e.was_active) {
6303 e.was_active = false;
6310 getWasActive : function ()
6313 Roo.each(this.navItems, function(e) {
6328 Roo.apply(Roo.bootstrap.nav.Group, {
6332 * register a Navigation Group
6333 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6335 register : function(navgrp)
6337 this.groups[navgrp.navId] = navgrp;
6341 * fetch a Navigation Group based on the navigation ID
6342 * @param {string} the navgroup to add
6343 * @returns {Roo.bootstrap.nav.Group} the navgroup
6345 get: function(navId) {
6346 if (typeof(this.groups[navId]) == 'undefined') {
6348 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6350 return this.groups[navId] ;
6358 * @class Roo.bootstrap.nav.Item
6359 * @extends Roo.bootstrap.Component
6360 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6361 * @parent Roo.bootstrap.nav.Group
6363 * Bootstrap Navbar.NavItem class
6365 * @cfg {String} href link to
6366 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6367 * @cfg {Boolean} button_outline show and outlined button
6368 * @cfg {String} html content of button
6369 * @cfg {String} badge text inside badge
6370 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6371 * @cfg {String} glyphicon DEPRICATED - use fa
6372 * @cfg {String} icon DEPRICATED - use fa
6373 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6374 * @cfg {Boolean} active Is item active
6375 * @cfg {Boolean} disabled Is item disabled
6376 * @cfg {String} linkcls Link Class
6377 * @cfg {Boolean} preventDefault (true | false) default false
6378 * @cfg {String} tabId the tab that this item activates.
6379 * @cfg {String} tagtype (a|span) render as a href or span?
6380 * @cfg {Boolean} animateRef (true|false) link to element default false
6381 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6384 * Create a new Navbar Item
6385 * @param {Object} config The config object
6387 Roo.bootstrap.nav.Item = function(config){
6388 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6393 * The raw click event for the entire grid.
6394 * @param {Roo.EventObject} e
6399 * Fires when the active item active state changes
6400 * @param {Roo.bootstrap.nav.Item} this
6401 * @param {boolean} state the new state
6407 * Fires when scroll to element
6408 * @param {Roo.bootstrap.nav.Item} this
6409 * @param {Object} options
6410 * @param {Roo.EventObject} e
6418 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6427 preventDefault : false,
6435 button_outline : false,
6439 getAutoCreate : function(){
6446 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6449 cfg.cls += ' active' ;
6451 if (this.disabled) {
6452 cfg.cls += ' disabled';
6456 if (this.button_weight.length) {
6457 cfg.tag = this.href ? 'a' : 'button';
6458 cfg.html = this.html || '';
6459 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6461 cfg.href = this.href;
6464 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6466 cfg.cls += " nav-html";
6469 // menu .. should add dropdown-menu class - so no need for carat..
6471 if (this.badge !== '') {
6473 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6478 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6482 href : this.href || "#",
6483 html: this.html || '',
6487 if (this.tagtype == 'a') {
6488 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6492 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6493 } else if (this.fa) {
6494 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6495 } else if(this.glyphicon) {
6496 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6498 cfg.cn[0].cls += " nav-html";
6502 cfg.cn[0].html += " <span class='caret'></span>";
6506 if (this.badge !== '') {
6507 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6515 onRender : function(ct, position)
6517 // Roo.log("Call onRender: " + this.xtype);
6518 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6522 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6523 this.navLink = this.el.select('.nav-link',true).first();
6524 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6529 initEvents: function()
6531 if (typeof (this.menu) != 'undefined') {
6532 this.menu.parentType = this.xtype;
6533 this.menu.triggerEl = this.el;
6534 this.menu = this.addxtype(Roo.apply({}, this.menu));
6537 this.el.on('click', this.onClick, this);
6539 //if(this.tagtype == 'span'){
6540 // this.el.select('span',true).on('click', this.onClick, this);
6543 // at this point parent should be available..
6544 this.parent().register(this);
6547 onClick : function(e)
6549 if (e.getTarget('.dropdown-menu-item')) {
6550 // did you click on a menu itemm.... - then don't trigger onclick..
6555 this.preventDefault ||
6556 this.href === false ||
6559 //Roo.log("NavItem - prevent Default?");
6563 if (this.disabled) {
6567 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6568 if (tg && tg.transition) {
6569 Roo.log("waiting for the transitionend");
6575 //Roo.log("fire event clicked");
6576 if(this.fireEvent('click', this, e) === false){
6580 if(this.tagtype == 'span'){
6584 //Roo.log(this.href);
6585 var ael = this.el.select('a',true).first();
6588 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6589 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6590 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6591 return; // ignore... - it's a 'hash' to another page.
6593 Roo.log("NavItem - prevent Default?");
6595 this.scrollToElement(e);
6599 var p = this.parent();
6601 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6602 if (typeof(p.setActiveItem) !== 'undefined') {
6603 p.setActiveItem(this);
6607 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6608 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6609 // remove the collapsed menu expand...
6610 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6614 isActive: function () {
6617 setActive : function(state, fire, is_was_active)
6619 if (this.active && !state && this.navId) {
6620 this.was_active = true;
6621 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6623 nv.clearWasActive(this);
6627 this.active = state;
6630 this.el.removeClass('active');
6631 this.navLink ? this.navLink.removeClass('active') : false;
6632 } else if (!this.el.hasClass('active')) {
6634 this.el.addClass('active');
6635 if (Roo.bootstrap.version == 4 && this.navLink ) {
6636 this.navLink.addClass('active');
6641 this.fireEvent('changed', this, state);
6644 // show a panel if it's registered and related..
6646 if (!this.navId || !this.tabId || !state || is_was_active) {
6650 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6654 var pan = tg.getPanelByName(this.tabId);
6658 // if we can not flip to new panel - go back to old nav highlight..
6659 if (false == tg.showPanel(pan)) {
6660 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6662 var onav = nv.getWasActive();
6664 onav.setActive(true, false, true);
6673 // this should not be here...
6674 setDisabled : function(state)
6676 this.disabled = state;
6678 this.el.removeClass('disabled');
6679 } else if (!this.el.hasClass('disabled')) {
6680 this.el.addClass('disabled');
6686 * Fetch the element to display the tooltip on.
6687 * @return {Roo.Element} defaults to this.el
6689 tooltipEl : function()
6691 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6694 scrollToElement : function(e)
6696 var c = document.body;
6699 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6701 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6702 c = document.documentElement;
6705 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6711 var o = target.calcOffsetsTo(c);
6718 this.fireEvent('scrollto', this, options, e);
6720 Roo.get(c).scrollTo('top', options.value, true);
6725 * Set the HTML (text content) of the item
6726 * @param {string} html content for the nav item
6728 setHtml : function(html)
6731 this.htmlEl.dom.innerHTML = html;
6743 * <span> icon </span>
6744 * <span> text </span>
6745 * <span>badge </span>
6749 * @class Roo.bootstrap.nav.SidebarItem
6750 * @extends Roo.bootstrap.nav.Item
6751 * Bootstrap Navbar.NavSidebarItem class
6753 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6754 * {Boolean} open is the menu open
6755 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6756 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6757 * {String} buttonSize (sm|md|lg)the extra classes for the button
6758 * {Boolean} showArrow show arrow next to the text (default true)
6760 * Create a new Navbar Button
6761 * @param {Object} config The config object
6763 Roo.bootstrap.nav.SidebarItem = function(config){
6764 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6769 * The raw click event for the entire grid.
6770 * @param {Roo.EventObject} e
6775 * Fires when the active item active state changes
6776 * @param {Roo.bootstrap.nav.SidebarItem} this
6777 * @param {boolean} state the new state
6785 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6787 badgeWeight : 'default',
6793 buttonWeight : 'default',
6799 getAutoCreate : function(){
6804 href : this.href || '#',
6810 if(this.buttonView){
6813 href : this.href || '#',
6814 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6827 cfg.cls += ' active';
6830 if (this.disabled) {
6831 cfg.cls += ' disabled';
6834 cfg.cls += ' open x-open';
6837 if (this.glyphicon || this.icon) {
6838 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6839 a.cn.push({ tag : 'i', cls : c }) ;
6842 if(!this.buttonView){
6845 html : this.html || ''
6852 if (this.badge !== '') {
6853 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6859 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6862 a.cls += ' dropdown-toggle treeview' ;
6868 initEvents : function()
6870 if (typeof (this.menu) != 'undefined') {
6871 this.menu.parentType = this.xtype;
6872 this.menu.triggerEl = this.el;
6873 this.menu = this.addxtype(Roo.apply({}, this.menu));
6876 this.el.on('click', this.onClick, this);
6878 if(this.badge !== ''){
6879 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6884 onClick : function(e)
6891 if(this.preventDefault){
6895 this.fireEvent('click', this, e);
6898 disable : function()
6900 this.setDisabled(true);
6905 this.setDisabled(false);
6908 setDisabled : function(state)
6910 if(this.disabled == state){
6914 this.disabled = state;
6917 this.el.addClass('disabled');
6921 this.el.removeClass('disabled');
6926 setActive : function(state)
6928 if(this.active == state){
6932 this.active = state;
6935 this.el.addClass('active');
6939 this.el.removeClass('active');
6944 isActive: function ()
6949 setBadge : function(str)
6955 this.badgeEl.dom.innerHTML = str;
6972 * @class Roo.bootstrap.nav.ProgressBar
6973 * @extends Roo.bootstrap.Component
6974 * @children Roo.bootstrap.nav.ProgressBarItem
6975 * Bootstrap NavProgressBar class
6978 * Create a new nav progress bar - a bar indicating step along a process
6979 * @param {Object} config The config object
6982 Roo.bootstrap.nav.ProgressBar = function(config){
6983 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6985 this.bullets = this.bullets || [];
6987 // Roo.bootstrap.nav.ProgressBar.register(this);
6991 * Fires when the active item changes
6992 * @param {Roo.bootstrap.nav.ProgressBar} this
6993 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6994 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7001 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7003 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7004 * Bullets for the Nav Progress bar for the toolbar
7009 getAutoCreate : function()
7011 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7015 cls : 'roo-navigation-bar-group',
7019 cls : 'roo-navigation-top-bar'
7023 cls : 'roo-navigation-bullets-bar',
7027 cls : 'roo-navigation-bar'
7034 cls : 'roo-navigation-bottom-bar'
7044 initEvents: function()
7049 onRender : function(ct, position)
7051 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7053 if(this.bullets.length){
7054 Roo.each(this.bullets, function(b){
7063 addItem : function(cfg)
7065 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7067 item.parentId = this.id;
7068 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7071 var top = new Roo.bootstrap.Element({
7073 cls : 'roo-navigation-bar-text'
7076 var bottom = new Roo.bootstrap.Element({
7078 cls : 'roo-navigation-bar-text'
7081 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7082 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7084 var topText = new Roo.bootstrap.Element({
7086 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7089 var bottomText = new Roo.bootstrap.Element({
7091 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7094 topText.onRender(top.el, null);
7095 bottomText.onRender(bottom.el, null);
7098 item.bottomEl = bottom;
7101 this.barItems.push(item);
7106 getActive : function()
7110 Roo.each(this.barItems, function(v){
7112 if (!v.isActive()) {
7124 setActiveItem : function(item)
7128 Roo.each(this.barItems, function(v){
7129 if (v.rid == item.rid) {
7139 item.setActive(true);
7141 this.fireEvent('changed', this, item, prev);
7144 getBarItem: function(rid)
7148 Roo.each(this.barItems, function(e) {
7160 indexOfItem : function(item)
7164 Roo.each(this.barItems, function(v, i){
7166 if (v.rid != item.rid) {
7177 setActiveNext : function()
7179 var i = this.indexOfItem(this.getActive());
7181 if (i > this.barItems.length) {
7185 this.setActiveItem(this.barItems[i+1]);
7188 setActivePrev : function()
7190 var i = this.indexOfItem(this.getActive());
7196 this.setActiveItem(this.barItems[i-1]);
7201 if(!this.barItems.length){
7205 var width = 100 / this.barItems.length;
7207 Roo.each(this.barItems, function(i){
7208 i.el.setStyle('width', width + '%');
7209 i.topEl.el.setStyle('width', width + '%');
7210 i.bottomEl.el.setStyle('width', width + '%');
7224 * @class Roo.bootstrap.nav.ProgressBarItem
7225 * @extends Roo.bootstrap.Component
7226 * Bootstrap NavProgressBarItem class
7227 * @cfg {String} rid the reference id
7228 * @cfg {Boolean} active (true|false) Is item active default false
7229 * @cfg {Boolean} disabled (true|false) Is item active default false
7230 * @cfg {String} html
7231 * @cfg {String} position (top|bottom) text position default bottom
7232 * @cfg {String} icon show icon instead of number
7235 * Create a new NavProgressBarItem
7236 * @param {Object} config The config object
7238 Roo.bootstrap.nav.ProgressBarItem = function(config){
7239 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7244 * The raw click event for the entire grid.
7245 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7246 * @param {Roo.EventObject} e
7253 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7259 position : 'bottom',
7262 getAutoCreate : function()
7264 var iconCls = 'roo-navigation-bar-item-icon';
7266 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7270 cls: 'roo-navigation-bar-item',
7280 cfg.cls += ' active';
7283 cfg.cls += ' disabled';
7289 disable : function()
7291 this.setDisabled(true);
7296 this.setDisabled(false);
7299 initEvents: function()
7301 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7303 this.iconEl.on('click', this.onClick, this);
7306 onClick : function(e)
7314 if(this.fireEvent('click', this, e) === false){
7318 this.parent().setActiveItem(this);
7321 isActive: function ()
7326 setActive : function(state)
7328 if(this.active == state){
7332 this.active = state;
7335 this.el.addClass('active');
7339 this.el.removeClass('active');
7344 setDisabled : function(state)
7346 if(this.disabled == state){
7350 this.disabled = state;
7353 this.el.addClass('disabled');
7357 this.el.removeClass('disabled');
7360 tooltipEl : function()
7362 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7373 Roo.namespace('Roo.bootstrap.breadcrumb');
7377 * @class Roo.bootstrap.breadcrumb.Nav
7378 * @extends Roo.bootstrap.Component
7379 * Bootstrap Breadcrumb Nav Class
7381 * @children Roo.bootstrap.breadcrumb.Item
7384 * Create a new breadcrumb.Nav
7385 * @param {Object} config The config object
7389 Roo.bootstrap.breadcrumb.Nav = function(config){
7390 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7395 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7397 getAutoCreate : function()
7414 initEvents: function()
7416 this.olEl = this.el.select('ol',true).first();
7418 getChildContainer : function()
7434 * @class Roo.bootstrap.breadcrumb.Nav
7435 * @extends Roo.bootstrap.Component
7436 * @children Roo.bootstrap.Component
7437 * @parent Roo.bootstrap.breadcrumb.Nav
7438 * Bootstrap Breadcrumb Nav Class
7441 * @cfg {String} html the content of the link.
7442 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7443 * @cfg {Boolean} active is it active
7447 * Create a new breadcrumb.Nav
7448 * @param {Object} config The config object
7451 Roo.bootstrap.breadcrumb.Item = function(config){
7452 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7457 * The img click event for the img.
7458 * @param {Roo.EventObject} e
7465 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7470 getAutoCreate : function()
7475 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7477 if (this.href !== false) {
7484 cfg.html = this.html;
7490 initEvents: function()
7493 this.el.select('a', true).first().on('click',this.onClick, this)
7497 onClick : function(e)
7500 this.fireEvent('click',this, e);
7513 * @class Roo.bootstrap.Row
7514 * @extends Roo.bootstrap.Component
7515 * @children Roo.bootstrap.Component
7516 * Bootstrap Row class (contains columns...)
7520 * @param {Object} config The config object
7523 Roo.bootstrap.Row = function(config){
7524 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7527 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7529 getAutoCreate : function(){
7548 * @class Roo.bootstrap.Pagination
7549 * @extends Roo.bootstrap.Component
7550 * @children Roo.bootstrap.Pagination
7551 * Bootstrap Pagination class
7553 * @cfg {String} size (xs|sm|md|lg|xl)
7554 * @cfg {Boolean} inverse
7557 * Create a new Pagination
7558 * @param {Object} config The config object
7561 Roo.bootstrap.Pagination = function(config){
7562 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7565 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7571 getAutoCreate : function(){
7577 cfg.cls += ' inverse';
7583 cfg.cls += " " + this.cls;
7601 * @class Roo.bootstrap.PaginationItem
7602 * @extends Roo.bootstrap.Component
7603 * Bootstrap PaginationItem class
7604 * @cfg {String} html text
7605 * @cfg {String} href the link
7606 * @cfg {Boolean} preventDefault (true | false) default true
7607 * @cfg {Boolean} active (true | false) default false
7608 * @cfg {Boolean} disabled default false
7612 * Create a new PaginationItem
7613 * @param {Object} config The config object
7617 Roo.bootstrap.PaginationItem = function(config){
7618 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7623 * The raw click event for the entire grid.
7624 * @param {Roo.EventObject} e
7630 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7634 preventDefault: true,
7639 getAutoCreate : function(){
7645 href : this.href ? this.href : '#',
7646 html : this.html ? this.html : ''
7656 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7660 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7666 initEvents: function() {
7668 this.el.on('click', this.onClick, this);
7671 onClick : function(e)
7673 Roo.log('PaginationItem on click ');
7674 if(this.preventDefault){
7682 this.fireEvent('click', this, e);
7698 * @class Roo.bootstrap.Slider
7699 * @extends Roo.bootstrap.Component
7700 * Bootstrap Slider class
7703 * Create a new Slider
7704 * @param {Object} config The config object
7707 Roo.bootstrap.Slider = function(config){
7708 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7711 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7713 getAutoCreate : function(){
7717 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7721 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7733 * Ext JS Library 1.1.1
7734 * Copyright(c) 2006-2007, Ext JS, LLC.
7736 * Originally Released Under LGPL - original licence link has changed is not relivant.
7739 * <script type="text/javascript">
7742 * @extends Roo.dd.DDProxy
7743 * @class Roo.grid.SplitDragZone
7744 * Support for Column Header resizing
7746 * @param {Object} config
7749 // This is a support class used internally by the Grid components
7750 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7752 this.view = grid.getView();
7753 this.proxy = this.view.resizeProxy;
7754 Roo.grid.SplitDragZone.superclass.constructor.call(
7757 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7759 dragElId : Roo.id(this.proxy.dom),
7764 this.setHandleElId(Roo.id(hd));
7765 if (hd2 !== false) {
7766 this.setOuterHandleElId(Roo.id(hd2));
7769 this.scroll = false;
7771 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7772 fly: Roo.Element.fly,
7774 b4StartDrag : function(x, y){
7775 this.view.headersDisabled = true;
7776 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7777 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7779 this.proxy.setHeight(h);
7781 // for old system colWidth really stored the actual width?
7782 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7783 // which in reality did not work.. - it worked only for fixed sizes
7784 // for resizable we need to use actual sizes.
7785 var w = this.cm.getColumnWidth(this.cellIndex);
7786 if (!this.view.mainWrap) {
7788 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7793 // this was w-this.grid.minColumnWidth;
7794 // doesnt really make sense? - w = thie curren width or the rendered one?
7795 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7796 this.resetConstraints();
7797 this.setXConstraint(minw, 1000);
7798 this.setYConstraint(0, 0);
7799 this.minX = x - minw;
7800 this.maxX = x + 1000;
7802 if (!this.view.mainWrap) { // this is Bootstrap code..
7803 this.getDragEl().style.display='block';
7806 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7810 handleMouseDown : function(e){
7811 ev = Roo.EventObject.setEvent(e);
7812 var t = this.fly(ev.getTarget());
7813 if(t.hasClass("x-grid-split")){
7814 this.cellIndex = this.view.getCellIndex(t.dom);
7816 this.cm = this.grid.colModel;
7817 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7818 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7823 endDrag : function(e){
7824 this.view.headersDisabled = false;
7825 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7826 var diff = endX - this.startPos;
7828 var w = this.cm.getColumnWidth(this.cellIndex);
7829 if (!this.view.mainWrap) {
7832 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7835 autoOffset : function(){
7840 * Ext JS Library 1.1.1
7841 * Copyright(c) 2006-2007, Ext JS, LLC.
7843 * Originally Released Under LGPL - original licence link has changed is not relivant.
7846 * <script type="text/javascript">
7850 * @class Roo.grid.AbstractSelectionModel
7851 * @extends Roo.util.Observable
7853 * Abstract base class for grid SelectionModels. It provides the interface that should be
7854 * implemented by descendant classes. This class should not be directly instantiated.
7857 Roo.grid.AbstractSelectionModel = function(){
7858 this.locked = false;
7859 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7862 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7863 /** @ignore Called by the grid automatically. Do not call directly. */
7864 init : function(grid){
7870 * Locks the selections.
7877 * Unlocks the selections.
7879 unlock : function(){
7880 this.locked = false;
7884 * Returns true if the selections are locked.
7887 isLocked : function(){
7892 * Ext JS Library 1.1.1
7893 * Copyright(c) 2006-2007, Ext JS, LLC.
7895 * Originally Released Under LGPL - original licence link has changed is not relivant.
7898 * <script type="text/javascript">
7901 * @extends Roo.grid.AbstractSelectionModel
7902 * @class Roo.grid.RowSelectionModel
7903 * The default SelectionModel used by {@link Roo.grid.Grid}.
7904 * It supports multiple selections and keyboard selection/navigation.
7906 * @param {Object} config
7908 Roo.grid.RowSelectionModel = function(config){
7909 Roo.apply(this, config);
7910 this.selections = new Roo.util.MixedCollection(false, function(o){
7915 this.lastActive = false;
7919 * @event selectionchange
7920 * Fires when the selection changes
7921 * @param {SelectionModel} this
7923 "selectionchange" : true,
7925 * @event afterselectionchange
7926 * Fires after the selection changes (eg. by key press or clicking)
7927 * @param {SelectionModel} this
7929 "afterselectionchange" : true,
7931 * @event beforerowselect
7932 * Fires when a row is selected being selected, return false to cancel.
7933 * @param {SelectionModel} this
7934 * @param {Number} rowIndex The selected index
7935 * @param {Boolean} keepExisting False if other selections will be cleared
7937 "beforerowselect" : true,
7940 * Fires when a row is selected.
7941 * @param {SelectionModel} this
7942 * @param {Number} rowIndex The selected index
7943 * @param {Roo.data.Record} r The record
7947 * @event rowdeselect
7948 * Fires when a row is deselected.
7949 * @param {SelectionModel} this
7950 * @param {Number} rowIndex The selected index
7952 "rowdeselect" : true
7954 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7955 this.locked = false;
7958 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7960 * @cfg {Boolean} singleSelect
7961 * True to allow selection of only one row at a time (defaults to false)
7963 singleSelect : false,
7966 initEvents : function(){
7968 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7969 this.grid.on("mousedown", this.handleMouseDown, this);
7970 }else{ // allow click to work like normal
7971 this.grid.on("rowclick", this.handleDragableRowClick, this);
7973 // bootstrap does not have a view..
7974 var view = this.grid.view ? this.grid.view : this.grid;
7975 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7978 this.selectPrevious(e.shiftKey);
7979 }else if(this.last !== false && this.lastActive !== false){
7980 var last = this.last;
7981 this.selectRange(this.last, this.lastActive-1);
7982 view.focusRow(this.lastActive);
7987 this.selectFirstRow();
7989 this.fireEvent("afterselectionchange", this);
7991 "down" : function(e){
7993 this.selectNext(e.shiftKey);
7994 }else if(this.last !== false && this.lastActive !== false){
7995 var last = this.last;
7996 this.selectRange(this.last, this.lastActive+1);
7997 view.focusRow(this.lastActive);
8002 this.selectFirstRow();
8004 this.fireEvent("afterselectionchange", this);
8010 view.on("refresh", this.onRefresh, this);
8011 view.on("rowupdated", this.onRowUpdated, this);
8012 view.on("rowremoved", this.onRemove, this);
8016 onRefresh : function(){
8017 var ds = this.grid.ds, i, v = this.grid.view;
8018 var s = this.selections;
8020 if((i = ds.indexOfId(r.id)) != -1){
8022 s.add(ds.getAt(i)); // updating the selection relate data
8030 onRemove : function(v, index, r){
8031 this.selections.remove(r);
8035 onRowUpdated : function(v, index, r){
8036 if(this.isSelected(r)){
8037 v.onRowSelect(index);
8043 * @param {Array} records The records to select
8044 * @param {Boolean} keepExisting (optional) True to keep existing selections
8046 selectRecords : function(records, keepExisting){
8048 this.clearSelections();
8050 var ds = this.grid.ds;
8051 for(var i = 0, len = records.length; i < len; i++){
8052 this.selectRow(ds.indexOf(records[i]), true);
8057 * Gets the number of selected rows.
8060 getCount : function(){
8061 return this.selections.length;
8065 * Selects the first row in the grid.
8067 selectFirstRow : function(){
8072 * Select the last row.
8073 * @param {Boolean} keepExisting (optional) True to keep existing selections
8075 selectLastRow : function(keepExisting){
8076 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8080 * Selects the row immediately following the last selected row.
8081 * @param {Boolean} keepExisting (optional) True to keep existing selections
8083 selectNext : function(keepExisting){
8084 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8085 this.selectRow(this.last+1, keepExisting);
8086 var view = this.grid.view ? this.grid.view : this.grid;
8087 view.focusRow(this.last);
8092 * Selects the row that precedes the last selected row.
8093 * @param {Boolean} keepExisting (optional) True to keep existing selections
8095 selectPrevious : function(keepExisting){
8097 this.selectRow(this.last-1, keepExisting);
8098 var view = this.grid.view ? this.grid.view : this.grid;
8099 view.focusRow(this.last);
8104 * Returns the selected records
8105 * @return {Array} Array of selected records
8107 getSelections : function(){
8108 return [].concat(this.selections.items);
8112 * Returns the first selected record.
8115 getSelected : function(){
8116 return this.selections.itemAt(0);
8121 * Clears all selections.
8123 clearSelections : function(fast){
8128 var ds = this.grid.ds;
8129 var s = this.selections;
8131 this.deselectRow(ds.indexOfId(r.id));
8135 this.selections.clear();
8144 selectAll : function(){
8148 this.selections.clear();
8149 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8150 this.selectRow(i, true);
8155 * Returns True if there is a selection.
8158 hasSelection : function(){
8159 return this.selections.length > 0;
8163 * Returns True if the specified row is selected.
8164 * @param {Number/Record} record The record or index of the record to check
8167 isSelected : function(index){
8168 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8169 return (r && this.selections.key(r.id) ? true : false);
8173 * Returns True if the specified record id is selected.
8174 * @param {String} id The id of record to check
8177 isIdSelected : function(id){
8178 return (this.selections.key(id) ? true : false);
8182 handleMouseDown : function(e, t)
8184 var view = this.grid.view ? this.grid.view : this.grid;
8186 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8189 if(e.shiftKey && this.last !== false){
8190 var last = this.last;
8191 this.selectRange(last, rowIndex, e.ctrlKey);
8192 this.last = last; // reset the last
8193 view.focusRow(rowIndex);
8195 var isSelected = this.isSelected(rowIndex);
8196 if(e.button !== 0 && isSelected){
8197 view.focusRow(rowIndex);
8198 }else if(e.ctrlKey && isSelected){
8199 this.deselectRow(rowIndex);
8200 }else if(!isSelected){
8201 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8202 view.focusRow(rowIndex);
8205 this.fireEvent("afterselectionchange", this);
8208 handleDragableRowClick : function(grid, rowIndex, e)
8210 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8211 this.selectRow(rowIndex, false);
8212 var view = this.grid.view ? this.grid.view : this.grid;
8213 view.focusRow(rowIndex);
8214 this.fireEvent("afterselectionchange", this);
8219 * Selects multiple rows.
8220 * @param {Array} rows Array of the indexes of the row to select
8221 * @param {Boolean} keepExisting (optional) True to keep existing selections
8223 selectRows : function(rows, keepExisting){
8225 this.clearSelections();
8227 for(var i = 0, len = rows.length; i < len; i++){
8228 this.selectRow(rows[i], true);
8233 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8234 * @param {Number} startRow The index of the first row in the range
8235 * @param {Number} endRow The index of the last row in the range
8236 * @param {Boolean} keepExisting (optional) True to retain existing selections
8238 selectRange : function(startRow, endRow, keepExisting){
8243 this.clearSelections();
8245 if(startRow <= endRow){
8246 for(var i = startRow; i <= endRow; i++){
8247 this.selectRow(i, true);
8250 for(var i = startRow; i >= endRow; i--){
8251 this.selectRow(i, true);
8257 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8258 * @param {Number} startRow The index of the first row in the range
8259 * @param {Number} endRow The index of the last row in the range
8261 deselectRange : function(startRow, endRow, preventViewNotify){
8265 for(var i = startRow; i <= endRow; i++){
8266 this.deselectRow(i, preventViewNotify);
8272 * @param {Number} row The index of the row to select
8273 * @param {Boolean} keepExisting (optional) True to keep existing selections
8275 selectRow : function(index, keepExisting, preventViewNotify){
8276 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8279 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8280 if(!keepExisting || this.singleSelect){
8281 this.clearSelections();
8283 var r = this.grid.ds.getAt(index);
8284 this.selections.add(r);
8285 this.last = this.lastActive = index;
8286 if(!preventViewNotify){
8287 var view = this.grid.view ? this.grid.view : this.grid;
8288 view.onRowSelect(index);
8290 this.fireEvent("rowselect", this, index, r);
8291 this.fireEvent("selectionchange", this);
8297 * @param {Number} row The index of the row to deselect
8299 deselectRow : function(index, preventViewNotify){
8303 if(this.last == index){
8306 if(this.lastActive == index){
8307 this.lastActive = false;
8309 var r = this.grid.ds.getAt(index);
8310 this.selections.remove(r);
8311 if(!preventViewNotify){
8312 var view = this.grid.view ? this.grid.view : this.grid;
8313 view.onRowDeselect(index);
8315 this.fireEvent("rowdeselect", this, index);
8316 this.fireEvent("selectionchange", this);
8320 restoreLast : function(){
8322 this.last = this._last;
8327 acceptsNav : function(row, col, cm){
8328 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8332 onEditorKey : function(field, e){
8333 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8338 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8340 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8342 }else if(k == e.ENTER && !e.ctrlKey){
8346 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8348 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8350 }else if(k == e.ESC){
8354 g.startEditing(newCell[0], newCell[1]);
8359 * Ext JS Library 1.1.1
8360 * Copyright(c) 2006-2007, Ext JS, LLC.
8362 * Originally Released Under LGPL - original licence link has changed is not relivant.
8365 * <script type="text/javascript">
8370 * @class Roo.grid.ColumnModel
8371 * @extends Roo.util.Observable
8372 * This is the default implementation of a ColumnModel used by the Grid. It defines
8373 * the columns in the grid.
8376 var colModel = new Roo.grid.ColumnModel([
8377 {header: "Ticker", width: 60, sortable: true, locked: true},
8378 {header: "Company Name", width: 150, sortable: true},
8379 {header: "Market Cap.", width: 100, sortable: true},
8380 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8381 {header: "Employees", width: 100, sortable: true, resizable: false}
8386 * The config options listed for this class are options which may appear in each
8387 * individual column definition.
8388 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8390 * @param {Object} config An Array of column config objects. See this class's
8391 * config objects for details.
8393 Roo.grid.ColumnModel = function(config){
8395 * The config passed into the constructor
8397 this.config = []; //config;
8400 // if no id, create one
8401 // if the column does not have a dataIndex mapping,
8402 // map it to the order it is in the config
8403 for(var i = 0, len = config.length; i < len; i++){
8404 this.addColumn(config[i]);
8409 * The width of columns which have no width specified (defaults to 100)
8412 this.defaultWidth = 100;
8415 * Default sortable of columns which have no sortable specified (defaults to false)
8418 this.defaultSortable = false;
8422 * @event widthchange
8423 * Fires when the width of a column changes.
8424 * @param {ColumnModel} this
8425 * @param {Number} columnIndex The column index
8426 * @param {Number} newWidth The new width
8428 "widthchange": true,
8430 * @event headerchange
8431 * Fires when the text of a header changes.
8432 * @param {ColumnModel} this
8433 * @param {Number} columnIndex The column index
8434 * @param {Number} newText The new header text
8436 "headerchange": true,
8438 * @event hiddenchange
8439 * Fires when a column is hidden or "unhidden".
8440 * @param {ColumnModel} this
8441 * @param {Number} columnIndex The column index
8442 * @param {Boolean} hidden true if hidden, false otherwise
8444 "hiddenchange": true,
8446 * @event columnmoved
8447 * Fires when a column is moved.
8448 * @param {ColumnModel} this
8449 * @param {Number} oldIndex
8450 * @param {Number} newIndex
8452 "columnmoved" : true,
8454 * @event columlockchange
8455 * Fires when a column's locked state is changed
8456 * @param {ColumnModel} this
8457 * @param {Number} colIndex
8458 * @param {Boolean} locked true if locked
8460 "columnlockchange" : true
8462 Roo.grid.ColumnModel.superclass.constructor.call(this);
8464 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8466 * @cfg {String} header [required] The header text to display in the Grid view.
8469 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8472 * @cfg {String} smHeader Header at Bootsrap Small width
8475 * @cfg {String} mdHeader Header at Bootsrap Medium width
8478 * @cfg {String} lgHeader Header at Bootsrap Large width
8481 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8484 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8485 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8486 * specified, the column's index is used as an index into the Record's data Array.
8489 * @cfg {Number} width The initial width in pixels of the column. Using this
8490 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8493 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8494 * Defaults to the value of the {@link #defaultSortable} property.
8495 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8498 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8501 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8504 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8507 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8510 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8511 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8512 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8513 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8516 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8519 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8522 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8525 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8528 * @cfg {String} tooltip mouse over tooltip text
8531 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8534 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8537 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8540 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8546 * Returns the id of the column at the specified index.
8547 * @param {Number} index The column index
8548 * @return {String} the id
8550 getColumnId : function(index){
8551 return this.config[index].id;
8555 * Returns the column for a specified id.
8556 * @param {String} id The column id
8557 * @return {Object} the column
8559 getColumnById : function(id){
8560 return this.lookup[id];
8565 * Returns the column Object for a specified dataIndex.
8566 * @param {String} dataIndex The column dataIndex
8567 * @return {Object|Boolean} the column or false if not found
8569 getColumnByDataIndex: function(dataIndex){
8570 var index = this.findColumnIndex(dataIndex);
8571 return index > -1 ? this.config[index] : false;
8575 * Returns the index for a specified column id.
8576 * @param {String} id The column id
8577 * @return {Number} the index, or -1 if not found
8579 getIndexById : function(id){
8580 for(var i = 0, len = this.config.length; i < len; i++){
8581 if(this.config[i].id == id){
8589 * Returns the index for a specified column dataIndex.
8590 * @param {String} dataIndex The column dataIndex
8591 * @return {Number} the index, or -1 if not found
8594 findColumnIndex : function(dataIndex){
8595 for(var i = 0, len = this.config.length; i < len; i++){
8596 if(this.config[i].dataIndex == dataIndex){
8604 moveColumn : function(oldIndex, newIndex){
8605 var c = this.config[oldIndex];
8606 this.config.splice(oldIndex, 1);
8607 this.config.splice(newIndex, 0, c);
8608 this.dataMap = null;
8609 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8612 isLocked : function(colIndex){
8613 return this.config[colIndex].locked === true;
8616 setLocked : function(colIndex, value, suppressEvent){
8617 if(this.isLocked(colIndex) == value){
8620 this.config[colIndex].locked = value;
8622 this.fireEvent("columnlockchange", this, colIndex, value);
8626 getTotalLockedWidth : function(){
8628 for(var i = 0; i < this.config.length; i++){
8629 if(this.isLocked(i) && !this.isHidden(i)){
8630 this.totalWidth += this.getColumnWidth(i);
8636 getLockedCount : function(){
8637 for(var i = 0, len = this.config.length; i < len; i++){
8638 if(!this.isLocked(i)){
8643 return this.config.length;
8647 * Returns the number of columns.
8650 getColumnCount : function(visibleOnly){
8651 if(visibleOnly === true){
8653 for(var i = 0, len = this.config.length; i < len; i++){
8654 if(!this.isHidden(i)){
8660 return this.config.length;
8664 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8665 * @param {Function} fn
8666 * @param {Object} scope (optional)
8667 * @return {Array} result
8669 getColumnsBy : function(fn, scope){
8671 for(var i = 0, len = this.config.length; i < len; i++){
8672 var c = this.config[i];
8673 if(fn.call(scope||this, c, i) === true){
8681 * Returns true if the specified column is sortable.
8682 * @param {Number} col The column index
8685 isSortable : function(col){
8686 if(typeof this.config[col].sortable == "undefined"){
8687 return this.defaultSortable;
8689 return this.config[col].sortable;
8693 * Returns the rendering (formatting) function defined for the column.
8694 * @param {Number} col The column index.
8695 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8697 getRenderer : function(col){
8698 if(!this.config[col].renderer){
8699 return Roo.grid.ColumnModel.defaultRenderer;
8701 return this.config[col].renderer;
8705 * Sets the rendering (formatting) function for a column.
8706 * @param {Number} col The column index
8707 * @param {Function} fn The function to use to process the cell's raw data
8708 * to return HTML markup for the grid view. The render function is called with
8709 * the following parameters:<ul>
8710 * <li>Data value.</li>
8711 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8712 * <li>css A CSS style string to apply to the table cell.</li>
8713 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8714 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8715 * <li>Row index</li>
8716 * <li>Column index</li>
8717 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8719 setRenderer : function(col, fn){
8720 this.config[col].renderer = fn;
8724 * Returns the width for the specified column.
8725 * @param {Number} col The column index
8726 * @param (optional) {String} gridSize bootstrap width size.
8729 getColumnWidth : function(col, gridSize)
8731 var cfg = this.config[col];
8733 if (typeof(gridSize) == 'undefined') {
8734 return cfg.width * 1 || this.defaultWidth;
8736 if (gridSize === false) { // if we set it..
8737 return cfg.width || false;
8739 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8741 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8742 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8745 return cfg[ sizes[i] ];
8752 * Sets the width for a column.
8753 * @param {Number} col The column index
8754 * @param {Number} width The new width
8756 setColumnWidth : function(col, width, suppressEvent){
8757 this.config[col].width = width;
8758 this.totalWidth = null;
8760 this.fireEvent("widthchange", this, col, width);
8765 * Returns the total width of all columns.
8766 * @param {Boolean} includeHidden True to include hidden column widths
8769 getTotalWidth : function(includeHidden){
8770 if(!this.totalWidth){
8771 this.totalWidth = 0;
8772 for(var i = 0, len = this.config.length; i < len; i++){
8773 if(includeHidden || !this.isHidden(i)){
8774 this.totalWidth += this.getColumnWidth(i);
8778 return this.totalWidth;
8782 * Returns the header for the specified column.
8783 * @param {Number} col The column index
8786 getColumnHeader : function(col){
8787 return this.config[col].header;
8791 * Sets the header for a column.
8792 * @param {Number} col The column index
8793 * @param {String} header The new header
8795 setColumnHeader : function(col, header){
8796 this.config[col].header = header;
8797 this.fireEvent("headerchange", this, col, header);
8801 * Returns the tooltip for the specified column.
8802 * @param {Number} col The column index
8805 getColumnTooltip : function(col){
8806 return this.config[col].tooltip;
8809 * Sets the tooltip for a column.
8810 * @param {Number} col The column index
8811 * @param {String} tooltip The new tooltip
8813 setColumnTooltip : function(col, tooltip){
8814 this.config[col].tooltip = tooltip;
8818 * Returns the dataIndex for the specified column.
8819 * @param {Number} col The column index
8822 getDataIndex : function(col){
8823 return this.config[col].dataIndex;
8827 * Sets the dataIndex for a column.
8828 * @param {Number} col The column index
8829 * @param {Number} dataIndex The new dataIndex
8831 setDataIndex : function(col, dataIndex){
8832 this.config[col].dataIndex = dataIndex;
8838 * Returns true if the cell is editable.
8839 * @param {Number} colIndex The column index
8840 * @param {Number} rowIndex The row index - this is nto actually used..?
8843 isCellEditable : function(colIndex, rowIndex){
8844 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8848 * Returns the editor defined for the cell/column.
8849 * return false or null to disable editing.
8850 * @param {Number} colIndex The column index
8851 * @param {Number} rowIndex The row index
8854 getCellEditor : function(colIndex, rowIndex){
8855 return this.config[colIndex].editor;
8859 * Sets if a column is editable.
8860 * @param {Number} col The column index
8861 * @param {Boolean} editable True if the column is editable
8863 setEditable : function(col, editable){
8864 this.config[col].editable = editable;
8869 * Returns true if the column is hidden.
8870 * @param {Number} colIndex The column index
8873 isHidden : function(colIndex){
8874 return this.config[colIndex].hidden;
8879 * Returns true if the column width cannot be changed
8881 isFixed : function(colIndex){
8882 return this.config[colIndex].fixed;
8886 * Returns true if the column can be resized
8889 isResizable : function(colIndex){
8890 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8893 * Sets if a column is hidden.
8894 * @param {Number} colIndex The column index
8895 * @param {Boolean} hidden True if the column is hidden
8897 setHidden : function(colIndex, hidden){
8898 this.config[colIndex].hidden = hidden;
8899 this.totalWidth = null;
8900 this.fireEvent("hiddenchange", this, colIndex, hidden);
8904 * Sets the editor for a column.
8905 * @param {Number} col The column index
8906 * @param {Object} editor The editor object
8908 setEditor : function(col, editor){
8909 this.config[col].editor = editor;
8912 * Add a column (experimental...) - defaults to adding to the end..
8913 * @param {Object} config
8915 addColumn : function(c)
8918 var i = this.config.length;
8921 if(typeof c.dataIndex == "undefined"){
8924 if(typeof c.renderer == "string"){
8925 c.renderer = Roo.util.Format[c.renderer];
8927 if(typeof c.id == "undefined"){
8930 if(c.editor && c.editor.xtype){
8931 c.editor = Roo.factory(c.editor, Roo.grid);
8933 if(c.editor && c.editor.isFormField){
8934 c.editor = new Roo.grid.GridEditor(c.editor);
8936 this.lookup[c.id] = c;
8941 Roo.grid.ColumnModel.defaultRenderer = function(value)
8943 if(typeof value == "object") {
8946 if(typeof value == "string" && value.length < 1){
8950 return String.format("{0}", value);
8953 // Alias for backwards compatibility
8954 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8957 * Ext JS Library 1.1.1
8958 * Copyright(c) 2006-2007, Ext JS, LLC.
8960 * Originally Released Under LGPL - original licence link has changed is not relivant.
8963 * <script type="text/javascript">
8967 * @class Roo.LoadMask
8968 * A simple utility class for generically masking elements while loading data. If the element being masked has
8969 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8970 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8971 * element's UpdateManager load indicator and will be destroyed after the initial load.
8973 * Create a new LoadMask
8974 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8975 * @param {Object} config The config object
8977 Roo.LoadMask = function(el, config){
8978 this.el = Roo.get(el);
8979 Roo.apply(this, config);
8981 this.store.on('beforeload', this.onBeforeLoad, this);
8982 this.store.on('load', this.onLoad, this);
8983 this.store.on('loadexception', this.onLoadException, this);
8984 this.removeMask = false;
8986 var um = this.el.getUpdateManager();
8987 um.showLoadIndicator = false; // disable the default indicator
8988 um.on('beforeupdate', this.onBeforeLoad, this);
8989 um.on('update', this.onLoad, this);
8990 um.on('failure', this.onLoad, this);
8991 this.removeMask = true;
8995 Roo.LoadMask.prototype = {
8997 * @cfg {Boolean} removeMask
8998 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8999 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9004 * The text to display in a centered loading message box (defaults to 'Loading...')
9008 * @cfg {String} msgCls
9009 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9011 msgCls : 'x-mask-loading',
9014 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9020 * Disables the mask to prevent it from being displayed
9022 disable : function(){
9023 this.disabled = true;
9027 * Enables the mask so that it can be displayed
9029 enable : function(){
9030 this.disabled = false;
9033 onLoadException : function()
9037 if (typeof(arguments[3]) != 'undefined') {
9038 Roo.MessageBox.alert("Error loading",arguments[3]);
9042 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9043 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9050 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9055 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9059 onBeforeLoad : function(){
9061 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9066 destroy : function(){
9068 this.store.un('beforeload', this.onBeforeLoad, this);
9069 this.store.un('load', this.onLoad, this);
9070 this.store.un('loadexception', this.onLoadException, this);
9072 var um = this.el.getUpdateManager();
9073 um.un('beforeupdate', this.onBeforeLoad, this);
9074 um.un('update', this.onLoad, this);
9075 um.un('failure', this.onLoad, this);
9079 * @class Roo.bootstrap.Table
9081 * @extends Roo.bootstrap.Component
9082 * @children Roo.bootstrap.TableBody
9083 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9084 * Similar to Roo.grid.Grid
9086 var table = Roo.factory({
9088 xns : Roo.bootstrap,
9089 autoSizeColumns: true,
9096 sortInfo : { direction : 'ASC', field: 'name' },
9098 xtype : 'HttpProxy',
9101 url : 'https://example.com/some.data.url.json'
9104 xtype : 'JsonReader',
9106 fields : [ 'id', 'name', whatever' ],
9113 xtype : 'ColumnModel',
9117 dataIndex : 'is_in_group',
9120 renderer : function(v, x , r) {
9122 return String.format("{0}", v)
9128 xtype : 'RowSelectionModel',
9129 xns : Roo.bootstrap.Table
9130 // you can add listeners to catch selection change here....
9136 grid.render(Roo.get("some-div"));
9139 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9144 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9145 * @cfg {Roo.data.Store} store The data store to use
9146 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9148 * @cfg {String} cls table class
9151 * @cfg {string} empty_results Text to display for no results
9152 * @cfg {boolean} striped Should the rows be alternative striped
9153 * @cfg {boolean} bordered Add borders to the table
9154 * @cfg {boolean} hover Add hover highlighting
9155 * @cfg {boolean} condensed Format condensed
9156 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9157 * also adds table-responsive (see bootstrap docs for details)
9158 * @cfg {Boolean} loadMask (true|false) default false
9159 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9160 * @cfg {Boolean} summaryFooterShow (true|false) generate tfoot for summary, default false
9161 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162 * @cfg {Boolean} rowSelection (true|false) default false
9163 * @cfg {Boolean} cellSelection (true|false) default false
9164 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9166 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9167 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9168 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9169 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9172 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9175 * Create a new Table
9176 * @param {Object} config The config object
9179 Roo.bootstrap.Table = function(config)
9181 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9184 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9185 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9186 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9187 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9189 this.view = this; // compat with grid.
9191 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9193 this.sm.grid = this;
9194 this.selModel = Roo.factory(this.sm, Roo.grid);
9195 this.sm = this.selModel;
9196 this.sm.xmodule = this.xmodule || false;
9199 if (this.cm && typeof(this.cm.config) == 'undefined') {
9200 this.colModel = new Roo.grid.ColumnModel(this.cm);
9201 this.cm = this.colModel;
9202 this.cm.xmodule = this.xmodule || false;
9205 this.store= Roo.factory(this.store, Roo.data);
9206 this.ds = this.store;
9207 this.ds.xmodule = this.xmodule || false;
9210 if (this.footer && this.store) {
9211 this.footer.dataSource = this.ds;
9212 this.footer = Roo.factory(this.footer);
9219 * Fires when a cell is clicked
9220 * @param {Roo.bootstrap.Table} this
9221 * @param {Roo.Element} el
9222 * @param {Number} rowIndex
9223 * @param {Number} columnIndex
9224 * @param {Roo.EventObject} e
9228 * @event celldblclick
9229 * Fires when a cell is double clicked
9230 * @param {Roo.bootstrap.Table} this
9231 * @param {Roo.Element} el
9232 * @param {Number} rowIndex
9233 * @param {Number} columnIndex
9234 * @param {Roo.EventObject} e
9236 "celldblclick" : true,
9239 * Fires when a row is clicked
9240 * @param {Roo.bootstrap.Table} this
9241 * @param {Roo.Element} el
9242 * @param {Number} rowIndex
9243 * @param {Roo.EventObject} e
9247 * @event rowdblclick
9248 * Fires when a row is double clicked
9249 * @param {Roo.bootstrap.Table} this
9250 * @param {Roo.Element} el
9251 * @param {Number} rowIndex
9252 * @param {Roo.EventObject} e
9254 "rowdblclick" : true,
9257 * Fires when a mouseover occur
9258 * @param {Roo.bootstrap.Table} this
9259 * @param {Roo.Element} el
9260 * @param {Number} rowIndex
9261 * @param {Number} columnIndex
9262 * @param {Roo.EventObject} e
9267 * Fires when a mouseout occur
9268 * @param {Roo.bootstrap.Table} this
9269 * @param {Roo.Element} el
9270 * @param {Number} rowIndex
9271 * @param {Number} columnIndex
9272 * @param {Roo.EventObject} e
9277 * Fires when a row is rendered, so you can change add a style to it.
9278 * @param {Roo.bootstrap.Table} this
9279 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9283 * @event rowsrendered
9284 * Fires when all the rows have been rendered
9285 * @param {Roo.bootstrap.Table} this
9287 'rowsrendered' : true,
9289 * @event contextmenu
9290 * The raw contextmenu event for the entire grid.
9291 * @param {Roo.EventObject} e
9293 "contextmenu" : true,
9295 * @event rowcontextmenu
9296 * Fires when a row is right clicked
9297 * @param {Roo.bootstrap.Table} this
9298 * @param {Number} rowIndex
9299 * @param {Roo.EventObject} e
9301 "rowcontextmenu" : true,
9303 * @event cellcontextmenu
9304 * Fires when a cell is right clicked
9305 * @param {Roo.bootstrap.Table} this
9306 * @param {Number} rowIndex
9307 * @param {Number} cellIndex
9308 * @param {Roo.EventObject} e
9310 "cellcontextmenu" : true,
9312 * @event headercontextmenu
9313 * Fires when a header is right clicked
9314 * @param {Roo.bootstrap.Table} this
9315 * @param {Number} columnIndex
9316 * @param {Roo.EventObject} e
9318 "headercontextmenu" : true,
9321 * The raw mousedown event for the entire grid.
9322 * @param {Roo.EventObject} e
9329 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9345 summaryFooterShow : false,
9347 enableColumnResize: true,
9348 disableAutoSize: false,
9350 rowSelection : false,
9351 cellSelection : false,
9354 minColumnWidth : 50,
9356 // Roo.Element - the tbody
9357 bodyEl: false, // <tbody> Roo.Element - thead element
9358 headEl: false, // <thead> Roo.Element - thead element
9359 resizeProxy : false, // proxy element for dragging?
9363 container: false, // used by gridpanel...
9369 auto_hide_footer : false,
9371 view: false, // actually points to this..
9373 getAutoCreate : function()
9375 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9382 // this get's auto added by panel.Grid
9383 if (this.scrollBody) {
9384 cfg.cls += ' table-body-fixed';
9387 cfg.cls += ' table-striped';
9391 cfg.cls += ' table-hover';
9393 if (this.bordered) {
9394 cfg.cls += ' table-bordered';
9396 if (this.condensed) {
9397 cfg.cls += ' table-condensed';
9400 if (this.responsive) {
9401 cfg.cls += ' table-responsive';
9405 cfg.cls+= ' ' +this.cls;
9411 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9414 if(this.store || this.cm){
9415 if(this.headerShow){
9416 cfg.cn.push(this.renderHeader());
9419 cfg.cn.push(this.renderBody());
9421 if(this.footerShow){
9422 cfg.cn.push(this.renderFooter());
9425 if(!this.footerShow && this.summaryFooterShow) {
9426 cfg.cn.push(this.renderSummaryFooter());
9429 // where does this come from?
9430 //cfg.cls+= ' TableGrid';
9433 return { cn : [ cfg ] };
9436 initEvents : function()
9438 if(!this.store || !this.cm){
9441 if (this.selModel) {
9442 this.selModel.initEvents();
9446 //Roo.log('initEvents with ds!!!!');
9448 this.bodyEl = this.el.select('tbody', true).first();
9449 this.headEl = this.el.select('thead', true).first();
9450 this.mainFoot = this.el.select('tfoot', true).first();
9455 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9456 e.on('click', this.sort, this);
9460 // why is this done????? = it breaks dialogs??
9461 //this.parent().el.setStyle('position', 'relative');
9465 this.footer.parentId = this.id;
9466 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9469 this.el.select('tfoot tr td').first().addClass('hide');
9474 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9477 this.store.on('load', this.onLoad, this);
9478 this.store.on('beforeload', this.onBeforeLoad, this);
9479 this.store.on('update', this.onUpdate, this);
9480 this.store.on('add', this.onAdd, this);
9481 this.store.on("clear", this.clear, this);
9483 this.el.on("contextmenu", this.onContextMenu, this);
9486 this.cm.on("headerchange", this.onHeaderChange, this);
9487 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9489 //?? does bodyEl get replaced on render?
9490 this.bodyEl.on("click", this.onClick, this);
9491 this.bodyEl.on("dblclick", this.onDblClick, this);
9492 this.bodyEl.on('scroll', this.onBodyScroll, this);
9494 // guessing mainbody will work - this relays usually caught by selmodel at present.
9495 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9498 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9501 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9502 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9507 // Compatibility with grid - we implement all the view features at present.
9508 getView : function()
9513 initCSS : function()
9515 if(this.disableAutoSize) {
9519 var cm = this.cm, styles = [];
9520 this.CSS.removeStyleSheet(this.id + '-cssrules');
9521 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9522 // we can honour xs/sm/md/xl as widths...
9523 // we first have to decide what widht we are currently at...
9524 var sz = Roo.getGridSize();
9528 var cols = []; // visable cols.
9530 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9531 var w = cm.getColumnWidth(i, false);
9533 cols.push( { rel : false, abs : 0 });
9537 cols.push( { rel : false, abs : w });
9539 last = i; // not really..
9542 var w = cm.getColumnWidth(i, sz);
9547 cols.push( { rel : w, abs : false });
9550 var avail = this.bodyEl.dom.clientWidth - total_abs;
9552 var unitWidth = Math.floor(avail / total);
9553 var rem = avail - (unitWidth * total);
9555 var hidden, width, pos = 0 , splithide , left;
9556 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9558 hidden = 'display:none;';
9560 width = 'width:0px;';
9562 if(!cm.isHidden(i)){
9566 // we can honour xs/sm/md/xl ?
9567 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9569 hidden = 'display:none;';
9571 // width should return a small number...
9573 w+=rem; // add the remaining with..
9576 left = "left:" + (pos -4) + "px;";
9577 width = "width:" + w+ "px;";
9580 if (this.responsive) {
9583 hidden = cm.isHidden(i) ? 'display:none;' : '';
9584 splithide = 'display: none;';
9587 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9590 splithide = 'display:none;';
9593 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9594 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9595 // this is the popover version..
9596 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9601 //Roo.log(styles.join(''));
9602 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9608 onContextMenu : function(e, t)
9610 this.processEvent("contextmenu", e);
9613 processEvent : function(name, e)
9615 if (name != 'touchstart' ) {
9616 this.fireEvent(name, e);
9619 var t = e.getTarget();
9621 var cell = Roo.get(t);
9627 if(cell.findParent('tfoot', false, true)){
9631 if(cell.findParent('thead', false, true)){
9633 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9634 cell = Roo.get(t).findParent('th', false, true);
9636 Roo.log("failed to find th in thead?");
9637 Roo.log(e.getTarget());
9642 var cellIndex = cell.dom.cellIndex;
9644 var ename = name == 'touchstart' ? 'click' : name;
9645 this.fireEvent("header" + ename, this, cellIndex, e);
9650 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9651 cell = Roo.get(t).findParent('td', false, true);
9653 Roo.log("failed to find th in tbody?");
9654 Roo.log(e.getTarget());
9659 var row = cell.findParent('tr', false, true);
9660 var cellIndex = cell.dom.cellIndex;
9661 var rowIndex = row.dom.rowIndex - 1;
9665 this.fireEvent("row" + name, this, rowIndex, e);
9669 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9675 onMouseover : function(e, el)
9677 var cell = Roo.get(el);
9683 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9684 cell = cell.findParent('td', false, true);
9687 var row = cell.findParent('tr', false, true);
9688 var cellIndex = cell.dom.cellIndex;
9689 var rowIndex = row.dom.rowIndex - 1; // start from 0
9691 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9695 onMouseout : function(e, el)
9697 var cell = Roo.get(el);
9703 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9704 cell = cell.findParent('td', false, true);
9707 var row = cell.findParent('tr', false, true);
9708 var cellIndex = cell.dom.cellIndex;
9709 var rowIndex = row.dom.rowIndex - 1; // start from 0
9711 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9715 onClick : function(e, el)
9717 var cell = Roo.get(el);
9719 if(!cell || (!this.cellSelection && !this.rowSelection)){
9723 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9724 cell = cell.findParent('td', false, true);
9727 if(!cell || typeof(cell) == 'undefined'){
9731 var row = cell.findParent('tr', false, true);
9733 if(!row || typeof(row) == 'undefined'){
9737 var cellIndex = cell.dom.cellIndex;
9738 var rowIndex = this.getRowIndex(row);
9740 // why??? - should these not be based on SelectionModel?
9741 //if(this.cellSelection){
9742 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9745 //if(this.rowSelection){
9746 this.fireEvent('rowclick', this, row, rowIndex, e);
9751 onDblClick : function(e,el)
9753 var cell = Roo.get(el);
9755 if(!cell || (!this.cellSelection && !this.rowSelection)){
9759 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9760 cell = cell.findParent('td', false, true);
9763 if(!cell || typeof(cell) == 'undefined'){
9767 var row = cell.findParent('tr', false, true);
9769 if(!row || typeof(row) == 'undefined'){
9773 var cellIndex = cell.dom.cellIndex;
9774 var rowIndex = this.getRowIndex(row);
9776 if(this.cellSelection){
9777 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9780 if(this.rowSelection){
9781 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9784 findRowIndex : function(el)
9786 var cell = Roo.get(el);
9790 var row = cell.findParent('tr', false, true);
9792 if(!row || typeof(row) == 'undefined'){
9795 return this.getRowIndex(row);
9797 sort : function(e,el)
9799 var col = Roo.get(el);
9801 if(!col.hasClass('sortable')){
9805 var sort = col.attr('sort');
9808 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9812 this.store.sortInfo = {field : sort, direction : dir};
9815 Roo.log("calling footer first");
9816 this.footer.onClick('first');
9819 this.store.load({ params : { start : 0 } });
9823 renderHeader : function()
9831 this.totalWidth = 0;
9833 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9835 var config = cm.config[i];
9839 cls : 'x-hcol-' + i,
9842 html: cm.getColumnHeader(i)
9845 var tooltip = cm.getColumnTooltip(i);
9847 c.tooltip = tooltip;
9853 if(typeof(config.sortable) != 'undefined' && config.sortable){
9854 c.cls += ' sortable';
9855 c.html = '<i class="fa"></i>' + c.html;
9858 // could use BS4 hidden-..-down
9860 if(typeof(config.lgHeader) != 'undefined'){
9861 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9864 if(typeof(config.mdHeader) != 'undefined'){
9865 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9868 if(typeof(config.smHeader) != 'undefined'){
9869 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9872 if(typeof(config.xsHeader) != 'undefined'){
9873 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9880 if(typeof(config.tooltip) != 'undefined'){
9881 c.tooltip = config.tooltip;
9884 if(typeof(config.colspan) != 'undefined'){
9885 c.colspan = config.colspan;
9888 // hidden is handled by CSS now
9890 if(typeof(config.dataIndex) != 'undefined'){
9891 c.sort = config.dataIndex;
9896 if(typeof(config.align) != 'undefined' && config.align.length){
9897 c.style += ' text-align:' + config.align + ';';
9900 /* width is done in CSS
9901 *if(typeof(config.width) != 'undefined'){
9902 c.style += ' width:' + config.width + 'px;';
9903 this.totalWidth += config.width;
9905 this.totalWidth += 100; // assume minimum of 100 per column?
9909 if(typeof(config.cls) != 'undefined'){
9910 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9912 // this is the bit that doesnt reall work at all...
9914 if (this.responsive) {
9917 ['xs','sm','md','lg'].map(function(size){
9919 if(typeof(config[size]) == 'undefined'){
9923 if (!config[size]) { // 0 = hidden
9924 // BS 4 '0' is treated as hide that column and below.
9925 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9929 c.cls += ' col-' + size + '-' + config[size] + (
9930 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9938 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9949 renderBody : function()
9959 colspan : this.cm.getColumnCount()
9969 renderFooter : function()
9979 colspan : this.cm.getColumnCount()
9989 renderSummaryFooter : function()
9998 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10002 cls : 'x-fcol-' + i,
10015 onLoad : function()
10017 // Roo.log('ds onload');
10022 var ds = this.store;
10024 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10025 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10026 if (_this.store.sortInfo) {
10028 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10029 e.select('i', true).addClass(['fa-arrow-up']);
10032 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10033 e.select('i', true).addClass(['fa-arrow-down']);
10038 var tbody = this.bodyEl;
10040 if(ds.getCount() > 0){
10041 ds.data.each(function(d,rowIndex){
10042 var row = this.renderRow(cm, ds, rowIndex);
10047 tbody.createChild(row);
10051 if(row.cellObjects.length){
10052 Roo.each(row.cellObjects, function(r){
10053 _this.renderCellObject(r);
10058 } else if (this.empty_results.length) {
10059 this.el.mask(this.empty_results, 'no-spinner');
10062 var tfoot = this.el.select('tfoot', true).first();
10064 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10066 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10068 var total = this.ds.getTotalCount();
10070 if(this.footer.pageSize < total){
10071 this.mainFoot.show();
10075 if(!this.footerShow && this.summaryFooterShow) {
10077 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10079 var value = cm.config[i].summaryFooter;
10081 Roo.log('value [' + i + '] : ' + value);
10085 Roo.each(this.el.select('tbody td', true).elements, function(e){
10086 e.on('mouseover', _this.onMouseover, _this);
10089 Roo.each(this.el.select('tbody td', true).elements, function(e){
10090 e.on('mouseout', _this.onMouseout, _this);
10092 this.fireEvent('rowsrendered', this);
10096 this.initCSS(); /// resize cols
10102 onUpdate : function(ds,record)
10104 this.refreshRow(record);
10108 onRemove : function(ds, record, index, isUpdate){
10109 if(isUpdate !== true){
10110 this.fireEvent("beforerowremoved", this, index, record);
10112 var bt = this.bodyEl.dom;
10114 var rows = this.el.select('tbody > tr', true).elements;
10116 if(typeof(rows[index]) != 'undefined'){
10117 bt.removeChild(rows[index].dom);
10120 // if(bt.rows[index]){
10121 // bt.removeChild(bt.rows[index]);
10124 if(isUpdate !== true){
10125 //this.stripeRows(index);
10126 //this.syncRowHeights(index, index);
10128 this.fireEvent("rowremoved", this, index, record);
10132 onAdd : function(ds, records, rowIndex)
10134 //Roo.log('on Add called');
10135 // - note this does not handle multiple adding very well..
10136 var bt = this.bodyEl.dom;
10137 for (var i =0 ; i < records.length;i++) {
10138 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10139 //Roo.log(records[i]);
10140 //Roo.log(this.store.getAt(rowIndex+i));
10141 this.insertRow(this.store, rowIndex + i, false);
10148 refreshRow : function(record){
10149 var ds = this.store, index;
10150 if(typeof record == 'number'){
10152 record = ds.getAt(index);
10154 index = ds.indexOf(record);
10156 return; // should not happen - but seems to
10159 this.insertRow(ds, index, true);
10161 this.onRemove(ds, record, index+1, true);
10163 //this.syncRowHeights(index, index);
10165 this.fireEvent("rowupdated", this, index, record);
10167 // private - called by RowSelection
10168 onRowSelect : function(rowIndex){
10169 var row = this.getRowDom(rowIndex);
10170 row.addClass(['bg-info','info']);
10172 // private - called by RowSelection
10173 onRowDeselect : function(rowIndex)
10175 if (rowIndex < 0) {
10178 var row = this.getRowDom(rowIndex);
10179 row.removeClass(['bg-info','info']);
10182 * Focuses the specified row.
10183 * @param {Number} row The row index
10185 focusRow : function(row)
10187 //Roo.log('GridView.focusRow');
10188 var x = this.bodyEl.dom.scrollLeft;
10189 this.focusCell(row, 0, false);
10190 this.bodyEl.dom.scrollLeft = x;
10194 * Focuses the specified cell.
10195 * @param {Number} row The row index
10196 * @param {Number} col The column index
10197 * @param {Boolean} hscroll false to disable horizontal scrolling
10199 focusCell : function(row, col, hscroll)
10201 //Roo.log('GridView.focusCell');
10202 var el = this.ensureVisible(row, col, hscroll);
10203 // not sure what focusEL achives = it's a <a> pos relative
10204 //this.focusEl.alignTo(el, "tl-tl");
10206 // this.focusEl.focus();
10208 // this.focusEl.focus.defer(1, this.focusEl);
10213 * Scrolls the specified cell into view
10214 * @param {Number} row The row index
10215 * @param {Number} col The column index
10216 * @param {Boolean} hscroll false to disable horizontal scrolling
10218 ensureVisible : function(row, col, hscroll)
10220 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10221 //return null; //disable for testing.
10222 if(typeof row != "number"){
10223 row = row.rowIndex;
10225 if(row < 0 && row >= this.ds.getCount()){
10228 col = (col !== undefined ? col : 0);
10230 while(cm.isHidden(col)){
10234 var el = this.getCellDom(row, col);
10238 var c = this.bodyEl.dom;
10240 var ctop = parseInt(el.offsetTop, 10);
10241 var cleft = parseInt(el.offsetLeft, 10);
10242 var cbot = ctop + el.offsetHeight;
10243 var cright = cleft + el.offsetWidth;
10245 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10246 var ch = 0; //?? header is not withing the area?
10247 var stop = parseInt(c.scrollTop, 10);
10248 var sleft = parseInt(c.scrollLeft, 10);
10249 var sbot = stop + ch;
10250 var sright = sleft + c.clientWidth;
10252 Roo.log('GridView.ensureVisible:' +
10254 ' c.clientHeight:' + c.clientHeight +
10255 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10263 c.scrollTop = ctop;
10264 //Roo.log("set scrolltop to ctop DISABLE?");
10265 }else if(cbot > sbot){
10266 //Roo.log("set scrolltop to cbot-ch");
10267 c.scrollTop = cbot-ch;
10270 if(hscroll !== false){
10272 c.scrollLeft = cleft;
10273 }else if(cright > sright){
10274 c.scrollLeft = cright-c.clientWidth;
10282 insertRow : function(dm, rowIndex, isUpdate){
10285 this.fireEvent("beforerowsinserted", this, rowIndex);
10287 //var s = this.getScrollState();
10288 var row = this.renderRow(this.cm, this.store, rowIndex);
10289 // insert before rowIndex..
10290 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10294 if(row.cellObjects.length){
10295 Roo.each(row.cellObjects, function(r){
10296 _this.renderCellObject(r);
10301 this.fireEvent("rowsinserted", this, rowIndex);
10302 //this.syncRowHeights(firstRow, lastRow);
10303 //this.stripeRows(firstRow);
10310 getRowDom : function(rowIndex)
10312 var rows = this.el.select('tbody > tr', true).elements;
10314 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10317 getCellDom : function(rowIndex, colIndex)
10319 var row = this.getRowDom(rowIndex);
10320 if (row === false) {
10323 var cols = row.select('td', true).elements;
10324 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10328 // returns the object tree for a tr..
10331 renderRow : function(cm, ds, rowIndex)
10333 var d = ds.getAt(rowIndex);
10337 cls : 'x-row-' + rowIndex,
10341 var cellObjects = [];
10343 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10344 var config = cm.config[i];
10346 var renderer = cm.getRenderer(i);
10350 if(typeof(renderer) !== 'undefined'){
10351 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10353 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10354 // and are rendered into the cells after the row is rendered - using the id for the element.
10356 if(typeof(value) === 'object'){
10366 rowIndex : rowIndex,
10371 this.fireEvent('rowclass', this, rowcfg);
10375 // this might end up displaying HTML?
10376 // this is too messy... - better to only do it on columsn you know are going to be too long
10377 //tooltip : (typeof(value) === 'object') ? '' : value,
10378 cls : rowcfg.rowClass + ' x-col-' + i,
10380 html: (typeof(value) === 'object') ? '' : value
10387 if(typeof(config.colspan) != 'undefined'){
10388 td.colspan = config.colspan;
10393 if(typeof(config.align) != 'undefined' && config.align.length){
10394 td.style += ' text-align:' + config.align + ';';
10396 if(typeof(config.valign) != 'undefined' && config.valign.length){
10397 td.style += ' vertical-align:' + config.valign + ';';
10400 if(typeof(config.width) != 'undefined'){
10401 td.style += ' width:' + config.width + 'px;';
10405 if(typeof(config.cursor) != 'undefined'){
10406 td.style += ' cursor:' + config.cursor + ';';
10409 if(typeof(config.cls) != 'undefined'){
10410 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10412 if (this.responsive) {
10413 ['xs','sm','md','lg'].map(function(size){
10415 if(typeof(config[size]) == 'undefined'){
10421 if (!config[size]) { // 0 = hidden
10422 // BS 4 '0' is treated as hide that column and below.
10423 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10427 td.cls += ' col-' + size + '-' + config[size] + (
10428 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10438 row.cellObjects = cellObjects;
10446 onBeforeLoad : function()
10448 this.el.unmask(); // if needed.
10455 this.el.select('tbody', true).first().dom.innerHTML = '';
10458 * Show or hide a row.
10459 * @param {Number} rowIndex to show or hide
10460 * @param {Boolean} state hide
10462 setRowVisibility : function(rowIndex, state)
10464 var bt = this.bodyEl.dom;
10466 var rows = this.el.select('tbody > tr', true).elements;
10468 if(typeof(rows[rowIndex]) == 'undefined'){
10471 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10476 getSelectionModel : function(){
10477 if(!this.selModel){
10478 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10480 return this.selModel;
10483 * Render the Roo.bootstrap object from renderder
10485 renderCellObject : function(r)
10489 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10491 var t = r.cfg.render(r.container);
10494 Roo.each(r.cfg.cn, function(c){
10496 container: t.getChildContainer(),
10499 _this.renderCellObject(child);
10504 * get the Row Index from a dom element.
10505 * @param {Roo.Element} row The row to look for
10506 * @returns {Number} the row
10508 getRowIndex : function(row)
10512 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10523 * get the header TH element for columnIndex
10524 * @param {Number} columnIndex
10525 * @returns {Roo.Element}
10527 getHeaderIndex: function(colIndex)
10529 var cols = this.headEl.select('th', true).elements;
10530 return cols[colIndex];
10533 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10534 * @param {domElement} cell to look for
10535 * @returns {Number} the column
10537 getCellIndex : function(cell)
10539 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10541 return parseInt(id[1], 10);
10546 * Returns the grid's underlying element = used by panel.Grid
10547 * @return {Element} The element
10549 getGridEl : function(){
10553 * Forces a resize - used by panel.Grid
10554 * @return {Element} The element
10556 autoSize : function()
10558 if(this.disableAutoSize) {
10561 //var ctr = Roo.get(this.container.dom.parentElement);
10562 var ctr = Roo.get(this.el.dom);
10564 var thd = this.getGridEl().select('thead',true).first();
10565 var tbd = this.getGridEl().select('tbody', true).first();
10566 var tfd = this.getGridEl().select('tfoot', true).first();
10568 var cw = ctr.getWidth();
10569 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10573 tbd.setWidth(ctr.getWidth());
10574 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10575 // this needs fixing for various usage - currently only hydra job advers I think..
10577 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10579 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10582 cw = Math.max(cw, this.totalWidth);
10583 this.getGridEl().select('tbody tr',true).setWidth(cw);
10586 // resize 'expandable coloumn?
10588 return; // we doe not have a view in this design..
10591 onBodyScroll: function()
10593 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10595 this.headEl.setStyle({
10596 'position' : 'relative',
10597 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10603 var scrollHeight = this.bodyEl.dom.scrollHeight;
10605 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10607 var height = this.bodyEl.getHeight();
10609 if(scrollHeight - height == scrollTop) {
10611 var total = this.ds.getTotalCount();
10613 if(this.footer.cursor + this.footer.pageSize < total){
10615 this.footer.ds.load({
10617 start : this.footer.cursor + this.footer.pageSize,
10618 limit : this.footer.pageSize
10627 onColumnSplitterMoved : function(i, diff)
10629 this.userResized = true;
10631 var cm = this.colModel;
10633 var w = this.getHeaderIndex(i).getWidth() + diff;
10636 cm.setColumnWidth(i, w, true);
10638 //var cid = cm.getColumnId(i); << not used in this version?
10639 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10641 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10642 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10643 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10645 //this.updateSplitters();
10646 //this.layout(); << ??
10647 this.fireEvent("columnresize", i, w);
10649 onHeaderChange : function()
10651 var header = this.renderHeader();
10652 var table = this.el.select('table', true).first();
10654 this.headEl.remove();
10655 this.headEl = table.createChild(header, this.bodyEl, false);
10657 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10658 e.on('click', this.sort, this);
10661 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10662 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10667 onHiddenChange : function(colModel, colIndex, hidden)
10670 this.cm.setHidden()
10671 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10672 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10674 this.CSS.updateRule(thSelector, "display", "");
10675 this.CSS.updateRule(tdSelector, "display", "");
10678 this.CSS.updateRule(thSelector, "display", "none");
10679 this.CSS.updateRule(tdSelector, "display", "none");
10682 // onload calls initCSS()
10683 this.onHeaderChange();
10687 setColumnWidth: function(col_index, width)
10689 // width = "md-2 xs-2..."
10690 if(!this.colModel.config[col_index]) {
10694 var w = width.split(" ");
10696 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10698 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10701 for(var j = 0; j < w.length; j++) {
10707 var size_cls = w[j].split("-");
10709 if(!Number.isInteger(size_cls[1] * 1)) {
10713 if(!this.colModel.config[col_index][size_cls[0]]) {
10717 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10721 h_row[0].classList.replace(
10722 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10723 "col-"+size_cls[0]+"-"+size_cls[1]
10726 for(var i = 0; i < rows.length; i++) {
10728 var size_cls = w[j].split("-");
10730 if(!Number.isInteger(size_cls[1] * 1)) {
10734 if(!this.colModel.config[col_index][size_cls[0]]) {
10738 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10742 rows[i].classList.replace(
10743 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10744 "col-"+size_cls[0]+"-"+size_cls[1]
10748 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10753 // currently only used to find the split on drag..
10754 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10759 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10760 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10769 * @class Roo.bootstrap.TableCell
10770 * @extends Roo.bootstrap.Component
10771 * @children Roo.bootstrap.Component
10772 * @parent Roo.bootstrap.TableRow
10773 * Bootstrap TableCell class
10775 * @cfg {String} html cell contain text
10776 * @cfg {String} cls cell class
10777 * @cfg {String} tag cell tag (td|th) default td
10778 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10779 * @cfg {String} align Aligns the content in a cell
10780 * @cfg {String} axis Categorizes cells
10781 * @cfg {String} bgcolor Specifies the background color of a cell
10782 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10783 * @cfg {Number} colspan Specifies the number of columns a cell should span
10784 * @cfg {String} headers Specifies one or more header cells a cell is related to
10785 * @cfg {Number} height Sets the height of a cell
10786 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10787 * @cfg {Number} rowspan Sets the number of rows a cell should span
10788 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10789 * @cfg {String} valign Vertical aligns the content in a cell
10790 * @cfg {Number} width Specifies the width of a cell
10793 * Create a new TableCell
10794 * @param {Object} config The config object
10797 Roo.bootstrap.TableCell = function(config){
10798 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10801 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10821 getAutoCreate : function(){
10822 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10829 cfg.tag = this.tag;
10842 cfg.align=this.align
10847 if (this.bgcolor) {
10848 cfg.bgcolor=this.bgcolor
10850 if (this.charoff) {
10851 cfg.charoff=this.charoff
10853 if (this.colspan) {
10854 cfg.colspan=this.colspan
10856 if (this.headers) {
10857 cfg.headers=this.headers
10860 cfg.height=this.height
10863 cfg.nowrap=this.nowrap
10865 if (this.rowspan) {
10866 cfg.rowspan=this.rowspan
10869 cfg.scope=this.scope
10872 cfg.valign=this.valign
10875 cfg.width=this.width
10894 * @class Roo.bootstrap.TableRow
10895 * @extends Roo.bootstrap.Component
10896 * @children Roo.bootstrap.TableCell
10897 * @parent Roo.bootstrap.TableBody
10898 * Bootstrap TableRow class
10899 * @cfg {String} cls row class
10900 * @cfg {String} align Aligns the content in a table row
10901 * @cfg {String} bgcolor Specifies a background color for a table row
10902 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10903 * @cfg {String} valign Vertical aligns the content in a table row
10906 * Create a new TableRow
10907 * @param {Object} config The config object
10910 Roo.bootstrap.TableRow = function(config){
10911 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10914 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10922 getAutoCreate : function(){
10923 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10930 cfg.cls = this.cls;
10933 cfg.align = this.align;
10936 cfg.bgcolor = this.bgcolor;
10939 cfg.charoff = this.charoff;
10942 cfg.valign = this.valign;
10960 * @class Roo.bootstrap.TableBody
10961 * @extends Roo.bootstrap.Component
10962 * @children Roo.bootstrap.TableRow
10963 * @parent Roo.bootstrap.Table
10964 * Bootstrap TableBody class
10965 * @cfg {String} cls element class
10966 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10967 * @cfg {String} align Aligns the content inside the element
10968 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10969 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10972 * Create a new TableBody
10973 * @param {Object} config The config object
10976 Roo.bootstrap.TableBody = function(config){
10977 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10980 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10988 getAutoCreate : function(){
10989 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10999 cfg.tag = this.tag;
11003 cfg.align = this.align;
11006 cfg.charoff = this.charoff;
11009 cfg.valign = this.valign;
11016 // initEvents : function()
11019 // if(!this.store){
11023 // this.store = Roo.factory(this.store, Roo.data);
11024 // this.store.on('load', this.onLoad, this);
11026 // this.store.load();
11030 // onLoad: function ()
11032 // this.fireEvent('load', this);
11042 * Ext JS Library 1.1.1
11043 * Copyright(c) 2006-2007, Ext JS, LLC.
11045 * Originally Released Under LGPL - original licence link has changed is not relivant.
11048 * <script type="text/javascript">
11051 // as we use this in bootstrap.
11052 Roo.namespace('Roo.form');
11054 * @class Roo.form.Action
11055 * Internal Class used to handle form actions
11057 * @param {Roo.form.BasicForm} el The form element or its id
11058 * @param {Object} config Configuration options
11063 // define the action interface
11064 Roo.form.Action = function(form, options){
11066 this.options = options || {};
11069 * Client Validation Failed
11072 Roo.form.Action.CLIENT_INVALID = 'client';
11074 * Server Validation Failed
11077 Roo.form.Action.SERVER_INVALID = 'server';
11079 * Connect to Server Failed
11082 Roo.form.Action.CONNECT_FAILURE = 'connect';
11084 * Reading Data from Server Failed
11087 Roo.form.Action.LOAD_FAILURE = 'load';
11089 Roo.form.Action.prototype = {
11091 failureType : undefined,
11092 response : undefined,
11093 result : undefined,
11095 // interface method
11096 run : function(options){
11100 // interface method
11101 success : function(response){
11105 // interface method
11106 handleResponse : function(response){
11110 // default connection failure
11111 failure : function(response){
11113 this.response = response;
11114 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11115 this.form.afterAction(this, false);
11118 processResponse : function(response){
11119 this.response = response;
11120 if(!response.responseText){
11123 this.result = this.handleResponse(response);
11124 return this.result;
11127 // utility functions used internally
11128 getUrl : function(appendParams){
11129 var url = this.options.url || this.form.url || this.form.el.dom.action;
11131 var p = this.getParams();
11133 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11139 getMethod : function(){
11140 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11143 getParams : function(){
11144 var bp = this.form.baseParams;
11145 var p = this.options.params;
11147 if(typeof p == "object"){
11148 p = Roo.urlEncode(Roo.applyIf(p, bp));
11149 }else if(typeof p == 'string' && bp){
11150 p += '&' + Roo.urlEncode(bp);
11153 p = Roo.urlEncode(bp);
11158 createCallback : function(){
11160 success: this.success,
11161 failure: this.failure,
11163 timeout: (this.form.timeout*1000),
11164 upload: this.form.fileUpload ? this.success : undefined
11169 Roo.form.Action.Submit = function(form, options){
11170 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11173 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11176 haveProgress : false,
11177 uploadComplete : false,
11179 // uploadProgress indicator.
11180 uploadProgress : function()
11182 if (!this.form.progressUrl) {
11186 if (!this.haveProgress) {
11187 Roo.MessageBox.progress("Uploading", "Uploading");
11189 if (this.uploadComplete) {
11190 Roo.MessageBox.hide();
11194 this.haveProgress = true;
11196 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11198 var c = new Roo.data.Connection();
11200 url : this.form.progressUrl,
11205 success : function(req){
11206 //console.log(data);
11210 rdata = Roo.decode(req.responseText)
11212 Roo.log("Invalid data from server..");
11216 if (!rdata || !rdata.success) {
11218 Roo.MessageBox.alert(Roo.encode(rdata));
11221 var data = rdata.data;
11223 if (this.uploadComplete) {
11224 Roo.MessageBox.hide();
11229 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11230 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11233 this.uploadProgress.defer(2000,this);
11236 failure: function(data) {
11237 Roo.log('progress url failed ');
11248 // run get Values on the form, so it syncs any secondary forms.
11249 this.form.getValues();
11251 var o = this.options;
11252 var method = this.getMethod();
11253 var isPost = method == 'POST';
11254 if(o.clientValidation === false || this.form.isValid()){
11256 if (this.form.progressUrl) {
11257 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11258 (new Date() * 1) + '' + Math.random());
11263 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11264 form:this.form.el.dom,
11265 url:this.getUrl(!isPost),
11267 params:isPost ? this.getParams() : null,
11268 isUpload: this.form.fileUpload,
11269 formData : this.form.formData
11272 this.uploadProgress();
11274 }else if (o.clientValidation !== false){ // client validation failed
11275 this.failureType = Roo.form.Action.CLIENT_INVALID;
11276 this.form.afterAction(this, false);
11280 success : function(response)
11282 this.uploadComplete= true;
11283 if (this.haveProgress) {
11284 Roo.MessageBox.hide();
11288 var result = this.processResponse(response);
11289 if(result === true || result.success){
11290 this.form.afterAction(this, true);
11294 this.form.markInvalid(result.errors);
11295 this.failureType = Roo.form.Action.SERVER_INVALID;
11297 this.form.afterAction(this, false);
11299 failure : function(response)
11301 this.uploadComplete= true;
11302 if (this.haveProgress) {
11303 Roo.MessageBox.hide();
11306 this.response = response;
11307 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11308 this.form.afterAction(this, false);
11311 handleResponse : function(response){
11312 if(this.form.errorReader){
11313 var rs = this.form.errorReader.read(response);
11316 for(var i = 0, len = rs.records.length; i < len; i++) {
11317 var r = rs.records[i];
11318 errors[i] = r.data;
11321 if(errors.length < 1){
11325 success : rs.success,
11331 ret = Roo.decode(response.responseText);
11335 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11345 Roo.form.Action.Load = function(form, options){
11346 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11347 this.reader = this.form.reader;
11350 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11355 Roo.Ajax.request(Roo.apply(
11356 this.createCallback(), {
11357 method:this.getMethod(),
11358 url:this.getUrl(false),
11359 params:this.getParams()
11363 success : function(response){
11365 var result = this.processResponse(response);
11366 if(result === true || !result.success || !result.data){
11367 this.failureType = Roo.form.Action.LOAD_FAILURE;
11368 this.form.afterAction(this, false);
11371 this.form.clearInvalid();
11372 this.form.setValues(result.data);
11373 this.form.afterAction(this, true);
11376 handleResponse : function(response){
11377 if(this.form.reader){
11378 var rs = this.form.reader.read(response);
11379 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11381 success : rs.success,
11385 return Roo.decode(response.responseText);
11389 Roo.form.Action.ACTION_TYPES = {
11390 'load' : Roo.form.Action.Load,
11391 'submit' : Roo.form.Action.Submit
11400 * @class Roo.bootstrap.form.Form
11401 * @extends Roo.bootstrap.Component
11402 * @children Roo.bootstrap.Component
11403 * Bootstrap Form class
11404 * @cfg {String} method GET | POST (default POST)
11405 * @cfg {String} labelAlign top | left (default top)
11406 * @cfg {String} align left | right - for navbars
11407 * @cfg {Boolean} loadMask load mask when submit (default true)
11411 * Create a new Form
11412 * @param {Object} config The config object
11416 Roo.bootstrap.form.Form = function(config){
11418 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11420 Roo.bootstrap.form.Form.popover.apply();
11424 * @event clientvalidation
11425 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11426 * @param {Form} this
11427 * @param {Boolean} valid true if the form has passed client-side validation
11429 clientvalidation: true,
11431 * @event beforeaction
11432 * Fires before any action is performed. Return false to cancel the action.
11433 * @param {Form} this
11434 * @param {Action} action The action to be performed
11436 beforeaction: true,
11438 * @event actionfailed
11439 * Fires when an action fails.
11440 * @param {Form} this
11441 * @param {Action} action The action that failed
11443 actionfailed : true,
11445 * @event actioncomplete
11446 * Fires when an action is completed.
11447 * @param {Form} this
11448 * @param {Action} action The action that completed
11450 actioncomplete : true
11454 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11457 * @cfg {String} method
11458 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11462 * @cfg {String} url
11463 * The URL to use for form actions if one isn't supplied in the action options.
11466 * @cfg {Boolean} fileUpload
11467 * Set to true if this form is a file upload.
11471 * @cfg {Object} baseParams
11472 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11476 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11480 * @cfg {Sting} align (left|right) for navbar forms
11485 activeAction : null,
11488 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11489 * element by passing it or its id or mask the form itself by passing in true.
11492 waitMsgTarget : false,
11497 * @cfg {Boolean} errorMask (true|false) default false
11502 * @cfg {Number} maskOffset Default 100
11507 * @cfg {Boolean} maskBody
11511 getAutoCreate : function(){
11515 method : this.method || 'POST',
11516 id : this.id || Roo.id(),
11519 if (this.parent().xtype.match(/^Nav/)) {
11520 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11524 if (this.labelAlign == 'left' ) {
11525 cfg.cls += ' form-horizontal';
11531 initEvents : function()
11533 this.el.on('submit', this.onSubmit, this);
11534 // this was added as random key presses on the form where triggering form submit.
11535 this.el.on('keypress', function(e) {
11536 if (e.getCharCode() != 13) {
11539 // we might need to allow it for textareas.. and some other items.
11540 // check e.getTarget().
11542 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11546 Roo.log("keypress blocked");
11548 e.preventDefault();
11554 onSubmit : function(e){
11559 * Returns true if client-side validation on the form is successful.
11562 isValid : function(){
11563 var items = this.getItems();
11565 var target = false;
11567 items.each(function(f){
11573 Roo.log('invalid field: ' + f.name);
11577 if(!target && f.el.isVisible(true)){
11583 if(this.errorMask && !valid){
11584 Roo.bootstrap.form.Form.popover.mask(this, target);
11591 * Returns true if any fields in this form have changed since their original load.
11594 isDirty : function(){
11596 var items = this.getItems();
11597 items.each(function(f){
11607 * Performs a predefined action (submit or load) or custom actions you define on this form.
11608 * @param {String} actionName The name of the action type
11609 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11610 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11611 * accept other config options):
11613 Property Type Description
11614 ---------------- --------------- ----------------------------------------------------------------------------------
11615 url String The url for the action (defaults to the form's url)
11616 method String The form method to use (defaults to the form's method, or POST if not defined)
11617 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11618 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11619 validate the form on the client (defaults to false)
11621 * @return {BasicForm} this
11623 doAction : function(action, options){
11624 if(typeof action == 'string'){
11625 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11627 if(this.fireEvent('beforeaction', this, action) !== false){
11628 this.beforeAction(action);
11629 action.run.defer(100, action);
11635 beforeAction : function(action){
11636 var o = action.options;
11641 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11643 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11646 // not really supported yet.. ??
11648 //if(this.waitMsgTarget === true){
11649 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11650 //}else if(this.waitMsgTarget){
11651 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11652 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11654 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11660 afterAction : function(action, success){
11661 this.activeAction = null;
11662 var o = action.options;
11667 Roo.get(document.body).unmask();
11673 //if(this.waitMsgTarget === true){
11674 // this.el.unmask();
11675 //}else if(this.waitMsgTarget){
11676 // this.waitMsgTarget.unmask();
11678 // Roo.MessageBox.updateProgress(1);
11679 // Roo.MessageBox.hide();
11686 Roo.callback(o.success, o.scope, [this, action]);
11687 this.fireEvent('actioncomplete', this, action);
11691 // failure condition..
11692 // we have a scenario where updates need confirming.
11693 // eg. if a locking scenario exists..
11694 // we look for { errors : { needs_confirm : true }} in the response.
11696 (typeof(action.result) != 'undefined') &&
11697 (typeof(action.result.errors) != 'undefined') &&
11698 (typeof(action.result.errors.needs_confirm) != 'undefined')
11701 Roo.log("not supported yet");
11704 Roo.MessageBox.confirm(
11705 "Change requires confirmation",
11706 action.result.errorMsg,
11711 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11721 Roo.callback(o.failure, o.scope, [this, action]);
11722 // show an error message if no failed handler is set..
11723 if (!this.hasListener('actionfailed')) {
11724 Roo.log("need to add dialog support");
11726 Roo.MessageBox.alert("Error",
11727 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11728 action.result.errorMsg :
11729 "Saving Failed, please check your entries or try again"
11734 this.fireEvent('actionfailed', this, action);
11739 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11740 * @param {String} id The value to search for
11743 findField : function(id){
11744 var items = this.getItems();
11745 var field = items.get(id);
11747 items.each(function(f){
11748 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11755 return field || null;
11758 * Mark fields in this form invalid in bulk.
11759 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11760 * @return {BasicForm} this
11762 markInvalid : function(errors){
11763 if(errors instanceof Array){
11764 for(var i = 0, len = errors.length; i < len; i++){
11765 var fieldError = errors[i];
11766 var f = this.findField(fieldError.id);
11768 f.markInvalid(fieldError.msg);
11774 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11775 field.markInvalid(errors[id]);
11779 //Roo.each(this.childForms || [], function (f) {
11780 // f.markInvalid(errors);
11787 * Set values for fields in this form in bulk.
11788 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11789 * @return {BasicForm} this
11791 setValues : function(values){
11792 if(values instanceof Array){ // array of objects
11793 for(var i = 0, len = values.length; i < len; i++){
11795 var f = this.findField(v.id);
11797 f.setValue(v.value);
11798 if(this.trackResetOnLoad){
11799 f.originalValue = f.getValue();
11803 }else{ // object hash
11806 if(typeof values[id] != 'function' && (field = this.findField(id))){
11808 if (field.setFromData &&
11809 field.valueField &&
11810 field.displayField &&
11811 // combos' with local stores can
11812 // be queried via setValue()
11813 // to set their value..
11814 (field.store && !field.store.isLocal)
11818 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11819 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11820 field.setFromData(sd);
11822 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11824 field.setFromData(values);
11827 field.setValue(values[id]);
11831 if(this.trackResetOnLoad){
11832 field.originalValue = field.getValue();
11838 //Roo.each(this.childForms || [], function (f) {
11839 // f.setValues(values);
11846 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11847 * they are returned as an array.
11848 * @param {Boolean} asString
11851 getValues : function(asString){
11852 //if (this.childForms) {
11853 // copy values from the child forms
11854 // Roo.each(this.childForms, function (f) {
11855 // this.setValues(f.getValues());
11861 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11862 if(asString === true){
11865 return Roo.urlDecode(fs);
11869 * Returns the fields in this form as an object with key/value pairs.
11870 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11873 getFieldValues : function(with_hidden)
11875 var items = this.getItems();
11877 items.each(function(f){
11879 if (!f.getName()) {
11883 var v = f.getValue();
11885 if (f.inputType =='radio') {
11886 if (typeof(ret[f.getName()]) == 'undefined') {
11887 ret[f.getName()] = ''; // empty..
11890 if (!f.el.dom.checked) {
11894 v = f.el.dom.value;
11898 if(f.xtype == 'MoneyField'){
11899 ret[f.currencyName] = f.getCurrency();
11902 // not sure if this supported any more..
11903 if ((typeof(v) == 'object') && f.getRawValue) {
11904 v = f.getRawValue() ; // dates..
11906 // combo boxes where name != hiddenName...
11907 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11908 ret[f.name] = f.getRawValue();
11910 ret[f.getName()] = v;
11917 * Clears all invalid messages in this form.
11918 * @return {BasicForm} this
11920 clearInvalid : function(){
11921 var items = this.getItems();
11923 items.each(function(f){
11931 * Resets this form.
11932 * @return {BasicForm} this
11934 reset : function(){
11935 var items = this.getItems();
11936 items.each(function(f){
11940 Roo.each(this.childForms || [], function (f) {
11948 getItems : function()
11950 var r=new Roo.util.MixedCollection(false, function(o){
11951 return o.id || (o.id = Roo.id());
11953 var iter = function(el) {
11960 Roo.each(el.items,function(e) {
11969 hideFields : function(items)
11971 Roo.each(items, function(i){
11973 var f = this.findField(i);
11984 showFields : function(items)
11986 Roo.each(items, function(i){
11988 var f = this.findField(i);
12001 Roo.apply(Roo.bootstrap.form.Form, {
12017 intervalID : false,
12023 if(this.isApplied){
12028 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12029 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12030 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12031 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12034 this.maskEl.top.enableDisplayMode("block");
12035 this.maskEl.left.enableDisplayMode("block");
12036 this.maskEl.bottom.enableDisplayMode("block");
12037 this.maskEl.right.enableDisplayMode("block");
12039 this.toolTip = new Roo.bootstrap.Tooltip({
12040 cls : 'roo-form-error-popover',
12042 'left' : ['r-l', [-2,0], 'right'],
12043 'right' : ['l-r', [2,0], 'left'],
12044 'bottom' : ['tl-bl', [0,2], 'top'],
12045 'top' : [ 'bl-tl', [0,-2], 'bottom']
12049 this.toolTip.render(Roo.get(document.body));
12051 this.toolTip.el.enableDisplayMode("block");
12053 Roo.get(document.body).on('click', function(){
12057 Roo.get(document.body).on('touchstart', function(){
12061 this.isApplied = true
12064 mask : function(form, target)
12068 this.target = target;
12070 if(!this.form.errorMask || !target.el){
12074 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12076 Roo.log(scrollable);
12078 var ot = this.target.el.calcOffsetsTo(scrollable);
12080 var scrollTo = ot[1] - this.form.maskOffset;
12082 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12084 scrollable.scrollTo('top', scrollTo);
12086 var box = this.target.el.getBox();
12088 var zIndex = Roo.bootstrap.Modal.zIndex++;
12091 this.maskEl.top.setStyle('position', 'absolute');
12092 this.maskEl.top.setStyle('z-index', zIndex);
12093 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12094 this.maskEl.top.setLeft(0);
12095 this.maskEl.top.setTop(0);
12096 this.maskEl.top.show();
12098 this.maskEl.left.setStyle('position', 'absolute');
12099 this.maskEl.left.setStyle('z-index', zIndex);
12100 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12101 this.maskEl.left.setLeft(0);
12102 this.maskEl.left.setTop(box.y - this.padding);
12103 this.maskEl.left.show();
12105 this.maskEl.bottom.setStyle('position', 'absolute');
12106 this.maskEl.bottom.setStyle('z-index', zIndex);
12107 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12108 this.maskEl.bottom.setLeft(0);
12109 this.maskEl.bottom.setTop(box.bottom + this.padding);
12110 this.maskEl.bottom.show();
12112 this.maskEl.right.setStyle('position', 'absolute');
12113 this.maskEl.right.setStyle('z-index', zIndex);
12114 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12115 this.maskEl.right.setLeft(box.right + this.padding);
12116 this.maskEl.right.setTop(box.y - this.padding);
12117 this.maskEl.right.show();
12119 this.toolTip.bindEl = this.target.el;
12121 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12123 var tip = this.target.blankText;
12125 if(this.target.getValue() !== '' ) {
12127 if (this.target.invalidText.length) {
12128 tip = this.target.invalidText;
12129 } else if (this.target.regexText.length){
12130 tip = this.target.regexText;
12134 this.toolTip.show(tip);
12136 this.intervalID = window.setInterval(function() {
12137 Roo.bootstrap.form.Form.popover.unmask();
12140 window.onwheel = function(){ return false;};
12142 (function(){ this.isMasked = true; }).defer(500, this);
12146 unmask : function()
12148 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12152 this.maskEl.top.setStyle('position', 'absolute');
12153 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12154 this.maskEl.top.hide();
12156 this.maskEl.left.setStyle('position', 'absolute');
12157 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12158 this.maskEl.left.hide();
12160 this.maskEl.bottom.setStyle('position', 'absolute');
12161 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12162 this.maskEl.bottom.hide();
12164 this.maskEl.right.setStyle('position', 'absolute');
12165 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12166 this.maskEl.right.hide();
12168 this.toolTip.hide();
12170 this.toolTip.el.hide();
12172 window.onwheel = function(){ return true;};
12174 if(this.intervalID){
12175 window.clearInterval(this.intervalID);
12176 this.intervalID = false;
12179 this.isMasked = false;
12189 * Ext JS Library 1.1.1
12190 * Copyright(c) 2006-2007, Ext JS, LLC.
12192 * Originally Released Under LGPL - original licence link has changed is not relivant.
12195 * <script type="text/javascript">
12198 * @class Roo.form.VTypes
12199 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12202 Roo.form.VTypes = function(){
12203 // closure these in so they are only created once.
12204 var alpha = /^[a-zA-Z_]+$/;
12205 var alphanum = /^[a-zA-Z0-9_]+$/;
12206 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12207 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12209 // All these messages and functions are configurable
12212 * The function used to validate email addresses
12213 * @param {String} value The email address
12215 email : function(v){
12216 return email.test(v);
12219 * The error text to display when the email validation function returns false
12222 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12224 * The keystroke filter mask to be applied on email input
12227 emailMask : /[a-z0-9_\.\-@]/i,
12230 * The function used to validate URLs
12231 * @param {String} value The URL
12234 return url.test(v);
12237 * The error text to display when the url validation function returns false
12240 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12243 * The function used to validate alpha values
12244 * @param {String} value The value
12246 alpha : function(v){
12247 return alpha.test(v);
12250 * The error text to display when the alpha validation function returns false
12253 alphaText : 'This field should only contain letters and _',
12255 * The keystroke filter mask to be applied on alpha input
12258 alphaMask : /[a-z_]/i,
12261 * The function used to validate alphanumeric values
12262 * @param {String} value The value
12264 alphanum : function(v){
12265 return alphanum.test(v);
12268 * The error text to display when the alphanumeric validation function returns false
12271 alphanumText : 'This field should only contain letters, numbers and _',
12273 * The keystroke filter mask to be applied on alphanumeric input
12276 alphanumMask : /[a-z0-9_]/i
12286 * @class Roo.bootstrap.form.Input
12287 * @extends Roo.bootstrap.Component
12288 * Bootstrap Input class
12289 * @cfg {Boolean} disabled is it disabled
12290 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12291 * @cfg {String} name name of the input
12292 * @cfg {string} fieldLabel - the label associated
12293 * @cfg {string} placeholder - placeholder to put in text.
12294 * @cfg {string} before - input group add on before
12295 * @cfg {string} after - input group add on after
12296 * @cfg {string} size - (lg|sm) or leave empty..
12297 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12298 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12299 * @cfg {Number} md colspan out of 12 for computer-sized screens
12300 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12301 * @cfg {string} value default value of the input
12302 * @cfg {Number} labelWidth set the width of label
12303 * @cfg {Number} labellg set the width of label (1-12)
12304 * @cfg {Number} labelmd set the width of label (1-12)
12305 * @cfg {Number} labelsm set the width of label (1-12)
12306 * @cfg {Number} labelxs set the width of label (1-12)
12307 * @cfg {String} labelAlign (top|left)
12308 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12309 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12310 * @cfg {String} indicatorpos (left|right) default left
12311 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12312 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12313 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12314 * @cfg {Roo.bootstrap.Button} before Button to show before
12315 * @cfg {Roo.bootstrap.Button} afterButton to show before
12316 * @cfg {String} align (left|center|right) Default left
12317 * @cfg {Boolean} forceFeedback (true|false) Default false
12320 * Create a new Input
12321 * @param {Object} config The config object
12324 Roo.bootstrap.form.Input = function(config){
12326 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12331 * Fires when this field receives input focus.
12332 * @param {Roo.form.Field} this
12337 * Fires when this field loses input focus.
12338 * @param {Roo.form.Field} this
12342 * @event specialkey
12343 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12344 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12345 * @param {Roo.form.Field} this
12346 * @param {Roo.EventObject} e The event object
12351 * Fires just before the field blurs if the field value has changed.
12352 * @param {Roo.form.Field} this
12353 * @param {Mixed} newValue The new value
12354 * @param {Mixed} oldValue The original value
12359 * Fires after the field has been marked as invalid.
12360 * @param {Roo.form.Field} this
12361 * @param {String} msg The validation message
12366 * Fires after the field has been validated with no errors.
12367 * @param {Roo.form.Field} this
12372 * Fires after the key up
12373 * @param {Roo.form.Field} this
12374 * @param {Roo.EventObject} e The event Object
12379 * Fires after the user pastes into input
12380 * @param {Roo.form.Field} this
12381 * @param {Roo.EventObject} e The event Object
12387 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12389 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12390 automatic validation (defaults to "keyup").
12392 validationEvent : "keyup",
12394 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12396 validateOnBlur : true,
12398 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12400 validationDelay : 250,
12402 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12404 focusClass : "x-form-focus", // not needed???
12408 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12410 invalidClass : "has-warning",
12413 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12415 validClass : "has-success",
12418 * @cfg {Boolean} hasFeedback (true|false) default true
12420 hasFeedback : true,
12423 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12425 invalidFeedbackClass : "glyphicon-warning-sign",
12428 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12430 validFeedbackClass : "glyphicon-ok",
12433 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12435 selectOnFocus : false,
12438 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12442 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12447 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12449 disableKeyFilter : false,
12452 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12456 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12460 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12462 blankText : "Please complete this mandatory field",
12465 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12469 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12471 maxLength : Number.MAX_VALUE,
12473 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12475 minLengthText : "The minimum length for this field is {0}",
12477 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12479 maxLengthText : "The maximum length for this field is {0}",
12483 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12484 * If available, this function will be called only after the basic validators all return true, and will be passed the
12485 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12489 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12490 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12491 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12495 * @cfg {String} regexText -- Depricated - use Invalid Text
12500 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12506 autocomplete: false,
12510 inputType : 'text',
12513 placeholder: false,
12518 preventMark: false,
12519 isFormField : true,
12522 labelAlign : false,
12525 formatedValue : false,
12526 forceFeedback : false,
12528 indicatorpos : 'left',
12538 parentLabelAlign : function()
12541 while (parent.parent()) {
12542 parent = parent.parent();
12543 if (typeof(parent.labelAlign) !='undefined') {
12544 return parent.labelAlign;
12551 getAutoCreate : function()
12553 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12559 if(this.inputType != 'hidden'){
12560 cfg.cls = 'form-group' //input-group
12566 type : this.inputType,
12567 value : this.value,
12568 cls : 'form-control',
12569 placeholder : this.placeholder || '',
12570 autocomplete : this.autocomplete || 'new-password'
12572 if (this.inputType == 'file') {
12573 input.style = 'overflow:hidden'; // why not in CSS?
12576 if(this.capture.length){
12577 input.capture = this.capture;
12580 if(this.accept.length){
12581 input.accept = this.accept + "/*";
12585 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12589 input.maxLength = this.maxLength;
12592 if (this.disabled) {
12593 input.disabled=true;
12596 if (this.readOnly) {
12597 input.readonly=true;
12601 input.name = this.name;
12605 input.cls += ' input-' + this.size;
12609 ['xs','sm','md','lg'].map(function(size){
12610 if (settings[size]) {
12611 cfg.cls += ' col-' + size + '-' + settings[size];
12615 var inputblock = input;
12619 cls: 'glyphicon form-control-feedback'
12622 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12625 cls : 'has-feedback',
12633 if (this.before || this.after) {
12636 cls : 'input-group',
12640 if (this.before && typeof(this.before) == 'string') {
12642 inputblock.cn.push({
12644 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12648 if (this.before && typeof(this.before) == 'object') {
12649 this.before = Roo.factory(this.before);
12651 inputblock.cn.push({
12653 cls : 'roo-input-before input-group-prepend input-group-' +
12654 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12658 inputblock.cn.push(input);
12660 if (this.after && typeof(this.after) == 'string') {
12661 inputblock.cn.push({
12663 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12667 if (this.after && typeof(this.after) == 'object') {
12668 this.after = Roo.factory(this.after);
12670 inputblock.cn.push({
12672 cls : 'roo-input-after input-group-append input-group-' +
12673 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12677 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12678 inputblock.cls += ' has-feedback';
12679 inputblock.cn.push(feedback);
12684 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12685 tooltip : 'This field is required'
12687 if (this.allowBlank ) {
12688 indicator.style = this.allowBlank ? ' display:none' : '';
12690 if (align ==='left' && this.fieldLabel.length) {
12692 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12699 cls : 'control-label col-form-label',
12700 html : this.fieldLabel
12711 var labelCfg = cfg.cn[1];
12712 var contentCfg = cfg.cn[2];
12714 if(this.indicatorpos == 'right'){
12719 cls : 'control-label col-form-label',
12723 html : this.fieldLabel
12737 labelCfg = cfg.cn[0];
12738 contentCfg = cfg.cn[1];
12742 if(this.labelWidth > 12){
12743 labelCfg.style = "width: " + this.labelWidth + 'px';
12746 if(this.labelWidth < 13 && this.labelmd == 0){
12747 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12750 if(this.labellg > 0){
12751 labelCfg.cls += ' col-lg-' + this.labellg;
12752 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12755 if(this.labelmd > 0){
12756 labelCfg.cls += ' col-md-' + this.labelmd;
12757 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12760 if(this.labelsm > 0){
12761 labelCfg.cls += ' col-sm-' + this.labelsm;
12762 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12765 if(this.labelxs > 0){
12766 labelCfg.cls += ' col-xs-' + this.labelxs;
12767 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12771 } else if ( this.fieldLabel.length) {
12778 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12779 tooltip : 'This field is required',
12780 style : this.allowBlank ? ' display:none' : ''
12784 //cls : 'input-group-addon',
12785 html : this.fieldLabel
12793 if(this.indicatorpos == 'right'){
12798 //cls : 'input-group-addon',
12799 html : this.fieldLabel
12804 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12805 tooltip : 'This field is required',
12806 style : this.allowBlank ? ' display:none' : ''
12826 if (this.parentType === 'Navbar' && this.parent().bar) {
12827 cfg.cls += ' navbar-form';
12830 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12831 // on BS4 we do this only if not form
12832 cfg.cls += ' navbar-form';
12840 * return the real input element.
12842 inputEl: function ()
12844 return this.el.select('input.form-control',true).first();
12847 tooltipEl : function()
12849 return this.inputEl();
12852 indicatorEl : function()
12854 if (Roo.bootstrap.version == 4) {
12855 return false; // not enabled in v4 yet.
12858 var indicator = this.el.select('i.roo-required-indicator',true).first();
12868 setDisabled : function(v)
12870 var i = this.inputEl().dom;
12872 i.removeAttribute('disabled');
12876 i.setAttribute('disabled','true');
12878 initEvents : function()
12881 this.inputEl().on("keydown" , this.fireKey, this);
12882 this.inputEl().on("focus", this.onFocus, this);
12883 this.inputEl().on("blur", this.onBlur, this);
12885 this.inputEl().relayEvent('keyup', this);
12886 this.inputEl().relayEvent('paste', this);
12888 this.indicator = this.indicatorEl();
12890 if(this.indicator){
12891 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12894 // reference to original value for reset
12895 this.originalValue = this.getValue();
12896 //Roo.form.TextField.superclass.initEvents.call(this);
12897 if(this.validationEvent == 'keyup'){
12898 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12899 this.inputEl().on('keyup', this.filterValidation, this);
12901 else if(this.validationEvent !== false){
12902 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12905 if(this.selectOnFocus){
12906 this.on("focus", this.preFocus, this);
12909 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12910 this.inputEl().on("keypress", this.filterKeys, this);
12912 this.inputEl().relayEvent('keypress', this);
12915 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12916 this.el.on("click", this.autoSize, this);
12919 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12920 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12923 if (typeof(this.before) == 'object') {
12924 this.before.render(this.el.select('.roo-input-before',true).first());
12926 if (typeof(this.after) == 'object') {
12927 this.after.render(this.el.select('.roo-input-after',true).first());
12930 this.inputEl().on('change', this.onChange, this);
12933 filterValidation : function(e){
12934 if(!e.isNavKeyPress()){
12935 this.validationTask.delay(this.validationDelay);
12939 * Validates the field value
12940 * @return {Boolean} True if the value is valid, else false
12942 validate : function(){
12943 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12944 if(this.disabled || this.validateValue(this.getRawValue())){
12949 this.markInvalid();
12955 * Validates a value according to the field's validation rules and marks the field as invalid
12956 * if the validation fails
12957 * @param {Mixed} value The value to validate
12958 * @return {Boolean} True if the value is valid, else false
12960 validateValue : function(value)
12962 if(this.getVisibilityEl().hasClass('hidden')){
12966 if(value.length < 1) { // if it's blank
12967 if(this.allowBlank){
12973 if(value.length < this.minLength){
12976 if(value.length > this.maxLength){
12980 var vt = Roo.form.VTypes;
12981 if(!vt[this.vtype](value, this)){
12985 if(typeof this.validator == "function"){
12986 var msg = this.validator(value);
12987 if (typeof(msg) == 'string') {
12988 this.invalidText = msg;
12995 if(this.regex && !this.regex.test(value)){
13003 fireKey : function(e){
13004 //Roo.log('field ' + e.getKey());
13005 if(e.isNavKeyPress()){
13006 this.fireEvent("specialkey", this, e);
13009 focus : function (selectText){
13011 this.inputEl().focus();
13012 if(selectText === true){
13013 this.inputEl().dom.select();
13019 onFocus : function(){
13020 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13021 // this.el.addClass(this.focusClass);
13023 if(!this.hasFocus){
13024 this.hasFocus = true;
13025 this.startValue = this.getValue();
13026 this.fireEvent("focus", this);
13030 beforeBlur : Roo.emptyFn,
13034 onBlur : function(){
13036 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13037 //this.el.removeClass(this.focusClass);
13039 this.hasFocus = false;
13040 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13043 var v = this.getValue();
13044 if(String(v) !== String(this.startValue)){
13045 this.fireEvent('change', this, v, this.startValue);
13047 this.fireEvent("blur", this);
13050 onChange : function(e)
13052 var v = this.getValue();
13053 if(String(v) !== String(this.startValue)){
13054 this.fireEvent('change', this, v, this.startValue);
13060 * Resets the current field value to the originally loaded value and clears any validation messages
13062 reset : function(){
13063 this.setValue(this.originalValue);
13067 * Returns the name of the field
13068 * @return {Mixed} name The name field
13070 getName: function(){
13074 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13075 * @return {Mixed} value The field value
13077 getValue : function(){
13079 var v = this.inputEl().getValue();
13084 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13085 * @return {Mixed} value The field value
13087 getRawValue : function(){
13088 var v = this.inputEl().getValue();
13094 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13095 * @param {Mixed} value The value to set
13097 setRawValue : function(v){
13098 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13101 selectText : function(start, end){
13102 var v = this.getRawValue();
13104 start = start === undefined ? 0 : start;
13105 end = end === undefined ? v.length : end;
13106 var d = this.inputEl().dom;
13107 if(d.setSelectionRange){
13108 d.setSelectionRange(start, end);
13109 }else if(d.createTextRange){
13110 var range = d.createTextRange();
13111 range.moveStart("character", start);
13112 range.moveEnd("character", v.length-end);
13119 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13120 * @param {Mixed} value The value to set
13122 setValue : function(v){
13125 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13131 processValue : function(value){
13132 if(this.stripCharsRe){
13133 var newValue = value.replace(this.stripCharsRe, '');
13134 if(newValue !== value){
13135 this.setRawValue(newValue);
13142 preFocus : function(){
13144 if(this.selectOnFocus){
13145 this.inputEl().dom.select();
13148 filterKeys : function(e){
13149 var k = e.getKey();
13150 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13153 var c = e.getCharCode(), cc = String.fromCharCode(c);
13154 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13157 if(!this.maskRe.test(cc)){
13162 * Clear any invalid styles/messages for this field
13164 clearInvalid : function(){
13166 if(!this.el || this.preventMark){ // not rendered
13171 this.el.removeClass([this.invalidClass, 'is-invalid']);
13173 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13175 var feedback = this.el.select('.form-control-feedback', true).first();
13178 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13183 if(this.indicator){
13184 this.indicator.removeClass('visible');
13185 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13188 this.fireEvent('valid', this);
13192 * Mark this field as valid
13194 markValid : function()
13196 if(!this.el || this.preventMark){ // not rendered...
13200 this.el.removeClass([this.invalidClass, this.validClass]);
13201 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13203 var feedback = this.el.select('.form-control-feedback', true).first();
13206 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13209 if(this.indicator){
13210 this.indicator.removeClass('visible');
13211 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13219 if(this.allowBlank && !this.getRawValue().length){
13222 if (Roo.bootstrap.version == 3) {
13223 this.el.addClass(this.validClass);
13225 this.inputEl().addClass('is-valid');
13228 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13230 var feedback = this.el.select('.form-control-feedback', true).first();
13233 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13234 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13239 this.fireEvent('valid', this);
13243 * Mark this field as invalid
13244 * @param {String} msg The validation message
13246 markInvalid : function(msg)
13248 if(!this.el || this.preventMark){ // not rendered
13252 this.el.removeClass([this.invalidClass, this.validClass]);
13253 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13255 var feedback = this.el.select('.form-control-feedback', true).first();
13258 this.el.select('.form-control-feedback', true).first().removeClass(
13259 [this.invalidFeedbackClass, this.validFeedbackClass]);
13266 if(this.allowBlank && !this.getRawValue().length){
13270 if(this.indicator){
13271 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13272 this.indicator.addClass('visible');
13274 if (Roo.bootstrap.version == 3) {
13275 this.el.addClass(this.invalidClass);
13277 this.inputEl().addClass('is-invalid');
13282 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13284 var feedback = this.el.select('.form-control-feedback', true).first();
13287 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13289 if(this.getValue().length || this.forceFeedback){
13290 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13297 this.fireEvent('invalid', this, msg);
13300 SafariOnKeyDown : function(event)
13302 // this is a workaround for a password hang bug on chrome/ webkit.
13303 if (this.inputEl().dom.type != 'password') {
13307 var isSelectAll = false;
13309 if(this.inputEl().dom.selectionEnd > 0){
13310 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13312 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13313 event.preventDefault();
13318 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13320 event.preventDefault();
13321 // this is very hacky as keydown always get's upper case.
13323 var cc = String.fromCharCode(event.getCharCode());
13324 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13328 adjustWidth : function(tag, w){
13329 tag = tag.toLowerCase();
13330 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13331 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13332 if(tag == 'input'){
13335 if(tag == 'textarea'){
13338 }else if(Roo.isOpera){
13339 if(tag == 'input'){
13342 if(tag == 'textarea'){
13350 setFieldLabel : function(v)
13352 if(!this.rendered){
13356 if(this.indicatorEl()){
13357 var ar = this.el.select('label > span',true);
13359 if (ar.elements.length) {
13360 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13361 this.fieldLabel = v;
13365 var br = this.el.select('label',true);
13367 if(br.elements.length) {
13368 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13369 this.fieldLabel = v;
13373 Roo.log('Cannot Found any of label > span || label in input');
13377 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13378 this.fieldLabel = v;
13393 * @class Roo.bootstrap.form.TextArea
13394 * @extends Roo.bootstrap.form.Input
13395 * Bootstrap TextArea class
13396 * @cfg {Number} cols Specifies the visible width of a text area
13397 * @cfg {Number} rows Specifies the visible number of lines in a text area
13398 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13399 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13400 * @cfg {string} html text
13403 * Create a new TextArea
13404 * @param {Object} config The config object
13407 Roo.bootstrap.form.TextArea = function(config){
13408 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13412 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13422 getAutoCreate : function(){
13424 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13430 if(this.inputType != 'hidden'){
13431 cfg.cls = 'form-group' //input-group
13439 value : this.value || '',
13440 html: this.html || '',
13441 cls : 'form-control',
13442 placeholder : this.placeholder || ''
13446 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13447 input.maxLength = this.maxLength;
13451 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13455 input.cols = this.cols;
13458 if (this.readOnly) {
13459 input.readonly = true;
13463 input.name = this.name;
13467 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13471 ['xs','sm','md','lg'].map(function(size){
13472 if (settings[size]) {
13473 cfg.cls += ' col-' + size + '-' + settings[size];
13477 var inputblock = input;
13479 if(this.hasFeedback && !this.allowBlank){
13483 cls: 'glyphicon form-control-feedback'
13487 cls : 'has-feedback',
13496 if (this.before || this.after) {
13499 cls : 'input-group',
13503 inputblock.cn.push({
13505 cls : 'input-group-addon',
13510 inputblock.cn.push(input);
13512 if(this.hasFeedback && !this.allowBlank){
13513 inputblock.cls += ' has-feedback';
13514 inputblock.cn.push(feedback);
13518 inputblock.cn.push({
13520 cls : 'input-group-addon',
13527 if (align ==='left' && this.fieldLabel.length) {
13532 cls : 'control-label',
13533 html : this.fieldLabel
13544 if(this.labelWidth > 12){
13545 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13548 if(this.labelWidth < 13 && this.labelmd == 0){
13549 this.labelmd = this.labelWidth;
13552 if(this.labellg > 0){
13553 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13554 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13557 if(this.labelmd > 0){
13558 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13559 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13562 if(this.labelsm > 0){
13563 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13564 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13567 if(this.labelxs > 0){
13568 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13569 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13572 } else if ( this.fieldLabel.length) {
13577 //cls : 'input-group-addon',
13578 html : this.fieldLabel
13596 if (this.disabled) {
13597 input.disabled=true;
13604 * return the real textarea element.
13606 inputEl: function ()
13608 return this.el.select('textarea.form-control',true).first();
13612 * Clear any invalid styles/messages for this field
13614 clearInvalid : function()
13617 if(!this.el || this.preventMark){ // not rendered
13621 var label = this.el.select('label', true).first();
13622 var icon = this.el.select('i.fa-star', true).first();
13627 this.el.removeClass( this.validClass);
13628 this.inputEl().removeClass('is-invalid');
13630 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13632 var feedback = this.el.select('.form-control-feedback', true).first();
13635 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13640 this.fireEvent('valid', this);
13644 * Mark this field as valid
13646 markValid : function()
13648 if(!this.el || this.preventMark){ // not rendered
13652 this.el.removeClass([this.invalidClass, this.validClass]);
13653 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13655 var feedback = this.el.select('.form-control-feedback', true).first();
13658 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13661 if(this.disabled || this.allowBlank){
13665 var label = this.el.select('label', true).first();
13666 var icon = this.el.select('i.fa-star', true).first();
13671 if (Roo.bootstrap.version == 3) {
13672 this.el.addClass(this.validClass);
13674 this.inputEl().addClass('is-valid');
13678 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13680 var feedback = this.el.select('.form-control-feedback', true).first();
13683 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13684 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13689 this.fireEvent('valid', this);
13693 * Mark this field as invalid
13694 * @param {String} msg The validation message
13696 markInvalid : function(msg)
13698 if(!this.el || this.preventMark){ // not rendered
13702 this.el.removeClass([this.invalidClass, this.validClass]);
13703 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13705 var feedback = this.el.select('.form-control-feedback', true).first();
13708 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13711 if(this.disabled || this.allowBlank){
13715 var label = this.el.select('label', true).first();
13716 var icon = this.el.select('i.fa-star', true).first();
13718 if(!this.getValue().length && label && !icon){
13719 this.el.createChild({
13721 cls : 'text-danger fa fa-lg fa-star',
13722 tooltip : 'This field is required',
13723 style : 'margin-right:5px;'
13727 if (Roo.bootstrap.version == 3) {
13728 this.el.addClass(this.invalidClass);
13730 this.inputEl().addClass('is-invalid');
13733 // fixme ... this may be depricated need to test..
13734 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13736 var feedback = this.el.select('.form-control-feedback', true).first();
13739 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13741 if(this.getValue().length || this.forceFeedback){
13742 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13749 this.fireEvent('invalid', this, msg);
13757 * trigger field - base class for combo..
13762 * @class Roo.bootstrap.form.TriggerField
13763 * @extends Roo.bootstrap.form.Input
13764 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13765 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13766 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13767 * for which you can provide a custom implementation. For example:
13769 var trigger = new Roo.bootstrap.form.TriggerField();
13770 trigger.onTriggerClick = myTriggerFn;
13771 trigger.applyTo('my-field');
13774 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13775 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13776 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13777 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13778 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13781 * Create a new TriggerField.
13782 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13783 * to the base TextField)
13785 Roo.bootstrap.form.TriggerField = function(config){
13786 this.mimicing = false;
13787 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13790 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13792 * @cfg {String} triggerClass A CSS class to apply to the trigger
13795 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13800 * @cfg {Boolean} removable (true|false) special filter default false
13804 /** @cfg {Boolean} grow @hide */
13805 /** @cfg {Number} growMin @hide */
13806 /** @cfg {Number} growMax @hide */
13812 autoSize: Roo.emptyFn,
13816 deferHeight : true,
13819 actionMode : 'wrap',
13824 getAutoCreate : function(){
13826 var align = this.labelAlign || this.parentLabelAlign();
13831 cls: 'form-group' //input-group
13838 type : this.inputType,
13839 cls : 'form-control',
13840 autocomplete: 'new-password',
13841 placeholder : this.placeholder || ''
13845 input.name = this.name;
13848 input.cls += ' input-' + this.size;
13851 if (this.disabled) {
13852 input.disabled=true;
13855 var inputblock = input;
13857 if(this.hasFeedback && !this.allowBlank){
13861 cls: 'glyphicon form-control-feedback'
13864 if(this.removable && !this.editable ){
13866 cls : 'has-feedback',
13872 cls : 'roo-combo-removable-btn close'
13879 cls : 'has-feedback',
13888 if(this.removable && !this.editable ){
13890 cls : 'roo-removable',
13896 cls : 'roo-combo-removable-btn close'
13903 if (this.before || this.after) {
13906 cls : 'input-group',
13910 inputblock.cn.push({
13912 cls : 'input-group-addon input-group-prepend input-group-text',
13917 inputblock.cn.push(input);
13919 if(this.hasFeedback && !this.allowBlank){
13920 inputblock.cls += ' has-feedback';
13921 inputblock.cn.push(feedback);
13925 inputblock.cn.push({
13927 cls : 'input-group-addon input-group-append input-group-text',
13936 var ibwrap = inputblock;
13941 cls: 'roo-select2-choices',
13945 cls: 'roo-select2-search-field',
13957 cls: 'roo-select2-container input-group',
13962 cls: 'form-hidden-field'
13968 if(!this.multiple && this.showToggleBtn){
13974 if (this.caret != false) {
13977 cls: 'fa fa-' + this.caret
13984 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13986 Roo.bootstrap.version == 3 ? caret : '',
13989 cls: 'combobox-clear',
14003 combobox.cls += ' roo-select2-container-multi';
14007 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
14008 tooltip : 'This field is required'
14010 if (Roo.bootstrap.version == 4) {
14013 style : 'display:none'
14018 if (align ==='left' && this.fieldLabel.length) {
14020 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
14027 cls : 'control-label',
14028 html : this.fieldLabel
14040 var labelCfg = cfg.cn[1];
14041 var contentCfg = cfg.cn[2];
14043 if(this.indicatorpos == 'right'){
14048 cls : 'control-label',
14052 html : this.fieldLabel
14066 labelCfg = cfg.cn[0];
14067 contentCfg = cfg.cn[1];
14070 if(this.labelWidth > 12){
14071 labelCfg.style = "width: " + this.labelWidth + 'px';
14074 if(this.labelWidth < 13 && this.labelmd == 0){
14075 this.labelmd = this.labelWidth;
14078 if(this.labellg > 0){
14079 labelCfg.cls += ' col-lg-' + this.labellg;
14080 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14083 if(this.labelmd > 0){
14084 labelCfg.cls += ' col-md-' + this.labelmd;
14085 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14088 if(this.labelsm > 0){
14089 labelCfg.cls += ' col-sm-' + this.labelsm;
14090 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14093 if(this.labelxs > 0){
14094 labelCfg.cls += ' col-xs-' + this.labelxs;
14095 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14098 } else if ( this.fieldLabel.length) {
14099 // Roo.log(" label");
14104 //cls : 'input-group-addon',
14105 html : this.fieldLabel
14113 if(this.indicatorpos == 'right'){
14121 html : this.fieldLabel
14135 // Roo.log(" no label && no align");
14142 ['xs','sm','md','lg'].map(function(size){
14143 if (settings[size]) {
14144 cfg.cls += ' col-' + size + '-' + settings[size];
14155 onResize : function(w, h){
14156 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14157 // if(typeof w == 'number'){
14158 // var x = w - this.trigger.getWidth();
14159 // this.inputEl().setWidth(this.adjustWidth('input', x));
14160 // this.trigger.setStyle('left', x+'px');
14165 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14168 getResizeEl : function(){
14169 return this.inputEl();
14173 getPositionEl : function(){
14174 return this.inputEl();
14178 alignErrorIcon : function(){
14179 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14183 initEvents : function(){
14187 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14188 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14189 if(!this.multiple && this.showToggleBtn){
14190 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14191 if(this.hideTrigger){
14192 this.trigger.setDisplayed(false);
14194 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14198 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14201 if(this.removable && !this.editable && !this.tickable){
14202 var close = this.closeTriggerEl();
14205 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14206 close.on('click', this.removeBtnClick, this, close);
14210 //this.trigger.addClassOnOver('x-form-trigger-over');
14211 //this.trigger.addClassOnClick('x-form-trigger-click');
14214 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14218 closeTriggerEl : function()
14220 var close = this.el.select('.roo-combo-removable-btn', true).first();
14221 return close ? close : false;
14224 removeBtnClick : function(e, h, el)
14226 e.preventDefault();
14228 if(this.fireEvent("remove", this) !== false){
14230 this.fireEvent("afterremove", this)
14234 createList : function()
14236 this.list = Roo.get(document.body).createChild({
14237 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14238 cls: 'typeahead typeahead-long dropdown-menu shadow',
14239 style: 'display:none'
14242 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14247 initTrigger : function(){
14252 onDestroy : function(){
14254 this.trigger.removeAllListeners();
14255 // this.trigger.remove();
14258 // this.wrap.remove();
14260 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14264 onFocus : function(){
14265 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14267 if(!this.mimicing){
14268 this.wrap.addClass('x-trigger-wrap-focus');
14269 this.mimicing = true;
14270 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14271 if(this.monitorTab){
14272 this.el.on("keydown", this.checkTab, this);
14279 checkTab : function(e){
14280 if(e.getKey() == e.TAB){
14281 this.triggerBlur();
14286 onBlur : function(){
14291 mimicBlur : function(e, t){
14293 if(!this.wrap.contains(t) && this.validateBlur()){
14294 this.triggerBlur();
14300 triggerBlur : function(){
14301 this.mimicing = false;
14302 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14303 if(this.monitorTab){
14304 this.el.un("keydown", this.checkTab, this);
14306 //this.wrap.removeClass('x-trigger-wrap-focus');
14307 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14311 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14312 validateBlur : function(e, t){
14317 onDisable : function(){
14318 this.inputEl().dom.disabled = true;
14319 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14321 // this.wrap.addClass('x-item-disabled');
14326 onEnable : function(){
14327 this.inputEl().dom.disabled = false;
14328 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14330 // this.el.removeClass('x-item-disabled');
14335 onShow : function(){
14336 var ae = this.getActionEl();
14339 ae.dom.style.display = '';
14340 ae.dom.style.visibility = 'visible';
14346 onHide : function(){
14347 var ae = this.getActionEl();
14348 ae.dom.style.display = 'none';
14352 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14353 * by an implementing function.
14355 * @param {EventObject} e
14357 onTriggerClick : Roo.emptyFn
14365 * @class Roo.bootstrap.form.CardUploader
14366 * @extends Roo.bootstrap.Button
14367 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14368 * @cfg {Number} errorTimeout default 3000
14369 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14370 * @cfg {Array} html The button text.
14374 * Create a new CardUploader
14375 * @param {Object} config The config object
14378 Roo.bootstrap.form.CardUploader = function(config){
14382 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14385 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14393 * When a image is clicked on - and needs to display a slideshow or similar..
14394 * @param {Roo.bootstrap.Card} this
14395 * @param {Object} The image information data
14401 * When a the download link is clicked
14402 * @param {Roo.bootstrap.Card} this
14403 * @param {Object} The image information data contains
14410 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14413 errorTimeout : 3000,
14417 fileCollection : false,
14420 getAutoCreate : function()
14424 cls :'form-group' ,
14429 //cls : 'input-group-addon',
14430 html : this.fieldLabel
14438 value : this.value,
14439 cls : 'd-none form-control'
14444 multiple : 'multiple',
14446 cls : 'd-none roo-card-upload-selector'
14450 cls : 'roo-card-uploader-button-container w-100 mb-2'
14453 cls : 'card-columns roo-card-uploader-container'
14463 getChildContainer : function() /// what children are added to.
14465 return this.containerEl;
14468 getButtonContainer : function() /// what children are added to.
14470 return this.el.select(".roo-card-uploader-button-container").first();
14473 initEvents : function()
14476 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14480 xns: Roo.bootstrap,
14483 container_method : 'getButtonContainer' ,
14484 html : this.html, // fix changable?
14487 'click' : function(btn, e) {
14496 this.urlAPI = (window.createObjectURL && window) ||
14497 (window.URL && URL.revokeObjectURL && URL) ||
14498 (window.webkitURL && webkitURL);
14503 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14505 this.selectorEl.on('change', this.onFileSelected, this);
14508 this.images.forEach(function(img) {
14511 this.images = false;
14513 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14519 onClick : function(e)
14521 e.preventDefault();
14523 this.selectorEl.dom.click();
14527 onFileSelected : function(e)
14529 e.preventDefault();
14531 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14535 Roo.each(this.selectorEl.dom.files, function(file){
14536 this.addFile(file);
14545 addFile : function(file)
14548 if(typeof(file) === 'string'){
14549 throw "Add file by name?"; // should not happen
14553 if(!file || !this.urlAPI){
14563 var url = _this.urlAPI.createObjectURL( file);
14566 id : Roo.bootstrap.form.CardUploader.ID--,
14567 is_uploaded : false,
14571 mimetype : file.type,
14579 * addCard - add an Attachment to the uploader
14580 * @param data - the data about the image to upload
14584 title : "Title of file",
14585 is_uploaded : false,
14586 src : "http://.....",
14587 srcfile : { the File upload object },
14588 mimetype : file.type,
14591 .. any other data...
14597 addCard : function (data)
14599 // hidden input element?
14600 // if the file is not an image...
14601 //then we need to use something other that and header_image
14606 xns : Roo.bootstrap,
14607 xtype : 'CardFooter',
14610 xns : Roo.bootstrap,
14616 xns : Roo.bootstrap,
14618 html : String.format("<small>{0}</small>", data.title),
14619 cls : 'col-10 text-left',
14624 click : function() {
14626 t.fireEvent( "download", t, data );
14632 xns : Roo.bootstrap,
14634 style: 'max-height: 28px; ',
14640 click : function() {
14641 t.removeCard(data.id)
14653 var cn = this.addxtype(
14656 xns : Roo.bootstrap,
14659 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14660 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14661 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14666 initEvents : function() {
14667 Roo.bootstrap.Card.prototype.initEvents.call(this);
14669 this.imgEl = this.el.select('.card-img-top').first();
14671 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14672 this.imgEl.set({ 'pointer' : 'cursor' });
14675 this.getCardFooter().addClass('p-1');
14682 // dont' really need ot update items.
14683 // this.items.push(cn);
14684 this.fileCollection.add(cn);
14686 if (!data.srcfile) {
14687 this.updateInput();
14692 var reader = new FileReader();
14693 reader.addEventListener("load", function() {
14694 data.srcdata = reader.result;
14697 reader.readAsDataURL(data.srcfile);
14702 removeCard : function(id)
14705 var card = this.fileCollection.get(id);
14706 card.data.is_deleted = 1;
14707 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14708 //this.fileCollection.remove(card);
14709 //this.items = this.items.filter(function(e) { return e != card });
14710 // dont' really need ot update items.
14711 card.el.dom.parentNode.removeChild(card.el.dom);
14712 this.updateInput();
14718 this.fileCollection.each(function(card) {
14719 if (card.el.dom && card.el.dom.parentNode) {
14720 card.el.dom.parentNode.removeChild(card.el.dom);
14723 this.fileCollection.clear();
14724 this.updateInput();
14727 updateInput : function()
14730 this.fileCollection.each(function(e) {
14734 this.inputEl().dom.value = JSON.stringify(data);
14744 Roo.bootstrap.form.CardUploader.ID = -1;/*
14746 * Ext JS Library 1.1.1
14747 * Copyright(c) 2006-2007, Ext JS, LLC.
14749 * Originally Released Under LGPL - original licence link has changed is not relivant.
14752 * <script type="text/javascript">
14757 * @class Roo.data.SortTypes
14759 * Defines the default sorting (casting?) comparison functions used when sorting data.
14761 Roo.data.SortTypes = {
14763 * Default sort that does nothing
14764 * @param {Mixed} s The value being converted
14765 * @return {Mixed} The comparison value
14767 none : function(s){
14772 * The regular expression used to strip tags
14776 stripTagsRE : /<\/?[^>]+>/gi,
14779 * Strips all HTML tags to sort on text only
14780 * @param {Mixed} s The value being converted
14781 * @return {String} The comparison value
14783 asText : function(s){
14784 return String(s).replace(this.stripTagsRE, "");
14788 * Strips all HTML tags to sort on text only - Case insensitive
14789 * @param {Mixed} s The value being converted
14790 * @return {String} The comparison value
14792 asUCText : function(s){
14793 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14797 * Case insensitive string
14798 * @param {Mixed} s The value being converted
14799 * @return {String} The comparison value
14801 asUCString : function(s) {
14802 return String(s).toUpperCase();
14807 * @param {Mixed} s The value being converted
14808 * @return {Number} The comparison value
14810 asDate : function(s) {
14814 if(s instanceof Date){
14815 return s.getTime();
14817 return Date.parse(String(s));
14822 * @param {Mixed} s The value being converted
14823 * @return {Float} The comparison value
14825 asFloat : function(s) {
14826 var val = parseFloat(String(s).replace(/,/g, ""));
14835 * @param {Mixed} s The value being converted
14836 * @return {Number} The comparison value
14838 asInt : function(s) {
14839 var val = parseInt(String(s).replace(/,/g, ""));
14847 * Ext JS Library 1.1.1
14848 * Copyright(c) 2006-2007, Ext JS, LLC.
14850 * Originally Released Under LGPL - original licence link has changed is not relivant.
14853 * <script type="text/javascript">
14857 * @class Roo.data.Record
14858 * Instances of this class encapsulate both record <em>definition</em> information, and record
14859 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14860 * to access Records cached in an {@link Roo.data.Store} object.<br>
14862 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14863 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14866 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14868 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14869 * {@link #create}. The parameters are the same.
14870 * @param {Array} data An associative Array of data values keyed by the field name.
14871 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14872 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14873 * not specified an integer id is generated.
14875 Roo.data.Record = function(data, id){
14876 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14881 * Generate a constructor for a specific record layout.
14882 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14883 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14884 * Each field definition object may contain the following properties: <ul>
14885 * <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,
14886 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14887 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14888 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14889 * is being used, then this is a string containing the javascript expression to reference the data relative to
14890 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14891 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14892 * this may be omitted.</p></li>
14893 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14894 * <ul><li>auto (Default, implies no conversion)</li>
14899 * <li>date</li></ul></p></li>
14900 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14901 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14902 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14903 * by the Reader into an object that will be stored in the Record. It is passed the
14904 * following parameters:<ul>
14905 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14907 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14909 * <br>usage:<br><pre><code>
14910 var TopicRecord = Roo.data.Record.create(
14911 {name: 'title', mapping: 'topic_title'},
14912 {name: 'author', mapping: 'username'},
14913 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14914 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14915 {name: 'lastPoster', mapping: 'user2'},
14916 {name: 'excerpt', mapping: 'post_text'}
14919 var myNewRecord = new TopicRecord({
14920 title: 'Do my job please',
14923 lastPost: new Date(),
14924 lastPoster: 'Animal',
14925 excerpt: 'No way dude!'
14927 myStore.add(myNewRecord);
14932 Roo.data.Record.create = function(o){
14933 var f = function(){
14934 f.superclass.constructor.apply(this, arguments);
14936 Roo.extend(f, Roo.data.Record);
14937 var p = f.prototype;
14938 p.fields = new Roo.util.MixedCollection(false, function(field){
14941 for(var i = 0, len = o.length; i < len; i++){
14942 p.fields.add(new Roo.data.Field(o[i]));
14944 f.getField = function(name){
14945 return p.fields.get(name);
14950 Roo.data.Record.AUTO_ID = 1000;
14951 Roo.data.Record.EDIT = 'edit';
14952 Roo.data.Record.REJECT = 'reject';
14953 Roo.data.Record.COMMIT = 'commit';
14955 Roo.data.Record.prototype = {
14957 * Readonly flag - true if this record has been modified.
14966 join : function(store){
14967 this.store = store;
14971 * Set the named field to the specified value.
14972 * @param {String} name The name of the field to set.
14973 * @param {Object} value The value to set the field to.
14975 set : function(name, value){
14976 if(this.data[name] == value){
14980 if(!this.modified){
14981 this.modified = {};
14983 if(typeof this.modified[name] == 'undefined'){
14984 this.modified[name] = this.data[name];
14986 this.data[name] = value;
14987 if(!this.editing && this.store){
14988 this.store.afterEdit(this);
14993 * Get the value of the named field.
14994 * @param {String} name The name of the field to get the value of.
14995 * @return {Object} The value of the field.
14997 get : function(name){
14998 return this.data[name];
15002 beginEdit : function(){
15003 this.editing = true;
15004 this.modified = {};
15008 cancelEdit : function(){
15009 this.editing = false;
15010 delete this.modified;
15014 endEdit : function(){
15015 this.editing = false;
15016 if(this.dirty && this.store){
15017 this.store.afterEdit(this);
15022 * Usually called by the {@link Roo.data.Store} which owns the Record.
15023 * Rejects all changes made to the Record since either creation, or the last commit operation.
15024 * Modified fields are reverted to their original values.
15026 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15027 * of reject operations.
15029 reject : function(){
15030 var m = this.modified;
15032 if(typeof m[n] != "function"){
15033 this.data[n] = m[n];
15036 this.dirty = false;
15037 delete this.modified;
15038 this.editing = false;
15040 this.store.afterReject(this);
15045 * Usually called by the {@link Roo.data.Store} which owns the Record.
15046 * Commits all changes made to the Record since either creation, or the last commit operation.
15048 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
15049 * of commit operations.
15051 commit : function(){
15052 this.dirty = false;
15053 delete this.modified;
15054 this.editing = false;
15056 this.store.afterCommit(this);
15061 hasError : function(){
15062 return this.error != null;
15066 clearError : function(){
15071 * Creates a copy of this record.
15072 * @param {String} id (optional) A new record id if you don't want to use this record's id
15075 copy : function(newId) {
15076 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15080 * Ext JS Library 1.1.1
15081 * Copyright(c) 2006-2007, Ext JS, LLC.
15083 * Originally Released Under LGPL - original licence link has changed is not relivant.
15086 * <script type="text/javascript">
15092 * @class Roo.data.Store
15093 * @extends Roo.util.Observable
15094 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15095 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15097 * 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
15098 * has no knowledge of the format of the data returned by the Proxy.<br>
15100 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15101 * instances from the data object. These records are cached and made available through accessor functions.
15103 * Creates a new Store.
15104 * @param {Object} config A config object containing the objects needed for the Store to access data,
15105 * and read the data into Records.
15107 Roo.data.Store = function(config){
15108 this.data = new Roo.util.MixedCollection(false);
15109 this.data.getKey = function(o){
15112 this.baseParams = {};
15114 this.paramNames = {
15119 "multisort" : "_multisort"
15122 if(config && config.data){
15123 this.inlineData = config.data;
15124 delete config.data;
15127 Roo.apply(this, config);
15129 if(this.reader){ // reader passed
15130 this.reader = Roo.factory(this.reader, Roo.data);
15131 this.reader.xmodule = this.xmodule || false;
15132 if(!this.recordType){
15133 this.recordType = this.reader.recordType;
15135 if(this.reader.onMetaChange){
15136 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15140 if(this.recordType){
15141 this.fields = this.recordType.prototype.fields;
15143 this.modified = [];
15147 * @event datachanged
15148 * Fires when the data cache has changed, and a widget which is using this Store
15149 * as a Record cache should refresh its view.
15150 * @param {Store} this
15152 datachanged : true,
15154 * @event metachange
15155 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15156 * @param {Store} this
15157 * @param {Object} meta The JSON metadata
15162 * Fires when Records have been added to the Store
15163 * @param {Store} this
15164 * @param {Roo.data.Record[]} records The array of Records added
15165 * @param {Number} index The index at which the record(s) were added
15170 * Fires when a Record has been removed from the Store
15171 * @param {Store} this
15172 * @param {Roo.data.Record} record The Record that was removed
15173 * @param {Number} index The index at which the record was removed
15178 * Fires when a Record has been updated
15179 * @param {Store} this
15180 * @param {Roo.data.Record} record The Record that was updated
15181 * @param {String} operation The update operation being performed. Value may be one of:
15183 Roo.data.Record.EDIT
15184 Roo.data.Record.REJECT
15185 Roo.data.Record.COMMIT
15191 * Fires when the data cache has been cleared.
15192 * @param {Store} this
15196 * @event beforeload
15197 * Fires before a request is made for a new data object. If the beforeload handler returns false
15198 * the load action will be canceled.
15199 * @param {Store} this
15200 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15204 * @event beforeloadadd
15205 * Fires after a new set of Records has been loaded.
15206 * @param {Store} this
15207 * @param {Roo.data.Record[]} records The Records that were loaded
15208 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15210 beforeloadadd : true,
15213 * Fires after a new set of Records has been loaded, before they are added to the store.
15214 * @param {Store} this
15215 * @param {Roo.data.Record[]} records The Records that were loaded
15216 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15217 * @params {Object} return from reader
15221 * @event loadexception
15222 * Fires if an exception occurs in the Proxy during loading.
15223 * Called with the signature of the Proxy's "loadexception" event.
15224 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15227 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15228 * @param {Object} load options
15229 * @param {Object} jsonData from your request (normally this contains the Exception)
15231 loadexception : true
15235 this.proxy = Roo.factory(this.proxy, Roo.data);
15236 this.proxy.xmodule = this.xmodule || false;
15237 this.relayEvents(this.proxy, ["loadexception"]);
15239 this.sortToggle = {};
15240 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15242 Roo.data.Store.superclass.constructor.call(this);
15244 if(this.inlineData){
15245 this.loadData(this.inlineData);
15246 delete this.inlineData;
15250 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15252 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15253 * without a remote query - used by combo/forms at present.
15257 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15260 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15263 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15264 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15267 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15268 * on any HTTP request
15271 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15274 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15278 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15279 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15281 remoteSort : false,
15284 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15285 * loaded or when a record is removed. (defaults to false).
15287 pruneModifiedRecords : false,
15290 lastOptions : null,
15293 * Add Records to the Store and fires the add event.
15294 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15296 add : function(records){
15297 records = [].concat(records);
15298 for(var i = 0, len = records.length; i < len; i++){
15299 records[i].join(this);
15301 var index = this.data.length;
15302 this.data.addAll(records);
15303 this.fireEvent("add", this, records, index);
15307 * Remove a Record from the Store and fires the remove event.
15308 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15310 remove : function(record){
15311 var index = this.data.indexOf(record);
15312 this.data.removeAt(index);
15314 if(this.pruneModifiedRecords){
15315 this.modified.remove(record);
15317 this.fireEvent("remove", this, record, index);
15321 * Remove all Records from the Store and fires the clear event.
15323 removeAll : function(){
15325 if(this.pruneModifiedRecords){
15326 this.modified = [];
15328 this.fireEvent("clear", this);
15332 * Inserts Records to the Store at the given index and fires the add event.
15333 * @param {Number} index The start index at which to insert the passed Records.
15334 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15336 insert : function(index, records){
15337 records = [].concat(records);
15338 for(var i = 0, len = records.length; i < len; i++){
15339 this.data.insert(index, records[i]);
15340 records[i].join(this);
15342 this.fireEvent("add", this, records, index);
15346 * Get the index within the cache of the passed Record.
15347 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15348 * @return {Number} The index of the passed Record. Returns -1 if not found.
15350 indexOf : function(record){
15351 return this.data.indexOf(record);
15355 * Get the index within the cache of the Record with the passed id.
15356 * @param {String} id The id of the Record to find.
15357 * @return {Number} The index of the Record. Returns -1 if not found.
15359 indexOfId : function(id){
15360 return this.data.indexOfKey(id);
15364 * Get the Record with the specified id.
15365 * @param {String} id The id of the Record to find.
15366 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15368 getById : function(id){
15369 return this.data.key(id);
15373 * Get the Record at the specified index.
15374 * @param {Number} index The index of the Record to find.
15375 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15377 getAt : function(index){
15378 return this.data.itemAt(index);
15382 * Returns a range of Records between specified indices.
15383 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15384 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15385 * @return {Roo.data.Record[]} An array of Records
15387 getRange : function(start, end){
15388 return this.data.getRange(start, end);
15392 storeOptions : function(o){
15393 o = Roo.apply({}, o);
15396 this.lastOptions = o;
15400 * Loads the Record cache from the configured Proxy using the configured Reader.
15402 * If using remote paging, then the first load call must specify the <em>start</em>
15403 * and <em>limit</em> properties in the options.params property to establish the initial
15404 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15406 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15407 * and this call will return before the new data has been loaded. Perform any post-processing
15408 * in a callback function, or in a "load" event handler.</strong>
15410 * @param {Object} options An object containing properties which control loading options:<ul>
15411 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15412 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15415 data : data, // array of key=>value data like JsonReader
15416 total : data.length,
15422 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15423 * passed the following arguments:<ul>
15424 * <li>r : Roo.data.Record[]</li>
15425 * <li>options: Options object from the load call</li>
15426 * <li>success: Boolean success indicator</li></ul></li>
15427 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15428 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15431 load : function(options){
15432 options = options || {};
15433 if(this.fireEvent("beforeload", this, options) !== false){
15434 this.storeOptions(options);
15435 var p = Roo.apply(options.params || {}, this.baseParams);
15436 // if meta was not loaded from remote source.. try requesting it.
15437 if (!this.reader.metaFromRemote) {
15438 p._requestMeta = 1;
15440 if(this.sortInfo && this.remoteSort){
15441 var pn = this.paramNames;
15442 p[pn["sort"]] = this.sortInfo.field;
15443 p[pn["dir"]] = this.sortInfo.direction;
15445 if (this.multiSort) {
15446 var pn = this.paramNames;
15447 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15450 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15455 * Reloads the Record cache from the configured Proxy using the configured Reader and
15456 * the options from the last load operation performed.
15457 * @param {Object} options (optional) An object containing properties which may override the options
15458 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15459 * the most recently used options are reused).
15461 reload : function(options){
15462 this.load(Roo.applyIf(options||{}, this.lastOptions));
15466 // Called as a callback by the Reader during a load operation.
15467 loadRecords : function(o, options, success){
15470 if(success !== false){
15471 this.fireEvent("load", this, [], options, o);
15473 if(options.callback){
15474 options.callback.call(options.scope || this, [], options, false);
15478 // if data returned failure - throw an exception.
15479 if (o.success === false) {
15480 // show a message if no listener is registered.
15481 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15482 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15484 // loadmask wil be hooked into this..
15485 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15488 var r = o.records, t = o.totalRecords || r.length;
15490 this.fireEvent("beforeloadadd", this, r, options, o);
15492 if(!options || options.add !== true){
15493 if(this.pruneModifiedRecords){
15494 this.modified = [];
15496 for(var i = 0, len = r.length; i < len; i++){
15500 this.data = this.snapshot;
15501 delete this.snapshot;
15504 this.data.addAll(r);
15505 this.totalLength = t;
15507 this.fireEvent("datachanged", this);
15509 this.totalLength = Math.max(t, this.data.length+r.length);
15513 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15515 var e = new Roo.data.Record({});
15517 e.set(this.parent.displayField, this.parent.emptyTitle);
15518 e.set(this.parent.valueField, '');
15523 this.fireEvent("load", this, r, options, o);
15524 if(options.callback){
15525 options.callback.call(options.scope || this, r, options, true);
15531 * Loads data from a passed data block. A Reader which understands the format of the data
15532 * must have been configured in the constructor.
15533 * @param {Object} data The data block from which to read the Records. The format of the data expected
15534 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15535 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15537 loadData : function(o, append){
15538 var r = this.reader.readRecords(o);
15539 this.loadRecords(r, {add: append}, true);
15543 * using 'cn' the nested child reader read the child array into it's child stores.
15544 * @param {Object} rec The record with a 'children array
15546 loadDataFromChildren : function(rec)
15548 this.loadData(this.reader.toLoadData(rec));
15553 * Gets the number of cached records.
15555 * <em>If using paging, this may not be the total size of the dataset. If the data object
15556 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15557 * the data set size</em>
15559 getCount : function(){
15560 return this.data.length || 0;
15564 * Gets the total number of records in the dataset as returned by the server.
15566 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15567 * the dataset size</em>
15569 getTotalCount : function(){
15570 return this.totalLength || 0;
15574 * Returns the sort state of the Store as an object with two properties:
15576 field {String} The name of the field by which the Records are sorted
15577 direction {String} The sort order, "ASC" or "DESC"
15580 getSortState : function(){
15581 return this.sortInfo;
15585 applySort : function(){
15586 if(this.sortInfo && !this.remoteSort){
15587 var s = this.sortInfo, f = s.field;
15588 var st = this.fields.get(f).sortType;
15589 var fn = function(r1, r2){
15590 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15591 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15593 this.data.sort(s.direction, fn);
15594 if(this.snapshot && this.snapshot != this.data){
15595 this.snapshot.sort(s.direction, fn);
15601 * Sets the default sort column and order to be used by the next load operation.
15602 * @param {String} fieldName The name of the field to sort by.
15603 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15605 setDefaultSort : function(field, dir){
15606 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15610 * Sort the Records.
15611 * If remote sorting is used, the sort is performed on the server, and the cache is
15612 * reloaded. If local sorting is used, the cache is sorted internally.
15613 * @param {String} fieldName The name of the field to sort by.
15614 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15616 sort : function(fieldName, dir){
15617 var f = this.fields.get(fieldName);
15619 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15621 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15622 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15627 this.sortToggle[f.name] = dir;
15628 this.sortInfo = {field: f.name, direction: dir};
15629 if(!this.remoteSort){
15631 this.fireEvent("datachanged", this);
15633 this.load(this.lastOptions);
15638 * Calls the specified function for each of the Records in the cache.
15639 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15640 * Returning <em>false</em> aborts and exits the iteration.
15641 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15643 each : function(fn, scope){
15644 this.data.each(fn, scope);
15648 * Gets all records modified since the last commit. Modified records are persisted across load operations
15649 * (e.g., during paging).
15650 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15652 getModifiedRecords : function(){
15653 return this.modified;
15657 createFilterFn : function(property, value, anyMatch){
15658 if(!value.exec){ // not a regex
15659 value = String(value);
15660 if(value.length == 0){
15663 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15665 return function(r){
15666 return value.test(r.data[property]);
15671 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15672 * @param {String} property A field on your records
15673 * @param {Number} start The record index to start at (defaults to 0)
15674 * @param {Number} end The last record index to include (defaults to length - 1)
15675 * @return {Number} The sum
15677 sum : function(property, start, end){
15678 var rs = this.data.items, v = 0;
15679 start = start || 0;
15680 end = (end || end === 0) ? end : rs.length-1;
15682 for(var i = start; i <= end; i++){
15683 v += (rs[i].data[property] || 0);
15689 * Filter the records by a specified property.
15690 * @param {String} field A field on your records
15691 * @param {String/RegExp} value Either a string that the field
15692 * should start with or a RegExp to test against the field
15693 * @param {Boolean} anyMatch True to match any part not just the beginning
15695 filter : function(property, value, anyMatch){
15696 var fn = this.createFilterFn(property, value, anyMatch);
15697 return fn ? this.filterBy(fn) : this.clearFilter();
15701 * Filter by a function. The specified function will be called with each
15702 * record in this data source. If the function returns true the record is included,
15703 * otherwise it is filtered.
15704 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15705 * @param {Object} scope (optional) The scope of the function (defaults to this)
15707 filterBy : function(fn, scope){
15708 this.snapshot = this.snapshot || this.data;
15709 this.data = this.queryBy(fn, scope||this);
15710 this.fireEvent("datachanged", this);
15714 * Query the records by a specified property.
15715 * @param {String} field A field on your records
15716 * @param {String/RegExp} value Either a string that the field
15717 * should start with or a RegExp to test against the field
15718 * @param {Boolean} anyMatch True to match any part not just the beginning
15719 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15721 query : function(property, value, anyMatch){
15722 var fn = this.createFilterFn(property, value, anyMatch);
15723 return fn ? this.queryBy(fn) : this.data.clone();
15727 * Query by a function. The specified function will be called with each
15728 * record in this data source. If the function returns true the record is included
15730 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15731 * @param {Object} scope (optional) The scope of the function (defaults to this)
15732 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15734 queryBy : function(fn, scope){
15735 var data = this.snapshot || this.data;
15736 return data.filterBy(fn, scope||this);
15740 * Collects unique values for a particular dataIndex from this store.
15741 * @param {String} dataIndex The property to collect
15742 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15743 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15744 * @return {Array} An array of the unique values
15746 collect : function(dataIndex, allowNull, bypassFilter){
15747 var d = (bypassFilter === true && this.snapshot) ?
15748 this.snapshot.items : this.data.items;
15749 var v, sv, r = [], l = {};
15750 for(var i = 0, len = d.length; i < len; i++){
15751 v = d[i].data[dataIndex];
15753 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15762 * Revert to a view of the Record cache with no filtering applied.
15763 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15765 clearFilter : function(suppressEvent){
15766 if(this.snapshot && this.snapshot != this.data){
15767 this.data = this.snapshot;
15768 delete this.snapshot;
15769 if(suppressEvent !== true){
15770 this.fireEvent("datachanged", this);
15776 afterEdit : function(record){
15777 if(this.modified.indexOf(record) == -1){
15778 this.modified.push(record);
15780 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15784 afterReject : function(record){
15785 this.modified.remove(record);
15786 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15790 afterCommit : function(record){
15791 this.modified.remove(record);
15792 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15796 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15797 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15799 commitChanges : function(){
15800 var m = this.modified.slice(0);
15801 this.modified = [];
15802 for(var i = 0, len = m.length; i < len; i++){
15808 * Cancel outstanding changes on all changed records.
15810 rejectChanges : function(){
15811 var m = this.modified.slice(0);
15812 this.modified = [];
15813 for(var i = 0, len = m.length; i < len; i++){
15818 onMetaChange : function(meta, rtype, o){
15819 this.recordType = rtype;
15820 this.fields = rtype.prototype.fields;
15821 delete this.snapshot;
15822 this.sortInfo = meta.sortInfo || this.sortInfo;
15823 this.modified = [];
15824 this.fireEvent('metachange', this, this.reader.meta);
15827 moveIndex : function(data, type)
15829 var index = this.indexOf(data);
15831 var newIndex = index + type;
15835 this.insert(newIndex, data);
15840 * Ext JS Library 1.1.1
15841 * Copyright(c) 2006-2007, Ext JS, LLC.
15843 * Originally Released Under LGPL - original licence link has changed is not relivant.
15846 * <script type="text/javascript">
15850 * @class Roo.data.SimpleStore
15851 * @extends Roo.data.Store
15852 * Small helper class to make creating Stores from Array data easier.
15853 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15854 * @cfg {Array} fields An array of field definition objects, or field name strings.
15855 * @cfg {Object} an existing reader (eg. copied from another store)
15856 * @cfg {Array} data The multi-dimensional array of data
15857 * @cfg {Roo.data.DataProxy} proxy [not-required]
15858 * @cfg {Roo.data.Reader} reader [not-required]
15860 * @param {Object} config
15862 Roo.data.SimpleStore = function(config)
15864 Roo.data.SimpleStore.superclass.constructor.call(this, {
15866 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15869 Roo.data.Record.create(config.fields)
15871 proxy : new Roo.data.MemoryProxy(config.data)
15875 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15877 * Ext JS Library 1.1.1
15878 * Copyright(c) 2006-2007, Ext JS, LLC.
15880 * Originally Released Under LGPL - original licence link has changed is not relivant.
15883 * <script type="text/javascript">
15888 * @extends Roo.data.Store
15889 * @class Roo.data.JsonStore
15890 * Small helper class to make creating Stores for JSON data easier. <br/>
15892 var store = new Roo.data.JsonStore({
15893 url: 'get-images.php',
15895 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15898 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15899 * JsonReader and HttpProxy (unless inline data is provided).</b>
15900 * @cfg {Array} fields An array of field definition objects, or field name strings.
15902 * @param {Object} config
15904 Roo.data.JsonStore = function(c){
15905 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15906 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15907 reader: new Roo.data.JsonReader(c, c.fields)
15910 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15912 * Ext JS Library 1.1.1
15913 * Copyright(c) 2006-2007, Ext JS, LLC.
15915 * Originally Released Under LGPL - original licence link has changed is not relivant.
15918 * <script type="text/javascript">
15922 Roo.data.Field = function(config){
15923 if(typeof config == "string"){
15924 config = {name: config};
15926 Roo.apply(this, config);
15929 this.type = "auto";
15932 var st = Roo.data.SortTypes;
15933 // named sortTypes are supported, here we look them up
15934 if(typeof this.sortType == "string"){
15935 this.sortType = st[this.sortType];
15938 // set default sortType for strings and dates
15939 if(!this.sortType){
15942 this.sortType = st.asUCString;
15945 this.sortType = st.asDate;
15948 this.sortType = st.none;
15953 var stripRe = /[\$,%]/g;
15955 // prebuilt conversion function for this field, instead of
15956 // switching every time we're reading a value
15958 var cv, dateFormat = this.dateFormat;
15963 cv = function(v){ return v; };
15966 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15970 return v !== undefined && v !== null && v !== '' ?
15971 parseInt(String(v).replace(stripRe, ""), 10) : '';
15976 return v !== undefined && v !== null && v !== '' ?
15977 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15982 cv = function(v){ return v === true || v === "true" || v == 1; };
15989 if(v instanceof Date){
15993 if(dateFormat == "timestamp"){
15994 return new Date(v*1000);
15996 return Date.parseDate(v, dateFormat);
15998 var parsed = Date.parse(v);
15999 return parsed ? new Date(parsed) : null;
16008 Roo.data.Field.prototype = {
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">
16025 // Base class for reading structured data from a data source. This class is intended to be
16026 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
16029 * @class Roo.data.DataReader
16031 * Base class for reading structured data from a data source. This class is intended to be
16032 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
16035 Roo.data.DataReader = function(meta, recordType){
16039 this.recordType = recordType instanceof Array ?
16040 Roo.data.Record.create(recordType) : recordType;
16043 Roo.data.DataReader.prototype = {
16046 readerType : 'Data',
16048 * Create an empty record
16049 * @param {Object} data (optional) - overlay some values
16050 * @return {Roo.data.Record} record created.
16052 newRow : function(d) {
16054 this.recordType.prototype.fields.each(function(c) {
16056 case 'int' : da[c.name] = 0; break;
16057 case 'date' : da[c.name] = new Date(); break;
16058 case 'float' : da[c.name] = 0.0; break;
16059 case 'boolean' : da[c.name] = false; break;
16060 default : da[c.name] = ""; break;
16064 return new this.recordType(Roo.apply(da, d));
16070 * Ext JS Library 1.1.1
16071 * Copyright(c) 2006-2007, Ext JS, LLC.
16073 * Originally Released Under LGPL - original licence link has changed is not relivant.
16076 * <script type="text/javascript">
16080 * @class Roo.data.DataProxy
16081 * @extends Roo.util.Observable
16083 * This class is an abstract base class for implementations which provide retrieval of
16084 * unformatted data objects.<br>
16086 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16087 * (of the appropriate type which knows how to parse the data object) to provide a block of
16088 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16090 * Custom implementations must implement the load method as described in
16091 * {@link Roo.data.HttpProxy#load}.
16093 Roo.data.DataProxy = function(){
16096 * @event beforeload
16097 * Fires before a network request is made to retrieve a data object.
16098 * @param {Object} This DataProxy object.
16099 * @param {Object} params The params parameter to the load function.
16104 * Fires before the load method's callback is called.
16105 * @param {Object} This DataProxy object.
16106 * @param {Object} o The data object.
16107 * @param {Object} arg The callback argument object passed to the load function.
16111 * @event loadexception
16112 * Fires if an Exception occurs during data retrieval.
16113 * @param {Object} This DataProxy object.
16114 * @param {Object} o The data object.
16115 * @param {Object} arg The callback argument object passed to the load function.
16116 * @param {Object} e The Exception.
16118 loadexception : true
16120 Roo.data.DataProxy.superclass.constructor.call(this);
16123 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16126 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16130 * Ext JS Library 1.1.1
16131 * Copyright(c) 2006-2007, Ext JS, LLC.
16133 * Originally Released Under LGPL - original licence link has changed is not relivant.
16136 * <script type="text/javascript">
16139 * @class Roo.data.MemoryProxy
16140 * @extends Roo.data.DataProxy
16141 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16142 * to the Reader when its load method is called.
16144 * @param {Object} config A config object containing the objects needed for the Store to access data,
16146 Roo.data.MemoryProxy = function(config){
16148 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16149 data = config.data;
16151 Roo.data.MemoryProxy.superclass.constructor.call(this);
16155 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16158 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16161 * Load data from the requested source (in this case an in-memory
16162 * data object passed to the constructor), read the data object into
16163 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16164 * process that block using the passed callback.
16165 * @param {Object} params This parameter is not used by the MemoryProxy class.
16166 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16167 * object into a block of Roo.data.Records.
16168 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16169 * The function must be passed <ul>
16170 * <li>The Record block object</li>
16171 * <li>The "arg" argument from the load function</li>
16172 * <li>A boolean success indicator</li>
16174 * @param {Object} scope The scope in which to call the callback
16175 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16177 load : function(params, reader, callback, scope, arg){
16178 params = params || {};
16181 result = reader.readRecords(params.data ? params.data :this.data);
16183 this.fireEvent("loadexception", this, arg, null, e);
16184 callback.call(scope, null, arg, false);
16187 callback.call(scope, result, arg, true);
16191 update : function(params, records){
16196 * Ext JS Library 1.1.1
16197 * Copyright(c) 2006-2007, Ext JS, LLC.
16199 * Originally Released Under LGPL - original licence link has changed is not relivant.
16202 * <script type="text/javascript">
16205 * @class Roo.data.HttpProxy
16206 * @extends Roo.data.DataProxy
16207 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16208 * configured to reference a certain URL.<br><br>
16210 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16211 * from which the running page was served.<br><br>
16213 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16215 * Be aware that to enable the browser to parse an XML document, the server must set
16216 * the Content-Type header in the HTTP response to "text/xml".
16218 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16219 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16220 * will be used to make the request.
16222 Roo.data.HttpProxy = function(conn){
16223 Roo.data.HttpProxy.superclass.constructor.call(this);
16224 // is conn a conn config or a real conn?
16226 this.useAjax = !conn || !conn.events;
16230 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16231 // thse are take from connection...
16234 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16237 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16238 * extra parameters to each request made by this object. (defaults to undefined)
16241 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16242 * to each request made by this object. (defaults to undefined)
16245 * @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)
16248 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16251 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16257 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16261 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16262 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16263 * a finer-grained basis than the DataProxy events.
16265 getConnection : function(){
16266 return this.useAjax ? Roo.Ajax : this.conn;
16270 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16271 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16272 * process that block using the passed callback.
16273 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16274 * for the request to the remote server.
16275 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16276 * object into a block of Roo.data.Records.
16277 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16278 * The function must be passed <ul>
16279 * <li>The Record block object</li>
16280 * <li>The "arg" argument from the load function</li>
16281 * <li>A boolean success indicator</li>
16283 * @param {Object} scope The scope in which to call the callback
16284 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16286 load : function(params, reader, callback, scope, arg){
16287 if(this.fireEvent("beforeload", this, params) !== false){
16289 params : params || {},
16291 callback : callback,
16296 callback : this.loadResponse,
16300 Roo.applyIf(o, this.conn);
16301 if(this.activeRequest){
16302 Roo.Ajax.abort(this.activeRequest);
16304 this.activeRequest = Roo.Ajax.request(o);
16306 this.conn.request(o);
16309 callback.call(scope||this, null, arg, false);
16314 loadResponse : function(o, success, response){
16315 delete this.activeRequest;
16317 this.fireEvent("loadexception", this, o, response);
16318 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16323 result = o.reader.read(response);
16326 o.raw = { errorMsg : response.responseText };
16327 this.fireEvent("loadexception", this, o, response, e);
16328 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16332 this.fireEvent("load", this, o, o.request.arg);
16333 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16337 update : function(dataSet){
16342 updateResponse : function(dataSet){
16347 * Ext JS Library 1.1.1
16348 * Copyright(c) 2006-2007, Ext JS, LLC.
16350 * Originally Released Under LGPL - original licence link has changed is not relivant.
16353 * <script type="text/javascript">
16357 * @class Roo.data.ScriptTagProxy
16358 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16359 * other than the originating domain of the running page.<br><br>
16361 * <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
16362 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16364 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16365 * source code that is used as the source inside a <script> tag.<br><br>
16367 * In order for the browser to process the returned data, the server must wrap the data object
16368 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16369 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16370 * depending on whether the callback name was passed:
16373 boolean scriptTag = false;
16374 String cb = request.getParameter("callback");
16377 response.setContentType("text/javascript");
16379 response.setContentType("application/x-json");
16381 Writer out = response.getWriter();
16383 out.write(cb + "(");
16385 out.print(dataBlock.toJsonString());
16392 * @param {Object} config A configuration object.
16394 Roo.data.ScriptTagProxy = function(config){
16395 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16396 Roo.apply(this, config);
16397 this.head = document.getElementsByTagName("head")[0];
16400 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16402 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16404 * @cfg {String} url The URL from which to request the data object.
16407 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16411 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16412 * the server the name of the callback function set up by the load call to process the returned data object.
16413 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16414 * javascript output which calls this named function passing the data object as its only parameter.
16416 callbackParam : "callback",
16418 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16419 * name to the request.
16424 * Load data from the configured URL, read the data object into
16425 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16426 * process that block using the passed callback.
16427 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16428 * for the request to the remote server.
16429 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16430 * object into a block of Roo.data.Records.
16431 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16432 * The function must be passed <ul>
16433 * <li>The Record block object</li>
16434 * <li>The "arg" argument from the load function</li>
16435 * <li>A boolean success indicator</li>
16437 * @param {Object} scope The scope in which to call the callback
16438 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16440 load : function(params, reader, callback, scope, arg){
16441 if(this.fireEvent("beforeload", this, params) !== false){
16443 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16445 var url = this.url;
16446 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16448 url += "&_dc=" + (new Date().getTime());
16450 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16453 cb : "stcCallback"+transId,
16454 scriptId : "stcScript"+transId,
16458 callback : callback,
16464 window[trans.cb] = function(o){
16465 conn.handleResponse(o, trans);
16468 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16470 if(this.autoAbort !== false){
16474 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16476 var script = document.createElement("script");
16477 script.setAttribute("src", url);
16478 script.setAttribute("type", "text/javascript");
16479 script.setAttribute("id", trans.scriptId);
16480 this.head.appendChild(script);
16482 this.trans = trans;
16484 callback.call(scope||this, null, arg, false);
16489 isLoading : function(){
16490 return this.trans ? true : false;
16494 * Abort the current server request.
16496 abort : function(){
16497 if(this.isLoading()){
16498 this.destroyTrans(this.trans);
16503 destroyTrans : function(trans, isLoaded){
16504 this.head.removeChild(document.getElementById(trans.scriptId));
16505 clearTimeout(trans.timeoutId);
16507 window[trans.cb] = undefined;
16509 delete window[trans.cb];
16512 // if hasn't been loaded, wait for load to remove it to prevent script error
16513 window[trans.cb] = function(){
16514 window[trans.cb] = undefined;
16516 delete window[trans.cb];
16523 handleResponse : function(o, trans){
16524 this.trans = false;
16525 this.destroyTrans(trans, true);
16528 result = trans.reader.readRecords(o);
16530 this.fireEvent("loadexception", this, o, trans.arg, e);
16531 trans.callback.call(trans.scope||window, null, trans.arg, false);
16534 this.fireEvent("load", this, o, trans.arg);
16535 trans.callback.call(trans.scope||window, result, trans.arg, true);
16539 handleFailure : function(trans){
16540 this.trans = false;
16541 this.destroyTrans(trans, false);
16542 this.fireEvent("loadexception", this, null, trans.arg);
16543 trans.callback.call(trans.scope||window, null, trans.arg, false);
16547 * Ext JS Library 1.1.1
16548 * Copyright(c) 2006-2007, Ext JS, LLC.
16550 * Originally Released Under LGPL - original licence link has changed is not relivant.
16553 * <script type="text/javascript">
16557 * @class Roo.data.JsonReader
16558 * @extends Roo.data.DataReader
16559 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16560 * based on mappings in a provided Roo.data.Record constructor.
16562 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16563 * in the reply previously.
16568 var RecordDef = Roo.data.Record.create([
16569 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16570 {name: 'occupation'} // This field will use "occupation" as the mapping.
16572 var myReader = new Roo.data.JsonReader({
16573 totalProperty: "results", // The property which contains the total dataset size (optional)
16574 root: "rows", // The property which contains an Array of row objects
16575 id: "id" // The property within each row object that provides an ID for the record (optional)
16579 * This would consume a JSON file like this:
16581 { 'results': 2, 'rows': [
16582 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16583 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16586 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16587 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16588 * paged from the remote server.
16589 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16590 * @cfg {String} root name of the property which contains the Array of row objects.
16591 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16592 * @cfg {Array} fields Array of field definition objects
16594 * Create a new JsonReader
16595 * @param {Object} meta Metadata configuration options
16596 * @param {Object} recordType Either an Array of field definition objects,
16597 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16599 Roo.data.JsonReader = function(meta, recordType){
16602 // set some defaults:
16603 Roo.applyIf(meta, {
16604 totalProperty: 'total',
16605 successProperty : 'success',
16610 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16612 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16614 readerType : 'Json',
16617 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16618 * Used by Store query builder to append _requestMeta to params.
16621 metaFromRemote : false,
16623 * This method is only used by a DataProxy which has retrieved data from a remote server.
16624 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16625 * @return {Object} data A data block which is used by an Roo.data.Store object as
16626 * a cache of Roo.data.Records.
16628 read : function(response){
16629 var json = response.responseText;
16631 var o = /* eval:var:o */ eval("("+json+")");
16633 throw {message: "JsonReader.read: Json object not found"};
16639 this.metaFromRemote = true;
16640 this.meta = o.metaData;
16641 this.recordType = Roo.data.Record.create(o.metaData.fields);
16642 this.onMetaChange(this.meta, this.recordType, o);
16644 return this.readRecords(o);
16647 // private function a store will implement
16648 onMetaChange : function(meta, recordType, o){
16655 simpleAccess: function(obj, subsc) {
16662 getJsonAccessor: function(){
16664 return function(expr) {
16666 return(re.test(expr))
16667 ? new Function("obj", "return obj." + expr)
16672 return Roo.emptyFn;
16677 * Create a data block containing Roo.data.Records from an XML document.
16678 * @param {Object} o An object which contains an Array of row objects in the property specified
16679 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16680 * which contains the total size of the dataset.
16681 * @return {Object} data A data block which is used by an Roo.data.Store object as
16682 * a cache of Roo.data.Records.
16684 readRecords : function(o){
16686 * After any data loads, the raw JSON data is available for further custom processing.
16690 var s = this.meta, Record = this.recordType,
16691 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16693 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16695 if(s.totalProperty) {
16696 this.getTotal = this.getJsonAccessor(s.totalProperty);
16698 if(s.successProperty) {
16699 this.getSuccess = this.getJsonAccessor(s.successProperty);
16701 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16703 var g = this.getJsonAccessor(s.id);
16704 this.getId = function(rec) {
16706 return (r === undefined || r === "") ? null : r;
16709 this.getId = function(){return null;};
16712 for(var jj = 0; jj < fl; jj++){
16714 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16715 this.ef[jj] = this.getJsonAccessor(map);
16719 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16720 if(s.totalProperty){
16721 var vt = parseInt(this.getTotal(o), 10);
16726 if(s.successProperty){
16727 var vs = this.getSuccess(o);
16728 if(vs === false || vs === 'false'){
16733 for(var i = 0; i < c; i++){
16736 var id = this.getId(n);
16737 for(var j = 0; j < fl; j++){
16739 var v = this.ef[j](n);
16741 Roo.log('missing convert for ' + f.name);
16745 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16749 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16755 var record = new Record(values, id);
16757 records[i] = record;
16763 totalRecords : totalRecords
16766 // used when loading children.. @see loadDataFromChildren
16767 toLoadData: function(rec)
16769 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16770 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16771 return { data : data, total : data.length };
16776 * Ext JS Library 1.1.1
16777 * Copyright(c) 2006-2007, Ext JS, LLC.
16779 * Originally Released Under LGPL - original licence link has changed is not relivant.
16782 * <script type="text/javascript">
16786 * @class Roo.data.ArrayReader
16787 * @extends Roo.data.DataReader
16788 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16789 * Each element of that Array represents a row of data fields. The
16790 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16791 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16795 var RecordDef = Roo.data.Record.create([
16796 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16797 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16799 var myReader = new Roo.data.ArrayReader({
16800 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16804 * This would consume an Array like this:
16806 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16810 * Create a new JsonReader
16811 * @param {Object} meta Metadata configuration options.
16812 * @param {Object|Array} recordType Either an Array of field definition objects
16814 * @cfg {Array} fields Array of field definition objects
16815 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16816 * as specified to {@link Roo.data.Record#create},
16817 * or an {@link Roo.data.Record} object
16820 * created using {@link Roo.data.Record#create}.
16822 Roo.data.ArrayReader = function(meta, recordType)
16824 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16827 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16830 * Create a data block containing Roo.data.Records from an XML document.
16831 * @param {Object} o An Array of row objects which represents the dataset.
16832 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16833 * a cache of Roo.data.Records.
16835 readRecords : function(o)
16837 var sid = this.meta ? this.meta.id : null;
16838 var recordType = this.recordType, fields = recordType.prototype.fields;
16841 for(var i = 0; i < root.length; i++){
16844 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16845 for(var j = 0, jlen = fields.length; j < jlen; j++){
16846 var f = fields.items[j];
16847 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16848 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16850 values[f.name] = v;
16852 var record = new recordType(values, id);
16854 records[records.length] = record;
16858 totalRecords : records.length
16861 // used when loading children.. @see loadDataFromChildren
16862 toLoadData: function(rec)
16864 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16865 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16876 * @class Roo.bootstrap.form.ComboBox
16877 * @extends Roo.bootstrap.form.TriggerField
16878 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16879 * @cfg {Boolean} append (true|false) default false
16880 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16881 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16882 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16883 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16884 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16885 * @cfg {Boolean} animate default true
16886 * @cfg {Boolean} emptyResultText only for touch device
16887 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16888 * @cfg {String} emptyTitle default ''
16889 * @cfg {Number} width fixed with? experimental
16891 * Create a new ComboBox.
16892 * @param {Object} config Configuration options
16894 Roo.bootstrap.form.ComboBox = function(config){
16895 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16899 * Fires when the dropdown list is expanded
16900 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16905 * Fires when the dropdown list is collapsed
16906 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910 * @event beforeselect
16911 * Fires before a list item is selected. Return false to cancel the selection.
16912 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16913 * @param {Roo.data.Record} record The data record returned from the underlying store
16914 * @param {Number} index The index of the selected item in the dropdown list
16916 'beforeselect' : true,
16919 * Fires when a list item is selected
16920 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16921 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16922 * @param {Number} index The index of the selected item in the dropdown list
16926 * @event beforequery
16927 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16928 * The event object passed has these properties:
16929 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16930 * @param {String} query The query
16931 * @param {Boolean} forceAll true to force "all" query
16932 * @param {Boolean} cancel true to cancel the query
16933 * @param {Object} e The query event object
16935 'beforequery': true,
16938 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16939 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16944 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16945 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16946 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16951 * Fires when the remove value from the combobox array
16952 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16956 * @event afterremove
16957 * Fires when the remove value from the combobox array
16958 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16960 'afterremove' : true,
16962 * @event specialfilter
16963 * Fires when specialfilter
16964 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16966 'specialfilter' : true,
16969 * Fires when tick the element
16970 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16974 * @event touchviewdisplay
16975 * Fires when touch view require special display (default is using displayField)
16976 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16977 * @param {Object} cfg set html .
16979 'touchviewdisplay' : true
16984 this.tickItems = [];
16986 this.selectedIndex = -1;
16987 if(this.mode == 'local'){
16988 if(config.queryDelay === undefined){
16989 this.queryDelay = 10;
16991 if(config.minChars === undefined){
16997 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
17000 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
17001 * rendering into an Roo.Editor, defaults to false)
17004 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
17005 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
17008 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
17011 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
17012 * the dropdown list (defaults to undefined, with no header element)
17016 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
17020 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
17022 listWidth: undefined,
17024 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
17025 * mode = 'remote' or 'text' if mode = 'local')
17027 displayField: undefined,
17030 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
17031 * mode = 'remote' or 'value' if mode = 'local').
17032 * Note: use of a valueField requires the user make a selection
17033 * in order for a value to be mapped.
17035 valueField: undefined,
17037 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
17042 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
17043 * field's data value (defaults to the underlying DOM element's name)
17045 hiddenName: undefined,
17047 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17051 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17053 selectedClass: 'active',
17056 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17060 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17061 * anchor positions (defaults to 'tl-bl')
17063 listAlign: 'tl-bl?',
17065 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17069 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17070 * query specified by the allQuery config option (defaults to 'query')
17072 triggerAction: 'query',
17074 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17075 * (defaults to 4, does not apply if editable = false)
17079 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17080 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17084 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17085 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17089 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17090 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17094 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17095 * when editable = true (defaults to false)
17097 selectOnFocus:false,
17099 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17101 queryParam: 'query',
17103 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17104 * when mode = 'remote' (defaults to 'Loading...')
17106 loadingText: 'Loading...',
17108 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17112 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17116 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17117 * traditional select (defaults to true)
17121 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17125 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17129 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17130 * listWidth has a higher value)
17134 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17135 * allow the user to set arbitrary text into the field (defaults to false)
17137 forceSelection:false,
17139 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17140 * if typeAhead = true (defaults to 250)
17142 typeAheadDelay : 250,
17144 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17145 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17147 valueNotFoundText : undefined,
17149 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17151 blockFocus : false,
17154 * @cfg {Boolean} disableClear Disable showing of clear button.
17156 disableClear : false,
17158 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17160 alwaysQuery : false,
17163 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17168 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17170 invalidClass : "has-warning",
17173 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17175 validClass : "has-success",
17178 * @cfg {Boolean} specialFilter (true|false) special filter default false
17180 specialFilter : false,
17183 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17185 mobileTouchView : true,
17188 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17190 useNativeIOS : false,
17193 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17195 mobile_restrict_height : false,
17197 ios_options : false,
17209 btnPosition : 'right',
17210 triggerList : true,
17211 showToggleBtn : true,
17213 emptyResultText: 'Empty',
17214 triggerText : 'Select',
17218 // element that contains real text value.. (when hidden is used..)
17220 getAutoCreate : function()
17225 * Render classic select for iso
17228 if(Roo.isIOS && this.useNativeIOS){
17229 cfg = this.getAutoCreateNativeIOS();
17237 if(Roo.isTouch && this.mobileTouchView){
17238 cfg = this.getAutoCreateTouchView();
17245 if(!this.tickable){
17246 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17251 * ComboBox with tickable selections
17254 var align = this.labelAlign || this.parentLabelAlign();
17257 cls : 'form-group roo-combobox-tickable' //input-group
17260 var btn_text_select = '';
17261 var btn_text_done = '';
17262 var btn_text_cancel = '';
17264 if (this.btn_text_show) {
17265 btn_text_select = 'Select';
17266 btn_text_done = 'Done';
17267 btn_text_cancel = 'Cancel';
17272 cls : 'tickable-buttons',
17277 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17278 //html : this.triggerText
17279 html: btn_text_select
17285 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17287 html: btn_text_done
17293 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17295 html: btn_text_cancel
17301 buttons.cn.unshift({
17303 cls: 'roo-select2-search-field-input'
17309 Roo.each(buttons.cn, function(c){
17311 c.cls += ' btn-' + _this.size;
17314 if (_this.disabled) {
17321 style : 'display: contents',
17326 cls: 'form-hidden-field'
17330 cls: 'roo-select2-choices',
17334 cls: 'roo-select2-search-field',
17345 cls: 'roo-select2-container input-group roo-select2-container-multi',
17351 // cls: 'typeahead typeahead-long dropdown-menu',
17352 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17357 if(this.hasFeedback && !this.allowBlank){
17361 cls: 'glyphicon form-control-feedback'
17364 combobox.cn.push(feedback);
17371 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17372 tooltip : 'This field is required'
17374 if (Roo.bootstrap.version == 4) {
17377 style : 'display:none'
17380 if (align ==='left' && this.fieldLabel.length) {
17382 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17389 cls : 'control-label col-form-label',
17390 html : this.fieldLabel
17402 var labelCfg = cfg.cn[1];
17403 var contentCfg = cfg.cn[2];
17406 if(this.indicatorpos == 'right'){
17412 cls : 'control-label col-form-label',
17416 html : this.fieldLabel
17432 labelCfg = cfg.cn[0];
17433 contentCfg = cfg.cn[1];
17437 if(this.labelWidth > 12){
17438 labelCfg.style = "width: " + this.labelWidth + 'px';
17440 if(this.width * 1 > 0){
17441 contentCfg.style = "width: " + this.width + 'px';
17443 if(this.labelWidth < 13 && this.labelmd == 0){
17444 this.labelmd = this.labelWidth;
17447 if(this.labellg > 0){
17448 labelCfg.cls += ' col-lg-' + this.labellg;
17449 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17452 if(this.labelmd > 0){
17453 labelCfg.cls += ' col-md-' + this.labelmd;
17454 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17457 if(this.labelsm > 0){
17458 labelCfg.cls += ' col-sm-' + this.labelsm;
17459 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17462 if(this.labelxs > 0){
17463 labelCfg.cls += ' col-xs-' + this.labelxs;
17464 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17468 } else if ( this.fieldLabel.length) {
17469 // Roo.log(" label");
17474 //cls : 'input-group-addon',
17475 html : this.fieldLabel
17480 if(this.indicatorpos == 'right'){
17484 //cls : 'input-group-addon',
17485 html : this.fieldLabel
17495 // Roo.log(" no label && no align");
17502 ['xs','sm','md','lg'].map(function(size){
17503 if (settings[size]) {
17504 cfg.cls += ' col-' + size + '-' + settings[size];
17512 _initEventsCalled : false,
17515 initEvents: function()
17517 if (this._initEventsCalled) { // as we call render... prevent looping...
17520 this._initEventsCalled = true;
17523 throw "can not find store for combo";
17526 this.indicator = this.indicatorEl();
17528 this.store = Roo.factory(this.store, Roo.data);
17529 this.store.parent = this;
17531 // if we are building from html. then this element is so complex, that we can not really
17532 // use the rendered HTML.
17533 // so we have to trash and replace the previous code.
17534 if (Roo.XComponent.build_from_html) {
17535 // remove this element....
17536 var e = this.el.dom, k=0;
17537 while (e ) { e = e.previousSibling; ++k;}
17542 this.rendered = false;
17544 this.render(this.parent().getChildContainer(true), k);
17547 if(Roo.isIOS && this.useNativeIOS){
17548 this.initIOSView();
17556 if(Roo.isTouch && this.mobileTouchView){
17557 this.initTouchView();
17562 this.initTickableEvents();
17566 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17568 if(this.hiddenName){
17570 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17572 this.hiddenField.dom.value =
17573 this.hiddenValue !== undefined ? this.hiddenValue :
17574 this.value !== undefined ? this.value : '';
17576 // prevent input submission
17577 this.el.dom.removeAttribute('name');
17578 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17583 // this.el.dom.setAttribute('autocomplete', 'off');
17586 var cls = 'x-combo-list';
17588 //this.list = new Roo.Layer({
17589 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17595 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17596 _this.list.setWidth(lw);
17599 this.list.on('mouseover', this.onViewOver, this);
17600 this.list.on('mousemove', this.onViewMove, this);
17601 this.list.on('scroll', this.onViewScroll, this);
17604 this.list.swallowEvent('mousewheel');
17605 this.assetHeight = 0;
17608 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17609 this.assetHeight += this.header.getHeight();
17612 this.innerList = this.list.createChild({cls:cls+'-inner'});
17613 this.innerList.on('mouseover', this.onViewOver, this);
17614 this.innerList.on('mousemove', this.onViewMove, this);
17615 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17617 if(this.allowBlank && !this.pageSize && !this.disableClear){
17618 this.footer = this.list.createChild({cls:cls+'-ft'});
17619 this.pageTb = new Roo.Toolbar(this.footer);
17623 this.footer = this.list.createChild({cls:cls+'-ft'});
17624 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17625 {pageSize: this.pageSize});
17629 if (this.pageTb && this.allowBlank && !this.disableClear) {
17631 this.pageTb.add(new Roo.Toolbar.Fill(), {
17632 cls: 'x-btn-icon x-btn-clear',
17634 handler: function()
17637 _this.clearValue();
17638 _this.onSelect(false, -1);
17643 this.assetHeight += this.footer.getHeight();
17648 this.tpl = Roo.bootstrap.version == 4 ?
17649 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17650 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17653 this.view = new Roo.View(this.list, this.tpl, {
17654 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17656 //this.view.wrapEl.setDisplayed(false);
17657 this.view.on('click', this.onViewClick, this);
17660 this.store.on('beforeload', this.onBeforeLoad, this);
17661 this.store.on('load', this.onLoad, this);
17662 this.store.on('loadexception', this.onLoadException, this);
17664 if(this.resizable){
17665 this.resizer = new Roo.Resizable(this.list, {
17666 pinned:true, handles:'se'
17668 this.resizer.on('resize', function(r, w, h){
17669 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17670 this.listWidth = w;
17671 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17672 this.restrictHeight();
17674 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17677 if(!this.editable){
17678 this.editable = true;
17679 this.setEditable(false);
17684 if (typeof(this.events.add.listeners) != 'undefined') {
17686 this.addicon = this.wrap.createChild(
17687 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17689 this.addicon.on('click', function(e) {
17690 this.fireEvent('add', this);
17693 if (typeof(this.events.edit.listeners) != 'undefined') {
17695 this.editicon = this.wrap.createChild(
17696 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17697 if (this.addicon) {
17698 this.editicon.setStyle('margin-left', '40px');
17700 this.editicon.on('click', function(e) {
17702 // we fire even if inothing is selected..
17703 this.fireEvent('edit', this, this.lastData );
17709 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17710 "up" : function(e){
17711 this.inKeyMode = true;
17715 "down" : function(e){
17716 if(!this.isExpanded()){
17717 this.onTriggerClick();
17719 this.inKeyMode = true;
17724 "enter" : function(e){
17725 // this.onViewClick();
17729 if(this.fireEvent("specialkey", this, e)){
17730 this.onViewClick(false);
17736 "esc" : function(e){
17740 "tab" : function(e){
17743 if(this.fireEvent("specialkey", this, e)){
17744 this.onViewClick(false);
17752 doRelay : function(foo, bar, hname){
17753 if(hname == 'down' || this.scope.isExpanded()){
17754 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17763 this.queryDelay = Math.max(this.queryDelay || 10,
17764 this.mode == 'local' ? 10 : 250);
17767 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17769 if(this.typeAhead){
17770 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17772 if(this.editable !== false){
17773 this.inputEl().on("keyup", this.onKeyUp, this);
17775 if(this.forceSelection){
17776 this.inputEl().on('blur', this.doForce, this);
17780 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17781 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17785 initTickableEvents: function()
17789 if(this.hiddenName){
17791 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17793 this.hiddenField.dom.value =
17794 this.hiddenValue !== undefined ? this.hiddenValue :
17795 this.value !== undefined ? this.value : '';
17797 // prevent input submission
17798 this.el.dom.removeAttribute('name');
17799 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17804 // this.list = this.el.select('ul.dropdown-menu',true).first();
17806 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17807 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17808 if(this.triggerList){
17809 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17812 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17813 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17815 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17816 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17818 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17819 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17821 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17822 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17823 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17826 this.cancelBtn.hide();
17831 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17832 _this.list.setWidth(lw);
17835 this.list.on('mouseover', this.onViewOver, this);
17836 this.list.on('mousemove', this.onViewMove, this);
17838 this.list.on('scroll', this.onViewScroll, this);
17841 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17842 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17845 this.view = new Roo.View(this.list, this.tpl, {
17850 selectedClass: this.selectedClass
17853 //this.view.wrapEl.setDisplayed(false);
17854 this.view.on('click', this.onViewClick, this);
17858 this.store.on('beforeload', this.onBeforeLoad, this);
17859 this.store.on('load', this.onLoad, this);
17860 this.store.on('loadexception', this.onLoadException, this);
17863 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17864 "up" : function(e){
17865 this.inKeyMode = true;
17869 "down" : function(e){
17870 this.inKeyMode = true;
17874 "enter" : function(e){
17875 if(this.fireEvent("specialkey", this, e)){
17876 this.onViewClick(false);
17882 "esc" : function(e){
17883 this.onTickableFooterButtonClick(e, false, false);
17886 "tab" : function(e){
17887 this.fireEvent("specialkey", this, e);
17889 this.onTickableFooterButtonClick(e, false, false);
17896 doRelay : function(e, fn, key){
17897 if(this.scope.isExpanded()){
17898 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17907 this.queryDelay = Math.max(this.queryDelay || 10,
17908 this.mode == 'local' ? 10 : 250);
17911 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17913 if(this.typeAhead){
17914 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17917 if(this.editable !== false){
17918 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17921 this.indicator = this.indicatorEl();
17923 if(this.indicator){
17924 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17925 this.indicator.hide();
17930 onDestroy : function(){
17932 this.view.setStore(null);
17933 this.view.el.removeAllListeners();
17934 this.view.el.remove();
17935 this.view.purgeListeners();
17938 this.list.dom.innerHTML = '';
17942 this.store.un('beforeload', this.onBeforeLoad, this);
17943 this.store.un('load', this.onLoad, this);
17944 this.store.un('loadexception', this.onLoadException, this);
17946 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17950 fireKey : function(e){
17951 if(e.isNavKeyPress() && !this.list.isVisible()){
17952 this.fireEvent("specialkey", this, e);
17957 onResize: function(w, h)
17961 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17963 // if(typeof w != 'number'){
17964 // // we do not handle it!?!?
17967 // var tw = this.trigger.getWidth();
17968 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17969 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17971 // this.inputEl().setWidth( this.adjustWidth('input', x));
17973 // //this.trigger.setStyle('left', x+'px');
17975 // if(this.list && this.listWidth === undefined){
17976 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17977 // this.list.setWidth(lw);
17978 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17986 * Allow or prevent the user from directly editing the field text. If false is passed,
17987 * the user will only be able to select from the items defined in the dropdown list. This method
17988 * is the runtime equivalent of setting the 'editable' config option at config time.
17989 * @param {Boolean} value True to allow the user to directly edit the field text
17991 setEditable : function(value){
17992 if(value == this.editable){
17995 this.editable = value;
17997 this.inputEl().dom.setAttribute('readOnly', true);
17998 this.inputEl().on('mousedown', this.onTriggerClick, this);
17999 this.inputEl().addClass('x-combo-noedit');
18001 this.inputEl().dom.removeAttribute('readOnly');
18002 this.inputEl().un('mousedown', this.onTriggerClick, this);
18003 this.inputEl().removeClass('x-combo-noedit');
18009 onBeforeLoad : function(combo,opts){
18010 if(!this.hasFocus){
18014 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
18016 this.restrictHeight();
18017 this.selectedIndex = -1;
18021 onLoad : function(){
18023 this.hasQuery = false;
18025 if(!this.hasFocus){
18029 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18030 this.loading.hide();
18033 if(this.store.getCount() > 0){
18036 this.restrictHeight();
18037 if(this.lastQuery == this.allQuery){
18038 if(this.editable && !this.tickable){
18039 this.inputEl().dom.select();
18043 !this.selectByValue(this.value, true) &&
18046 !this.store.lastOptions ||
18047 typeof(this.store.lastOptions.add) == 'undefined' ||
18048 this.store.lastOptions.add != true
18051 this.select(0, true);
18054 if(this.autoFocus){
18057 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18058 this.taTask.delay(this.typeAheadDelay);
18062 this.onEmptyResults();
18068 onLoadException : function()
18070 this.hasQuery = false;
18072 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18073 this.loading.hide();
18076 if(this.tickable && this.editable){
18081 // only causes errors at present
18082 //Roo.log(this.store.reader.jsonData);
18083 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18085 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18091 onTypeAhead : function(){
18092 if(this.store.getCount() > 0){
18093 var r = this.store.getAt(0);
18094 var newValue = r.data[this.displayField];
18095 var len = newValue.length;
18096 var selStart = this.getRawValue().length;
18098 if(selStart != len){
18099 this.setRawValue(newValue);
18100 this.selectText(selStart, newValue.length);
18106 onSelect : function(record, index){
18108 if(this.fireEvent('beforeselect', this, record, index) !== false){
18110 this.setFromData(index > -1 ? record.data : false);
18113 this.fireEvent('select', this, record, index);
18118 * Returns the currently selected field value or empty string if no value is set.
18119 * @return {String} value The selected value
18121 getValue : function()
18123 if(Roo.isIOS && this.useNativeIOS){
18124 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18128 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18131 if(this.valueField){
18132 return typeof this.value != 'undefined' ? this.value : '';
18134 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18138 getRawValue : function()
18140 if(Roo.isIOS && this.useNativeIOS){
18141 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18144 var v = this.inputEl().getValue();
18150 * Clears any text/value currently set in the field
18152 clearValue : function(){
18154 if(this.hiddenField){
18155 this.hiddenField.dom.value = '';
18158 this.setRawValue('');
18159 this.lastSelectionText = '';
18160 this.lastData = false;
18162 var close = this.closeTriggerEl();
18173 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18174 * will be displayed in the field. If the value does not match the data value of an existing item,
18175 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18176 * Otherwise the field will be blank (although the value will still be set).
18177 * @param {String} value The value to match
18179 setValue : function(v)
18181 if(Roo.isIOS && this.useNativeIOS){
18182 this.setIOSValue(v);
18192 if(this.valueField){
18193 var r = this.findRecord(this.valueField, v);
18195 text = r.data[this.displayField];
18196 }else if(this.valueNotFoundText !== undefined){
18197 text = this.valueNotFoundText;
18200 this.lastSelectionText = text;
18201 if(this.hiddenField){
18202 this.hiddenField.dom.value = v;
18204 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18207 var close = this.closeTriggerEl();
18210 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18216 * @property {Object} the last set data for the element
18221 * Sets the value of the field based on a object which is related to the record format for the store.
18222 * @param {Object} value the value to set as. or false on reset?
18224 setFromData : function(o){
18231 var dv = ''; // display value
18232 var vv = ''; // value value..
18234 if (this.displayField) {
18235 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18237 // this is an error condition!!!
18238 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18241 if(this.valueField){
18242 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18245 var close = this.closeTriggerEl();
18248 if(dv.length || vv * 1 > 0){
18250 this.blockFocus=true;
18256 if(this.hiddenField){
18257 this.hiddenField.dom.value = vv;
18259 this.lastSelectionText = dv;
18260 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18264 // no hidden field.. - we store the value in 'value', but still display
18265 // display field!!!!
18266 this.lastSelectionText = dv;
18267 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18274 reset : function(){
18275 // overridden so that last data is reset..
18282 this.setValue(this.originalValue);
18283 //this.clearInvalid();
18284 this.lastData = false;
18286 this.view.clearSelections();
18292 findRecord : function(prop, value){
18294 if(this.store.getCount() > 0){
18295 this.store.each(function(r){
18296 if(r.data[prop] == value){
18306 getName: function()
18308 // returns hidden if it's set..
18309 if (!this.rendered) {return ''};
18310 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18314 onViewMove : function(e, t){
18315 this.inKeyMode = false;
18319 onViewOver : function(e, t){
18320 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18323 var item = this.view.findItemFromChild(t);
18326 var index = this.view.indexOf(item);
18327 this.select(index, false);
18332 onViewClick : function(view, doFocus, el, e)
18334 var index = this.view.getSelectedIndexes()[0];
18336 var r = this.store.getAt(index);
18340 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18347 Roo.each(this.tickItems, function(v,k){
18349 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18351 _this.tickItems.splice(k, 1);
18353 if(typeof(e) == 'undefined' && view == false){
18354 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18366 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18367 this.tickItems.push(r.data);
18370 if(typeof(e) == 'undefined' && view == false){
18371 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18378 this.onSelect(r, index);
18380 if(doFocus !== false && !this.blockFocus){
18381 this.inputEl().focus();
18386 restrictHeight : function(){
18387 //this.innerList.dom.style.height = '';
18388 //var inner = this.innerList.dom;
18389 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18390 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18391 //this.list.beginUpdate();
18392 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18393 this.list.alignTo(this.inputEl(), this.listAlign);
18394 this.list.alignTo(this.inputEl(), this.listAlign);
18395 //this.list.endUpdate();
18399 onEmptyResults : function(){
18401 if(this.tickable && this.editable){
18402 this.hasFocus = false;
18403 this.restrictHeight();
18411 * Returns true if the dropdown list is expanded, else false.
18413 isExpanded : function(){
18414 return this.list.isVisible();
18418 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18419 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18420 * @param {String} value The data value of the item to select
18421 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18422 * selected item if it is not currently in view (defaults to true)
18423 * @return {Boolean} True if the value matched an item in the list, else false
18425 selectByValue : function(v, scrollIntoView){
18426 if(v !== undefined && v !== null){
18427 var r = this.findRecord(this.valueField || this.displayField, v);
18429 this.select(this.store.indexOf(r), scrollIntoView);
18437 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18438 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18439 * @param {Number} index The zero-based index of the list item to select
18440 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18441 * selected item if it is not currently in view (defaults to true)
18443 select : function(index, scrollIntoView){
18444 this.selectedIndex = index;
18445 this.view.select(index);
18446 if(scrollIntoView !== false){
18447 var el = this.view.getNode(index);
18449 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18452 this.list.scrollChildIntoView(el, false);
18458 selectNext : function(){
18459 var ct = this.store.getCount();
18461 if(this.selectedIndex == -1){
18463 }else if(this.selectedIndex < ct-1){
18464 this.select(this.selectedIndex+1);
18470 selectPrev : function(){
18471 var ct = this.store.getCount();
18473 if(this.selectedIndex == -1){
18475 }else if(this.selectedIndex != 0){
18476 this.select(this.selectedIndex-1);
18482 onKeyUp : function(e){
18483 if(this.editable !== false && !e.isSpecialKey()){
18484 this.lastKey = e.getKey();
18485 this.dqTask.delay(this.queryDelay);
18490 validateBlur : function(){
18491 return !this.list || !this.list.isVisible();
18495 initQuery : function(){
18497 var v = this.getRawValue();
18499 if(this.tickable && this.editable){
18500 v = this.tickableInputEl().getValue();
18507 doForce : function(){
18508 if(this.inputEl().dom.value.length > 0){
18509 this.inputEl().dom.value =
18510 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18516 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18517 * query allowing the query action to be canceled if needed.
18518 * @param {String} query The SQL query to execute
18519 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18520 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18521 * saved in the current store (defaults to false)
18523 doQuery : function(q, forceAll){
18525 if(q === undefined || q === null){
18530 forceAll: forceAll,
18534 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18539 forceAll = qe.forceAll;
18540 if(forceAll === true || (q.length >= this.minChars)){
18542 this.hasQuery = true;
18544 if(this.lastQuery != q || this.alwaysQuery){
18545 this.lastQuery = q;
18546 if(this.mode == 'local'){
18547 this.selectedIndex = -1;
18549 this.store.clearFilter();
18552 if(this.specialFilter){
18553 this.fireEvent('specialfilter', this);
18558 this.store.filter(this.displayField, q);
18561 this.store.fireEvent("datachanged", this.store);
18568 this.store.baseParams[this.queryParam] = q;
18570 var options = {params : this.getParams(q)};
18573 options.add = true;
18574 options.params.start = this.page * this.pageSize;
18577 this.store.load(options);
18580 * this code will make the page width larger, at the beginning, the list not align correctly,
18581 * we should expand the list on onLoad
18582 * so command out it
18587 this.selectedIndex = -1;
18592 this.loadNext = false;
18596 getParams : function(q){
18598 //p[this.queryParam] = q;
18602 p.limit = this.pageSize;
18608 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18610 collapse : function(){
18611 if(!this.isExpanded()){
18617 this.hasFocus = false;
18621 this.cancelBtn.hide();
18622 this.trigger.show();
18625 this.tickableInputEl().dom.value = '';
18626 this.tickableInputEl().blur();
18631 Roo.get(document).un('mousedown', this.collapseIf, this);
18632 Roo.get(document).un('mousewheel', this.collapseIf, this);
18633 if (!this.editable) {
18634 Roo.get(document).un('keydown', this.listKeyPress, this);
18636 this.fireEvent('collapse', this);
18642 collapseIf : function(e){
18643 var in_combo = e.within(this.el);
18644 var in_list = e.within(this.list);
18645 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18647 if (in_combo || in_list || is_list) {
18648 //e.stopPropagation();
18653 this.onTickableFooterButtonClick(e, false, false);
18661 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18663 expand : function(){
18665 if(this.isExpanded() || !this.hasFocus){
18669 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18670 this.list.setWidth(lw);
18676 this.restrictHeight();
18680 this.tickItems = Roo.apply([], this.item);
18683 this.cancelBtn.show();
18684 this.trigger.hide();
18687 this.tickableInputEl().focus();
18692 Roo.get(document).on('mousedown', this.collapseIf, this);
18693 Roo.get(document).on('mousewheel', this.collapseIf, this);
18694 if (!this.editable) {
18695 Roo.get(document).on('keydown', this.listKeyPress, this);
18698 this.fireEvent('expand', this);
18702 // Implements the default empty TriggerField.onTriggerClick function
18703 onTriggerClick : function(e)
18705 Roo.log('trigger click');
18707 if(this.disabled || !this.triggerList){
18712 this.loadNext = false;
18714 if(this.isExpanded()){
18716 if (!this.blockFocus) {
18717 this.inputEl().focus();
18721 this.hasFocus = true;
18722 if(this.triggerAction == 'all') {
18723 this.doQuery(this.allQuery, true);
18725 this.doQuery(this.getRawValue());
18727 if (!this.blockFocus) {
18728 this.inputEl().focus();
18733 onTickableTriggerClick : function(e)
18740 this.loadNext = false;
18741 this.hasFocus = true;
18743 if(this.triggerAction == 'all') {
18744 this.doQuery(this.allQuery, true);
18746 this.doQuery(this.getRawValue());
18750 onSearchFieldClick : function(e)
18752 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18753 this.onTickableFooterButtonClick(e, false, false);
18757 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18762 this.loadNext = false;
18763 this.hasFocus = true;
18765 if(this.triggerAction == 'all') {
18766 this.doQuery(this.allQuery, true);
18768 this.doQuery(this.getRawValue());
18772 listKeyPress : function(e)
18774 //Roo.log('listkeypress');
18775 // scroll to first matching element based on key pres..
18776 if (e.isSpecialKey()) {
18779 var k = String.fromCharCode(e.getKey()).toUpperCase();
18782 var csel = this.view.getSelectedNodes();
18783 var cselitem = false;
18785 var ix = this.view.indexOf(csel[0]);
18786 cselitem = this.store.getAt(ix);
18787 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18793 this.store.each(function(v) {
18795 // start at existing selection.
18796 if (cselitem.id == v.id) {
18802 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18803 match = this.store.indexOf(v);
18809 if (match === false) {
18810 return true; // no more action?
18813 this.view.select(match);
18814 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18815 sn.scrollIntoView(sn.dom.parentNode, false);
18818 onViewScroll : function(e, t){
18820 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){
18824 this.hasQuery = true;
18826 this.loading = this.list.select('.loading', true).first();
18828 if(this.loading === null){
18829 this.list.createChild({
18831 cls: 'loading roo-select2-more-results roo-select2-active',
18832 html: 'Loading more results...'
18835 this.loading = this.list.select('.loading', true).first();
18837 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18839 this.loading.hide();
18842 this.loading.show();
18847 this.loadNext = true;
18849 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18854 addItem : function(o)
18856 var dv = ''; // display value
18858 if (this.displayField) {
18859 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18861 // this is an error condition!!!
18862 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18869 var choice = this.choices.createChild({
18871 cls: 'roo-select2-search-choice',
18880 cls: 'roo-select2-search-choice-close fa fa-times',
18885 }, this.searchField);
18887 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18889 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18897 this.inputEl().dom.value = '';
18902 onRemoveItem : function(e, _self, o)
18904 e.preventDefault();
18906 this.lastItem = Roo.apply([], this.item);
18908 var index = this.item.indexOf(o.data) * 1;
18911 Roo.log('not this item?!');
18915 this.item.splice(index, 1);
18920 this.fireEvent('remove', this, e);
18926 syncValue : function()
18928 if(!this.item.length){
18935 Roo.each(this.item, function(i){
18936 if(_this.valueField){
18937 value.push(i[_this.valueField]);
18944 this.value = value.join(',');
18946 if(this.hiddenField){
18947 this.hiddenField.dom.value = this.value;
18950 this.store.fireEvent("datachanged", this.store);
18955 clearItem : function()
18957 if(!this.multiple){
18963 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18971 if(this.tickable && !Roo.isTouch){
18972 this.view.refresh();
18976 inputEl: function ()
18978 if(Roo.isIOS && this.useNativeIOS){
18979 return this.el.select('select.roo-ios-select', true).first();
18982 if(Roo.isTouch && this.mobileTouchView){
18983 return this.el.select('input.form-control',true).first();
18987 return this.searchField;
18990 return this.el.select('input.form-control',true).first();
18993 onTickableFooterButtonClick : function(e, btn, el)
18995 e.preventDefault();
18997 this.lastItem = Roo.apply([], this.item);
18999 if(btn && btn.name == 'cancel'){
19000 this.tickItems = Roo.apply([], this.item);
19009 Roo.each(this.tickItems, function(o){
19017 validate : function()
19019 if(this.getVisibilityEl().hasClass('hidden')){
19023 var v = this.getRawValue();
19026 v = this.getValue();
19029 if(this.disabled || this.allowBlank || v.length){
19034 this.markInvalid();
19038 tickableInputEl : function()
19040 if(!this.tickable || !this.editable){
19041 return this.inputEl();
19044 return this.inputEl().select('.roo-select2-search-field-input', true).first();
19048 getAutoCreateTouchView : function()
19053 cls: 'form-group' //input-group
19059 type : this.inputType,
19060 cls : 'form-control x-combo-noedit',
19061 autocomplete: 'new-password',
19062 placeholder : this.placeholder || '',
19067 input.name = this.name;
19071 input.cls += ' input-' + this.size;
19074 if (this.disabled) {
19075 input.disabled = true;
19079 cls : 'roo-combobox-wrap',
19086 inputblock.cls += ' input-group';
19088 inputblock.cn.unshift({
19090 cls : 'input-group-addon input-group-prepend input-group-text',
19095 if(this.removable && !this.multiple){
19096 inputblock.cls += ' roo-removable';
19098 inputblock.cn.push({
19101 cls : 'roo-combo-removable-btn close'
19105 if(this.hasFeedback && !this.allowBlank){
19107 inputblock.cls += ' has-feedback';
19109 inputblock.cn.push({
19111 cls: 'glyphicon form-control-feedback'
19118 inputblock.cls += (this.before) ? '' : ' input-group';
19120 inputblock.cn.push({
19122 cls : 'input-group-addon input-group-append input-group-text',
19128 var ibwrap = inputblock;
19133 cls: 'roo-select2-choices',
19137 cls: 'roo-select2-search-field',
19150 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19155 cls: 'form-hidden-field'
19161 if(!this.multiple && this.showToggleBtn){
19167 if (this.caret != false) {
19170 cls: 'fa fa-' + this.caret
19177 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19179 Roo.bootstrap.version == 3 ? caret : '',
19182 cls: 'combobox-clear',
19196 combobox.cls += ' roo-select2-container-multi';
19199 var required = this.allowBlank ? {
19201 style: 'display: none'
19204 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19205 tooltip : 'This field is required'
19208 var align = this.labelAlign || this.parentLabelAlign();
19210 if (align ==='left' && this.fieldLabel.length) {
19216 cls : 'control-label col-form-label',
19217 html : this.fieldLabel
19221 cls : 'roo-combobox-wrap ',
19228 var labelCfg = cfg.cn[1];
19229 var contentCfg = cfg.cn[2];
19232 if(this.indicatorpos == 'right'){
19237 cls : 'control-label col-form-label',
19241 html : this.fieldLabel
19247 cls : "roo-combobox-wrap ",
19255 labelCfg = cfg.cn[0];
19256 contentCfg = cfg.cn[1];
19261 if(this.labelWidth > 12){
19262 labelCfg.style = "width: " + this.labelWidth + 'px';
19265 if(this.labelWidth < 13 && this.labelmd == 0){
19266 this.labelmd = this.labelWidth;
19269 if(this.labellg > 0){
19270 labelCfg.cls += ' col-lg-' + this.labellg;
19271 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19274 if(this.labelmd > 0){
19275 labelCfg.cls += ' col-md-' + this.labelmd;
19276 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19279 if(this.labelsm > 0){
19280 labelCfg.cls += ' col-sm-' + this.labelsm;
19281 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19284 if(this.labelxs > 0){
19285 labelCfg.cls += ' col-xs-' + this.labelxs;
19286 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19290 } else if ( this.fieldLabel.length) {
19295 cls : 'control-label',
19296 html : this.fieldLabel
19307 if(this.indicatorpos == 'right'){
19311 cls : 'control-label',
19312 html : this.fieldLabel,
19330 var settings = this;
19332 ['xs','sm','md','lg'].map(function(size){
19333 if (settings[size]) {
19334 cfg.cls += ' col-' + size + '-' + settings[size];
19341 initTouchView : function()
19343 this.renderTouchView();
19345 this.touchViewEl.on('scroll', function(){
19346 this.el.dom.scrollTop = 0;
19349 this.originalValue = this.getValue();
19351 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19353 this.inputEl().on("click", this.showTouchView, this);
19354 if (this.triggerEl) {
19355 this.triggerEl.on("click", this.showTouchView, this);
19359 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19360 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19362 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19364 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19365 this.store.on('load', this.onTouchViewLoad, this);
19366 this.store.on('loadexception', this.onTouchViewLoadException, this);
19368 if(this.hiddenName){
19370 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19372 this.hiddenField.dom.value =
19373 this.hiddenValue !== undefined ? this.hiddenValue :
19374 this.value !== undefined ? this.value : '';
19376 this.el.dom.removeAttribute('name');
19377 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19381 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19382 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19385 if(this.removable && !this.multiple){
19386 var close = this.closeTriggerEl();
19388 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19389 close.on('click', this.removeBtnClick, this, close);
19393 * fix the bug in Safari iOS8
19395 this.inputEl().on("focus", function(e){
19396 document.activeElement.blur();
19399 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19406 renderTouchView : function()
19408 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19409 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19411 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19412 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19414 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19415 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19416 this.touchViewBodyEl.setStyle('overflow', 'auto');
19418 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19419 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19421 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19422 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19426 showTouchView : function()
19432 this.touchViewHeaderEl.hide();
19434 if(this.modalTitle.length){
19435 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19436 this.touchViewHeaderEl.show();
19439 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19440 this.touchViewEl.show();
19442 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19444 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19445 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19447 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19449 if(this.modalTitle.length){
19450 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19453 this.touchViewBodyEl.setHeight(bodyHeight);
19457 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19459 this.touchViewEl.addClass(['in','show']);
19462 if(this._touchViewMask){
19463 Roo.get(document.body).addClass("x-body-masked");
19464 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19465 this._touchViewMask.setStyle('z-index', 10000);
19466 this._touchViewMask.addClass('show');
19469 this.doTouchViewQuery();
19473 hideTouchView : function()
19475 this.touchViewEl.removeClass(['in','show']);
19479 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19481 this.touchViewEl.setStyle('display', 'none');
19484 if(this._touchViewMask){
19485 this._touchViewMask.removeClass('show');
19486 Roo.get(document.body).removeClass("x-body-masked");
19490 setTouchViewValue : function()
19497 Roo.each(this.tickItems, function(o){
19502 this.hideTouchView();
19505 doTouchViewQuery : function()
19514 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19518 if(!this.alwaysQuery || this.mode == 'local'){
19519 this.onTouchViewLoad();
19526 onTouchViewBeforeLoad : function(combo,opts)
19532 onTouchViewLoad : function()
19534 if(this.store.getCount() < 1){
19535 this.onTouchViewEmptyResults();
19539 this.clearTouchView();
19541 var rawValue = this.getRawValue();
19543 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19545 this.tickItems = [];
19547 this.store.data.each(function(d, rowIndex){
19548 var row = this.touchViewListGroup.createChild(template);
19550 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19551 row.addClass(d.data.cls);
19554 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19557 html : d.data[this.displayField]
19560 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19561 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19564 row.removeClass('selected');
19565 if(!this.multiple && this.valueField &&
19566 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19569 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19570 row.addClass('selected');
19573 if(this.multiple && this.valueField &&
19574 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19578 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19579 this.tickItems.push(d.data);
19582 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19586 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19588 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19590 if(this.modalTitle.length){
19591 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19594 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19596 if(this.mobile_restrict_height && listHeight < bodyHeight){
19597 this.touchViewBodyEl.setHeight(listHeight);
19602 if(firstChecked && listHeight > bodyHeight){
19603 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19608 onTouchViewLoadException : function()
19610 this.hideTouchView();
19613 onTouchViewEmptyResults : function()
19615 this.clearTouchView();
19617 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19619 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19623 clearTouchView : function()
19625 this.touchViewListGroup.dom.innerHTML = '';
19628 onTouchViewClick : function(e, el, o)
19630 e.preventDefault();
19633 var rowIndex = o.rowIndex;
19635 var r = this.store.getAt(rowIndex);
19637 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19639 if(!this.multiple){
19640 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19641 c.dom.removeAttribute('checked');
19644 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19646 this.setFromData(r.data);
19648 var close = this.closeTriggerEl();
19654 this.hideTouchView();
19656 this.fireEvent('select', this, r, rowIndex);
19661 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19662 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19663 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19667 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19668 this.addItem(r.data);
19669 this.tickItems.push(r.data);
19673 getAutoCreateNativeIOS : function()
19676 cls: 'form-group' //input-group,
19681 cls : 'roo-ios-select'
19685 combobox.name = this.name;
19688 if (this.disabled) {
19689 combobox.disabled = true;
19692 var settings = this;
19694 ['xs','sm','md','lg'].map(function(size){
19695 if (settings[size]) {
19696 cfg.cls += ' col-' + size + '-' + settings[size];
19706 initIOSView : function()
19708 this.store.on('load', this.onIOSViewLoad, this);
19713 onIOSViewLoad : function()
19715 if(this.store.getCount() < 1){
19719 this.clearIOSView();
19721 if(this.allowBlank) {
19723 var default_text = '-- SELECT --';
19725 if(this.placeholder.length){
19726 default_text = this.placeholder;
19729 if(this.emptyTitle.length){
19730 default_text += ' - ' + this.emptyTitle + ' -';
19733 var opt = this.inputEl().createChild({
19736 html : default_text
19740 o[this.valueField] = 0;
19741 o[this.displayField] = default_text;
19743 this.ios_options.push({
19750 this.store.data.each(function(d, rowIndex){
19754 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19755 html = d.data[this.displayField];
19760 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19761 value = d.data[this.valueField];
19770 if(this.value == d.data[this.valueField]){
19771 option['selected'] = true;
19774 var opt = this.inputEl().createChild(option);
19776 this.ios_options.push({
19783 this.inputEl().on('change', function(){
19784 this.fireEvent('select', this);
19789 clearIOSView: function()
19791 this.inputEl().dom.innerHTML = '';
19793 this.ios_options = [];
19796 setIOSValue: function(v)
19800 if(!this.ios_options){
19804 Roo.each(this.ios_options, function(opts){
19806 opts.el.dom.removeAttribute('selected');
19808 if(opts.data[this.valueField] != v){
19812 opts.el.dom.setAttribute('selected', true);
19818 * @cfg {Boolean} grow
19822 * @cfg {Number} growMin
19826 * @cfg {Number} growMax
19835 Roo.apply(Roo.bootstrap.form.ComboBox, {
19839 cls: 'modal-header',
19861 cls: 'list-group-item',
19865 cls: 'roo-combobox-list-group-item-value'
19869 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19883 listItemCheckbox : {
19885 cls: 'list-group-item',
19889 cls: 'roo-combobox-list-group-item-value'
19893 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19909 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19914 cls: 'modal-footer',
19922 cls: 'col-xs-6 text-left',
19925 cls: 'btn btn-danger roo-touch-view-cancel',
19931 cls: 'col-xs-6 text-right',
19934 cls: 'btn btn-success roo-touch-view-ok',
19945 Roo.apply(Roo.bootstrap.form.ComboBox, {
19947 touchViewTemplate : {
19949 cls: 'modal fade roo-combobox-touch-view',
19953 cls: 'modal-dialog',
19954 style : 'position:fixed', // we have to fix position....
19958 cls: 'modal-content',
19960 Roo.bootstrap.form.ComboBox.header,
19961 Roo.bootstrap.form.ComboBox.body,
19962 Roo.bootstrap.form.ComboBox.footer
19971 * Ext JS Library 1.1.1
19972 * Copyright(c) 2006-2007, Ext JS, LLC.
19974 * Originally Released Under LGPL - original licence link has changed is not relivant.
19977 * <script type="text/javascript">
19982 * @extends Roo.util.Observable
19983 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19984 * This class also supports single and multi selection modes. <br>
19985 * Create a data model bound view:
19987 var store = new Roo.data.Store(...);
19989 var view = new Roo.View({
19991 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19993 singleSelect: true,
19994 selectedClass: "ydataview-selected",
19998 // listen for node click?
19999 view.on("click", function(vw, index, node, e){
20000 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
20004 dataModel.load("foobar.xml");
20006 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
20008 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
20009 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
20011 * Note: old style constructor is still suported (container, template, config)
20014 * Create a new View
20015 * @param {Object} config The config object
20018 Roo.View = function(config, depreciated_tpl, depreciated_config){
20020 this.parent = false;
20022 if (typeof(depreciated_tpl) == 'undefined') {
20023 // new way.. - universal constructor.
20024 Roo.apply(this, config);
20025 this.el = Roo.get(this.el);
20028 this.el = Roo.get(config);
20029 this.tpl = depreciated_tpl;
20030 Roo.apply(this, depreciated_config);
20032 this.wrapEl = this.el.wrap().wrap();
20033 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
20036 if(typeof(this.tpl) == "string"){
20037 this.tpl = new Roo.Template(this.tpl);
20039 // support xtype ctors..
20040 this.tpl = new Roo.factory(this.tpl, Roo);
20044 this.tpl.compile();
20049 * @event beforeclick
20050 * Fires before a click is processed. Returns false to cancel the default action.
20051 * @param {Roo.View} this
20052 * @param {Number} index The index of the target node
20053 * @param {HTMLElement} node The target node
20054 * @param {Roo.EventObject} e The raw event object
20056 "beforeclick" : true,
20059 * Fires when a template node is clicked.
20060 * @param {Roo.View} this
20061 * @param {Number} index The index of the target node
20062 * @param {HTMLElement} node The target node
20063 * @param {Roo.EventObject} e The raw event object
20068 * Fires when a template node is double clicked.
20069 * @param {Roo.View} this
20070 * @param {Number} index The index of the target node
20071 * @param {HTMLElement} node The target node
20072 * @param {Roo.EventObject} e The raw event object
20076 * @event contextmenu
20077 * Fires when a template node is right clicked.
20078 * @param {Roo.View} this
20079 * @param {Number} index The index of the target node
20080 * @param {HTMLElement} node The target node
20081 * @param {Roo.EventObject} e The raw event object
20083 "contextmenu" : true,
20085 * @event selectionchange
20086 * Fires when the selected nodes change.
20087 * @param {Roo.View} this
20088 * @param {Array} selections Array of the selected nodes
20090 "selectionchange" : true,
20093 * @event beforeselect
20094 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20095 * @param {Roo.View} this
20096 * @param {HTMLElement} node The node to be selected
20097 * @param {Array} selections Array of currently selected nodes
20099 "beforeselect" : true,
20101 * @event preparedata
20102 * Fires on every row to render, to allow you to change the data.
20103 * @param {Roo.View} this
20104 * @param {Object} data to be rendered (change this)
20106 "preparedata" : true
20114 "click": this.onClick,
20115 "dblclick": this.onDblClick,
20116 "contextmenu": this.onContextMenu,
20120 this.selections = [];
20122 this.cmp = new Roo.CompositeElementLite([]);
20124 this.store = Roo.factory(this.store, Roo.data);
20125 this.setStore(this.store, true);
20128 if ( this.footer && this.footer.xtype) {
20130 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20132 this.footer.dataSource = this.store;
20133 this.footer.container = fctr;
20134 this.footer = Roo.factory(this.footer, Roo);
20135 fctr.insertFirst(this.el);
20137 // this is a bit insane - as the paging toolbar seems to detach the el..
20138 // dom.parentNode.parentNode.parentNode
20139 // they get detached?
20143 Roo.View.superclass.constructor.call(this);
20148 Roo.extend(Roo.View, Roo.util.Observable, {
20151 * @cfg {Roo.data.Store} store Data store to load data from.
20156 * @cfg {String|Roo.Element} el The container element.
20161 * @cfg {String|Roo.Template} tpl The template used by this View
20165 * @cfg {String} dataName the named area of the template to use as the data area
20166 * Works with domtemplates roo-name="name"
20170 * @cfg {String} selectedClass The css class to add to selected nodes
20172 selectedClass : "x-view-selected",
20174 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20179 * @cfg {String} text to display on mask (default Loading)
20183 * @cfg {Boolean} multiSelect Allow multiple selection
20185 multiSelect : false,
20187 * @cfg {Boolean} singleSelect Allow single selection
20189 singleSelect: false,
20192 * @cfg {Boolean} toggleSelect - selecting
20194 toggleSelect : false,
20197 * @cfg {Boolean} tickable - selecting
20202 * Returns the element this view is bound to.
20203 * @return {Roo.Element}
20205 getEl : function(){
20206 return this.wrapEl;
20212 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20214 refresh : function(){
20215 //Roo.log('refresh');
20218 // if we are using something like 'domtemplate', then
20219 // the what gets used is:
20220 // t.applySubtemplate(NAME, data, wrapping data..)
20221 // the outer template then get' applied with
20222 // the store 'extra data'
20223 // and the body get's added to the
20224 // roo-name="data" node?
20225 // <span class='roo-tpl-{name}'></span> ?????
20229 this.clearSelections();
20230 this.el.update("");
20232 var records = this.store.getRange();
20233 if(records.length < 1) {
20235 // is this valid?? = should it render a template??
20237 this.el.update(this.emptyText);
20241 if (this.dataName) {
20242 this.el.update(t.apply(this.store.meta)); //????
20243 el = this.el.child('.roo-tpl-' + this.dataName);
20246 for(var i = 0, len = records.length; i < len; i++){
20247 var data = this.prepareData(records[i].data, i, records[i]);
20248 this.fireEvent("preparedata", this, data, i, records[i]);
20250 var d = Roo.apply({}, data);
20253 Roo.apply(d, {'roo-id' : Roo.id()});
20257 Roo.each(this.parent.item, function(item){
20258 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20261 Roo.apply(d, {'roo-data-checked' : 'checked'});
20265 html[html.length] = Roo.util.Format.trim(
20267 t.applySubtemplate(this.dataName, d, this.store.meta) :
20274 el.update(html.join(""));
20275 this.nodes = el.dom.childNodes;
20276 this.updateIndexes(0);
20281 * Function to override to reformat the data that is sent to
20282 * the template for each node.
20283 * DEPRICATED - use the preparedata event handler.
20284 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20285 * a JSON object for an UpdateManager bound view).
20287 prepareData : function(data, index, record)
20289 this.fireEvent("preparedata", this, data, index, record);
20293 onUpdate : function(ds, record){
20294 // Roo.log('on update');
20295 this.clearSelections();
20296 var index = this.store.indexOf(record);
20297 var n = this.nodes[index];
20298 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20299 n.parentNode.removeChild(n);
20300 this.updateIndexes(index, index);
20306 onAdd : function(ds, records, index)
20308 //Roo.log(['on Add', ds, records, index] );
20309 this.clearSelections();
20310 if(this.nodes.length == 0){
20314 var n = this.nodes[index];
20315 for(var i = 0, len = records.length; i < len; i++){
20316 var d = this.prepareData(records[i].data, i, records[i]);
20318 this.tpl.insertBefore(n, d);
20321 this.tpl.append(this.el, d);
20324 this.updateIndexes(index);
20327 onRemove : function(ds, record, index){
20328 // Roo.log('onRemove');
20329 this.clearSelections();
20330 var el = this.dataName ?
20331 this.el.child('.roo-tpl-' + this.dataName) :
20334 el.dom.removeChild(this.nodes[index]);
20335 this.updateIndexes(index);
20339 * Refresh an individual node.
20340 * @param {Number} index
20342 refreshNode : function(index){
20343 this.onUpdate(this.store, this.store.getAt(index));
20346 updateIndexes : function(startIndex, endIndex){
20347 var ns = this.nodes;
20348 startIndex = startIndex || 0;
20349 endIndex = endIndex || ns.length - 1;
20350 for(var i = startIndex; i <= endIndex; i++){
20351 ns[i].nodeIndex = i;
20356 * Changes the data store this view uses and refresh the view.
20357 * @param {Store} store
20359 setStore : function(store, initial){
20360 if(!initial && this.store){
20361 this.store.un("datachanged", this.refresh);
20362 this.store.un("add", this.onAdd);
20363 this.store.un("remove", this.onRemove);
20364 this.store.un("update", this.onUpdate);
20365 this.store.un("clear", this.refresh);
20366 this.store.un("beforeload", this.onBeforeLoad);
20367 this.store.un("load", this.onLoad);
20368 this.store.un("loadexception", this.onLoad);
20372 store.on("datachanged", this.refresh, this);
20373 store.on("add", this.onAdd, this);
20374 store.on("remove", this.onRemove, this);
20375 store.on("update", this.onUpdate, this);
20376 store.on("clear", this.refresh, this);
20377 store.on("beforeload", this.onBeforeLoad, this);
20378 store.on("load", this.onLoad, this);
20379 store.on("loadexception", this.onLoad, this);
20387 * onbeforeLoad - masks the loading area.
20390 onBeforeLoad : function(store,opts)
20392 //Roo.log('onBeforeLoad');
20394 this.el.update("");
20396 this.el.mask(this.mask ? this.mask : "Loading" );
20398 onLoad : function ()
20405 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20406 * @param {HTMLElement} node
20407 * @return {HTMLElement} The template node
20409 findItemFromChild : function(node){
20410 var el = this.dataName ?
20411 this.el.child('.roo-tpl-' + this.dataName,true) :
20414 if(!node || node.parentNode == el){
20417 var p = node.parentNode;
20418 while(p && p != el){
20419 if(p.parentNode == el){
20428 onClick : function(e){
20429 var item = this.findItemFromChild(e.getTarget());
20431 var index = this.indexOf(item);
20432 if(this.onItemClick(item, index, e) !== false){
20433 this.fireEvent("click", this, index, item, e);
20436 this.clearSelections();
20441 onContextMenu : function(e){
20442 var item = this.findItemFromChild(e.getTarget());
20444 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20449 onDblClick : function(e){
20450 var item = this.findItemFromChild(e.getTarget());
20452 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20456 onItemClick : function(item, index, e)
20458 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20461 if (this.toggleSelect) {
20462 var m = this.isSelected(item) ? 'unselect' : 'select';
20465 _t[m](item, true, false);
20468 if(this.multiSelect || this.singleSelect){
20469 if(this.multiSelect && e.shiftKey && this.lastSelection){
20470 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20472 this.select(item, this.multiSelect && e.ctrlKey);
20473 this.lastSelection = item;
20476 if(!this.tickable){
20477 e.preventDefault();
20485 * Get the number of selected nodes.
20488 getSelectionCount : function(){
20489 return this.selections.length;
20493 * Get the currently selected nodes.
20494 * @return {Array} An array of HTMLElements
20496 getSelectedNodes : function(){
20497 return this.selections;
20501 * Get the indexes of the selected nodes.
20504 getSelectedIndexes : function(){
20505 var indexes = [], s = this.selections;
20506 for(var i = 0, len = s.length; i < len; i++){
20507 indexes.push(s[i].nodeIndex);
20513 * Clear all selections
20514 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20516 clearSelections : function(suppressEvent){
20517 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20518 this.cmp.elements = this.selections;
20519 this.cmp.removeClass(this.selectedClass);
20520 this.selections = [];
20521 if(!suppressEvent){
20522 this.fireEvent("selectionchange", this, this.selections);
20528 * Returns true if the passed node is selected
20529 * @param {HTMLElement/Number} node The node or node index
20530 * @return {Boolean}
20532 isSelected : function(node){
20533 var s = this.selections;
20537 node = this.getNode(node);
20538 return s.indexOf(node) !== -1;
20543 * @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
20544 * @param {Boolean} keepExisting (optional) true to keep existing selections
20545 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20547 select : function(nodeInfo, keepExisting, suppressEvent){
20548 if(nodeInfo instanceof Array){
20550 this.clearSelections(true);
20552 for(var i = 0, len = nodeInfo.length; i < len; i++){
20553 this.select(nodeInfo[i], true, true);
20557 var node = this.getNode(nodeInfo);
20558 if(!node || this.isSelected(node)){
20559 return; // already selected.
20562 this.clearSelections(true);
20565 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20566 Roo.fly(node).addClass(this.selectedClass);
20567 this.selections.push(node);
20568 if(!suppressEvent){
20569 this.fireEvent("selectionchange", this, this.selections);
20577 * @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
20578 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20579 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20581 unselect : function(nodeInfo, keepExisting, suppressEvent)
20583 if(nodeInfo instanceof Array){
20584 Roo.each(this.selections, function(s) {
20585 this.unselect(s, nodeInfo);
20589 var node = this.getNode(nodeInfo);
20590 if(!node || !this.isSelected(node)){
20591 //Roo.log("not selected");
20592 return; // not selected.
20596 Roo.each(this.selections, function(s) {
20598 Roo.fly(node).removeClass(this.selectedClass);
20605 this.selections= ns;
20606 this.fireEvent("selectionchange", this, this.selections);
20610 * Gets a template node.
20611 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20612 * @return {HTMLElement} The node or null if it wasn't found
20614 getNode : function(nodeInfo){
20615 if(typeof nodeInfo == "string"){
20616 return document.getElementById(nodeInfo);
20617 }else if(typeof nodeInfo == "number"){
20618 return this.nodes[nodeInfo];
20624 * Gets a range template nodes.
20625 * @param {Number} startIndex
20626 * @param {Number} endIndex
20627 * @return {Array} An array of nodes
20629 getNodes : function(start, end){
20630 var ns = this.nodes;
20631 start = start || 0;
20632 end = typeof end == "undefined" ? ns.length - 1 : end;
20635 for(var i = start; i <= end; i++){
20639 for(var i = start; i >= end; i--){
20647 * Finds the index of the passed node
20648 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20649 * @return {Number} The index of the node or -1
20651 indexOf : function(node){
20652 node = this.getNode(node);
20653 if(typeof node.nodeIndex == "number"){
20654 return node.nodeIndex;
20656 var ns = this.nodes;
20657 for(var i = 0, len = ns.length; i < len; i++){
20668 * based on jquery fullcalendar
20672 Roo.bootstrap = Roo.bootstrap || {};
20674 * @class Roo.bootstrap.Calendar
20675 * @extends Roo.bootstrap.Component
20676 * Bootstrap Calendar class
20677 * @cfg {Boolean} loadMask (true|false) default false
20678 * @cfg {Object} header generate the user specific header of the calendar, default false
20681 * Create a new Container
20682 * @param {Object} config The config object
20687 Roo.bootstrap.Calendar = function(config){
20688 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20692 * Fires when a date is selected
20693 * @param {DatePicker} this
20694 * @param {Date} date The selected date
20698 * @event monthchange
20699 * Fires when the displayed month changes
20700 * @param {DatePicker} this
20701 * @param {Date} date The selected month
20703 'monthchange': true,
20705 * @event evententer
20706 * Fires when mouse over an event
20707 * @param {Calendar} this
20708 * @param {event} Event
20710 'evententer': true,
20712 * @event eventleave
20713 * Fires when the mouse leaves an
20714 * @param {Calendar} this
20717 'eventleave': true,
20719 * @event eventclick
20720 * Fires when the mouse click an
20721 * @param {Calendar} this
20730 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20733 * @cfg {Roo.data.Store} store
20734 * The data source for the calendar
20738 * @cfg {Number} startDay
20739 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20747 getAutoCreate : function(){
20750 var fc_button = function(name, corner, style, content ) {
20751 return Roo.apply({},{
20753 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20755 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20758 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20769 style : 'width:100%',
20776 cls : 'fc-header-left',
20778 fc_button('prev', 'left', 'arrow', '‹' ),
20779 fc_button('next', 'right', 'arrow', '›' ),
20780 { tag: 'span', cls: 'fc-header-space' },
20781 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20789 cls : 'fc-header-center',
20793 cls: 'fc-header-title',
20796 html : 'month / year'
20804 cls : 'fc-header-right',
20806 /* fc_button('month', 'left', '', 'month' ),
20807 fc_button('week', '', '', 'week' ),
20808 fc_button('day', 'right', '', 'day' )
20820 header = this.header;
20823 var cal_heads = function() {
20825 // fixme - handle this.
20827 for (var i =0; i < Date.dayNames.length; i++) {
20828 var d = Date.dayNames[i];
20831 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20832 html : d.substring(0,3)
20836 ret[0].cls += ' fc-first';
20837 ret[6].cls += ' fc-last';
20840 var cal_cell = function(n) {
20843 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20848 cls: 'fc-day-number',
20852 cls: 'fc-day-content',
20856 style: 'position: relative;' // height: 17px;
20868 var cal_rows = function() {
20871 for (var r = 0; r < 6; r++) {
20878 for (var i =0; i < Date.dayNames.length; i++) {
20879 var d = Date.dayNames[i];
20880 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20883 row.cn[0].cls+=' fc-first';
20884 row.cn[0].cn[0].style = 'min-height:90px';
20885 row.cn[6].cls+=' fc-last';
20889 ret[0].cls += ' fc-first';
20890 ret[4].cls += ' fc-prev-last';
20891 ret[5].cls += ' fc-last';
20898 cls: 'fc-border-separate',
20899 style : 'width:100%',
20907 cls : 'fc-first fc-last',
20925 cls : 'fc-content',
20926 style : "position: relative;",
20929 cls : 'fc-view fc-view-month fc-grid',
20930 style : 'position: relative',
20931 unselectable : 'on',
20934 cls : 'fc-event-container',
20935 style : 'position:absolute;z-index:8;top:0;left:0;'
20953 initEvents : function()
20956 throw "can not find store for calendar";
20962 style: "text-align:center",
20966 style: "background-color:white;width:50%;margin:250 auto",
20970 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20981 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20983 var size = this.el.select('.fc-content', true).first().getSize();
20984 this.maskEl.setSize(size.width, size.height);
20985 this.maskEl.enableDisplayMode("block");
20986 if(!this.loadMask){
20987 this.maskEl.hide();
20990 this.store = Roo.factory(this.store, Roo.data);
20991 this.store.on('load', this.onLoad, this);
20992 this.store.on('beforeload', this.onBeforeLoad, this);
20996 this.cells = this.el.select('.fc-day',true);
20997 //Roo.log(this.cells);
20998 this.textNodes = this.el.query('.fc-day-number');
20999 this.cells.addClassOnOver('fc-state-hover');
21001 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
21002 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
21003 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
21004 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
21006 this.on('monthchange', this.onMonthChange, this);
21008 this.update(new Date().clearTime());
21011 resize : function() {
21012 var sz = this.el.getSize();
21014 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
21015 this.el.select('.fc-day-content div',true).setHeight(34);
21020 showPrevMonth : function(e){
21021 this.update(this.activeDate.add("mo", -1));
21023 showToday : function(e){
21024 this.update(new Date().clearTime());
21027 showNextMonth : function(e){
21028 this.update(this.activeDate.add("mo", 1));
21032 showPrevYear : function(){
21033 this.update(this.activeDate.add("y", -1));
21037 showNextYear : function(){
21038 this.update(this.activeDate.add("y", 1));
21043 update : function(date)
21045 var vd = this.activeDate;
21046 this.activeDate = date;
21047 // if(vd && this.el){
21048 // var t = date.getTime();
21049 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21050 // Roo.log('using add remove');
21052 // this.fireEvent('monthchange', this, date);
21054 // this.cells.removeClass("fc-state-highlight");
21055 // this.cells.each(function(c){
21056 // if(c.dateValue == t){
21057 // c.addClass("fc-state-highlight");
21058 // setTimeout(function(){
21059 // try{c.dom.firstChild.focus();}catch(e){}
21069 var days = date.getDaysInMonth();
21071 var firstOfMonth = date.getFirstDateOfMonth();
21072 var startingPos = firstOfMonth.getDay()-this.startDay;
21074 if(startingPos < this.startDay){
21078 var pm = date.add(Date.MONTH, -1);
21079 var prevStart = pm.getDaysInMonth()-startingPos;
21081 this.cells = this.el.select('.fc-day',true);
21082 this.textNodes = this.el.query('.fc-day-number');
21083 this.cells.addClassOnOver('fc-state-hover');
21085 var cells = this.cells.elements;
21086 var textEls = this.textNodes;
21088 Roo.each(cells, function(cell){
21089 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21092 days += startingPos;
21094 // convert everything to numbers so it's fast
21095 var day = 86400000;
21096 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21099 //Roo.log(prevStart);
21101 var today = new Date().clearTime().getTime();
21102 var sel = date.clearTime().getTime();
21103 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21104 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21105 var ddMatch = this.disabledDatesRE;
21106 var ddText = this.disabledDatesText;
21107 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21108 var ddaysText = this.disabledDaysText;
21109 var format = this.format;
21111 var setCellClass = function(cal, cell){
21115 //Roo.log('set Cell Class');
21117 var t = d.getTime();
21121 cell.dateValue = t;
21123 cell.className += " fc-today";
21124 cell.className += " fc-state-highlight";
21125 cell.title = cal.todayText;
21128 // disable highlight in other month..
21129 //cell.className += " fc-state-highlight";
21134 cell.className = " fc-state-disabled";
21135 cell.title = cal.minText;
21139 cell.className = " fc-state-disabled";
21140 cell.title = cal.maxText;
21144 if(ddays.indexOf(d.getDay()) != -1){
21145 cell.title = ddaysText;
21146 cell.className = " fc-state-disabled";
21149 if(ddMatch && format){
21150 var fvalue = d.dateFormat(format);
21151 if(ddMatch.test(fvalue)){
21152 cell.title = ddText.replace("%0", fvalue);
21153 cell.className = " fc-state-disabled";
21157 if (!cell.initialClassName) {
21158 cell.initialClassName = cell.dom.className;
21161 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21166 for(; i < startingPos; i++) {
21167 textEls[i].innerHTML = (++prevStart);
21168 d.setDate(d.getDate()+1);
21170 cells[i].className = "fc-past fc-other-month";
21171 setCellClass(this, cells[i]);
21176 for(; i < days; i++){
21177 intDay = i - startingPos + 1;
21178 textEls[i].innerHTML = (intDay);
21179 d.setDate(d.getDate()+1);
21181 cells[i].className = ''; // "x-date-active";
21182 setCellClass(this, cells[i]);
21186 for(; i < 42; i++) {
21187 textEls[i].innerHTML = (++extraDays);
21188 d.setDate(d.getDate()+1);
21190 cells[i].className = "fc-future fc-other-month";
21191 setCellClass(this, cells[i]);
21194 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21196 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21198 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21199 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21201 if(totalRows != 6){
21202 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21203 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21206 this.fireEvent('monthchange', this, date);
21210 if(!this.internalRender){
21211 var main = this.el.dom.firstChild;
21212 var w = main.offsetWidth;
21213 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21214 Roo.fly(main).setWidth(w);
21215 this.internalRender = true;
21216 // opera does not respect the auto grow header center column
21217 // then, after it gets a width opera refuses to recalculate
21218 // without a second pass
21219 if(Roo.isOpera && !this.secondPass){
21220 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21221 this.secondPass = true;
21222 this.update.defer(10, this, [date]);
21229 findCell : function(dt) {
21230 dt = dt.clearTime().getTime();
21232 this.cells.each(function(c){
21233 //Roo.log("check " +c.dateValue + '?=' + dt);
21234 if(c.dateValue == dt){
21244 findCells : function(ev) {
21245 var s = ev.start.clone().clearTime().getTime();
21247 var e= ev.end.clone().clearTime().getTime();
21250 this.cells.each(function(c){
21251 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21253 if(c.dateValue > e){
21256 if(c.dateValue < s){
21265 // findBestRow: function(cells)
21269 // for (var i =0 ; i < cells.length;i++) {
21270 // ret = Math.max(cells[i].rows || 0,ret);
21277 addItem : function(ev)
21279 // look for vertical location slot in
21280 var cells = this.findCells(ev);
21282 // ev.row = this.findBestRow(cells);
21284 // work out the location.
21288 for(var i =0; i < cells.length; i++) {
21290 cells[i].row = cells[0].row;
21293 cells[i].row = cells[i].row + 1;
21303 if (crow.start.getY() == cells[i].getY()) {
21305 crow.end = cells[i];
21322 cells[0].events.push(ev);
21324 this.calevents.push(ev);
21327 clearEvents: function() {
21329 if(!this.calevents){
21333 Roo.each(this.cells.elements, function(c){
21339 Roo.each(this.calevents, function(e) {
21340 Roo.each(e.els, function(el) {
21341 el.un('mouseenter' ,this.onEventEnter, this);
21342 el.un('mouseleave' ,this.onEventLeave, this);
21347 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21353 renderEvents: function()
21357 this.cells.each(function(c) {
21366 if(c.row != c.events.length){
21367 r = 4 - (4 - (c.row - c.events.length));
21370 c.events = ev.slice(0, r);
21371 c.more = ev.slice(r);
21373 if(c.more.length && c.more.length == 1){
21374 c.events.push(c.more.pop());
21377 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21381 this.cells.each(function(c) {
21383 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21386 for (var e = 0; e < c.events.length; e++){
21387 var ev = c.events[e];
21388 var rows = ev.rows;
21390 for(var i = 0; i < rows.length; i++) {
21392 // how many rows should it span..
21395 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21396 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21398 unselectable : "on",
21401 cls: 'fc-event-inner',
21405 // cls: 'fc-event-time',
21406 // html : cells.length > 1 ? '' : ev.time
21410 cls: 'fc-event-title',
21411 html : String.format('{0}', ev.title)
21418 cls: 'ui-resizable-handle ui-resizable-e',
21419 html : '  '
21426 cfg.cls += ' fc-event-start';
21428 if ((i+1) == rows.length) {
21429 cfg.cls += ' fc-event-end';
21432 var ctr = _this.el.select('.fc-event-container',true).first();
21433 var cg = ctr.createChild(cfg);
21435 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21436 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21438 var r = (c.more.length) ? 1 : 0;
21439 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21440 cg.setWidth(ebox.right - sbox.x -2);
21442 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21443 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21444 cg.on('click', _this.onEventClick, _this, ev);
21455 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21456 style : 'position: absolute',
21457 unselectable : "on",
21460 cls: 'fc-event-inner',
21464 cls: 'fc-event-title',
21472 cls: 'ui-resizable-handle ui-resizable-e',
21473 html : '  '
21479 var ctr = _this.el.select('.fc-event-container',true).first();
21480 var cg = ctr.createChild(cfg);
21482 var sbox = c.select('.fc-day-content',true).first().getBox();
21483 var ebox = c.select('.fc-day-content',true).first().getBox();
21485 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21486 cg.setWidth(ebox.right - sbox.x -2);
21488 cg.on('click', _this.onMoreEventClick, _this, c.more);
21498 onEventEnter: function (e, el,event,d) {
21499 this.fireEvent('evententer', this, el, event);
21502 onEventLeave: function (e, el,event,d) {
21503 this.fireEvent('eventleave', this, el, event);
21506 onEventClick: function (e, el,event,d) {
21507 this.fireEvent('eventclick', this, el, event);
21510 onMonthChange: function () {
21514 onMoreEventClick: function(e, el, more)
21518 this.calpopover.placement = 'right';
21519 this.calpopover.setTitle('More');
21521 this.calpopover.setContent('');
21523 var ctr = this.calpopover.el.select('.popover-content', true).first();
21525 Roo.each(more, function(m){
21527 cls : 'fc-event-hori fc-event-draggable',
21530 var cg = ctr.createChild(cfg);
21532 cg.on('click', _this.onEventClick, _this, m);
21535 this.calpopover.show(el);
21540 onLoad: function ()
21542 this.calevents = [];
21545 if(this.store.getCount() > 0){
21546 this.store.data.each(function(d){
21549 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21550 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21551 time : d.data.start_time,
21552 title : d.data.title,
21553 description : d.data.description,
21554 venue : d.data.venue
21559 this.renderEvents();
21561 if(this.calevents.length && this.loadMask){
21562 this.maskEl.hide();
21566 onBeforeLoad: function()
21568 this.clearEvents();
21570 this.maskEl.show();
21584 * @class Roo.bootstrap.Popover
21585 * @extends Roo.bootstrap.Component
21586 * @parent none builder
21587 * @children Roo.bootstrap.Component
21588 * Bootstrap Popover class
21589 * @cfg {String} html contents of the popover (or false to use children..)
21590 * @cfg {String} title of popover (or false to hide)
21591 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21592 * @cfg {String} trigger click || hover (or false to trigger manually)
21593 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21594 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21595 * - if false and it has a 'parent' then it will be automatically added to that element
21596 * - if string - Roo.get will be called
21597 * @cfg {Number} delay - delay before showing
21600 * Create a new Popover
21601 * @param {Object} config The config object
21604 Roo.bootstrap.Popover = function(config){
21605 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21611 * After the popover show
21613 * @param {Roo.bootstrap.Popover} this
21618 * After the popover hide
21620 * @param {Roo.bootstrap.Popover} this
21626 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21631 placement : 'right',
21632 trigger : 'hover', // hover
21638 can_build_overlaid : false,
21640 maskEl : false, // the mask element
21643 alignEl : false, // when show is called with an element - this get's stored.
21645 getChildContainer : function()
21647 return this.contentEl;
21650 getPopoverHeader : function()
21652 this.title = true; // flag not to hide it..
21653 this.headerEl.addClass('p-0');
21654 return this.headerEl
21658 getAutoCreate : function(){
21661 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21662 style: 'display:block',
21668 cls : 'popover-inner ',
21672 cls: 'popover-title popover-header',
21673 html : this.title === false ? '' : this.title
21676 cls : 'popover-content popover-body ' + (this.cls || ''),
21677 html : this.html || ''
21688 * @param {string} the title
21690 setTitle: function(str)
21694 this.headerEl.dom.innerHTML = str;
21699 * @param {string} the body content
21701 setContent: function(str)
21704 if (this.contentEl) {
21705 this.contentEl.dom.innerHTML = str;
21709 // as it get's added to the bottom of the page.
21710 onRender : function(ct, position)
21712 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21717 var cfg = Roo.apply({}, this.getAutoCreate());
21721 cfg.cls += ' ' + this.cls;
21724 cfg.style = this.style;
21726 //Roo.log("adding to ");
21727 this.el = Roo.get(document.body).createChild(cfg, position);
21728 // Roo.log(this.el);
21731 this.contentEl = this.el.select('.popover-content',true).first();
21732 this.headerEl = this.el.select('.popover-title',true).first();
21735 if(typeof(this.items) != 'undefined'){
21736 var items = this.items;
21739 for(var i =0;i < items.length;i++) {
21740 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21744 this.items = nitems;
21746 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21747 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21754 resizeMask : function()
21756 this.maskEl.setSize(
21757 Roo.lib.Dom.getViewWidth(true),
21758 Roo.lib.Dom.getViewHeight(true)
21762 initEvents : function()
21766 Roo.bootstrap.Popover.register(this);
21769 this.arrowEl = this.el.select('.arrow',true).first();
21770 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21771 this.el.enableDisplayMode('block');
21775 if (this.over === false && !this.parent()) {
21778 if (this.triggers === false) {
21783 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21784 var triggers = this.trigger ? this.trigger.split(' ') : [];
21785 Roo.each(triggers, function(trigger) {
21787 if (trigger == 'click') {
21788 on_el.on('click', this.toggle, this);
21789 } else if (trigger != 'manual') {
21790 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21791 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21793 on_el.on(eventIn ,this.enter, this);
21794 on_el.on(eventOut, this.leave, this);
21804 toggle : function () {
21805 this.hoverState == 'in' ? this.leave() : this.enter();
21808 enter : function () {
21810 clearTimeout(this.timeout);
21812 this.hoverState = 'in';
21814 if (!this.delay || !this.delay.show) {
21819 this.timeout = setTimeout(function () {
21820 if (_t.hoverState == 'in') {
21823 }, this.delay.show)
21826 leave : function() {
21827 clearTimeout(this.timeout);
21829 this.hoverState = 'out';
21831 if (!this.delay || !this.delay.hide) {
21836 this.timeout = setTimeout(function () {
21837 if (_t.hoverState == 'out') {
21840 }, this.delay.hide)
21844 * update the position of the dialog
21845 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21850 doAlign : function()
21853 if (this.alignEl) {
21854 this.updatePosition(this.placement, true);
21857 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21858 var es = this.el.getSize();
21859 var x = Roo.lib.Dom.getViewWidth()/2;
21860 var y = Roo.lib.Dom.getViewHeight()/2;
21861 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21873 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21874 * @param {string} (left|right|top|bottom) position
21876 show : function (on_el, placement)
21878 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21879 on_el = on_el || false; // default to false
21882 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21883 on_el = this.parent().el;
21884 } else if (this.over) {
21885 on_el = Roo.get(this.over);
21890 this.alignEl = Roo.get( on_el );
21893 this.render(document.body);
21899 if (this.title === false) {
21900 this.headerEl.hide();
21905 this.el.dom.style.display = 'block';
21909 //var arrow = this.el.select('.arrow',true).first();
21910 //arrow.set(align[2],
21912 this.el.addClass('in');
21916 this.hoverState = 'in';
21919 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21920 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21921 this.maskEl.dom.style.display = 'block';
21922 this.maskEl.addClass('show');
21924 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21926 this.fireEvent('show', this);
21930 * fire this manually after loading a grid in the table for example
21931 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21932 * @param {Boolean} try and move it if we cant get right position.
21934 updatePosition : function(placement, try_move)
21936 // allow for calling with no parameters
21937 placement = placement ? placement : this.placement;
21938 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21940 this.el.removeClass([
21941 'fade','top','bottom', 'left', 'right','in',
21942 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21944 this.el.addClass(placement + ' bs-popover-' + placement);
21946 if (!this.alignEl ) {
21950 switch (placement) {
21952 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21953 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21954 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21955 //normal display... or moved up/down.
21956 this.el.setXY(offset);
21957 var xy = this.alignEl.getAnchorXY('tr', false);
21959 this.arrowEl.setXY(xy);
21962 // continue through...
21963 return this.updatePosition('left', false);
21967 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21968 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21969 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21970 //normal display... or moved up/down.
21971 this.el.setXY(offset);
21972 var xy = this.alignEl.getAnchorXY('tl', false);
21973 xy[0]-=10;xy[1]+=5; // << fix me
21974 this.arrowEl.setXY(xy);
21978 return this.updatePosition('right', false);
21981 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21982 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21983 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21984 //normal display... or moved up/down.
21985 this.el.setXY(offset);
21986 var xy = this.alignEl.getAnchorXY('t', false);
21987 xy[1]-=10; // << fix me
21988 this.arrowEl.setXY(xy);
21992 return this.updatePosition('bottom', false);
21995 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21996 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21997 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21998 //normal display... or moved up/down.
21999 this.el.setXY(offset);
22000 var xy = this.alignEl.getAnchorXY('b', false);
22001 xy[1]+=2; // << fix me
22002 this.arrowEl.setXY(xy);
22006 return this.updatePosition('top', false);
22017 this.el.setXY([0,0]);
22018 this.el.removeClass('in');
22020 this.hoverState = null;
22021 this.maskEl.hide(); // always..
22022 this.fireEvent('hide', this);
22028 Roo.apply(Roo.bootstrap.Popover, {
22031 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
22032 'right' : ['l-br', [10,0], 'right bs-popover-right'],
22033 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
22034 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
22039 clickHander : false,
22043 onMouseDown : function(e)
22045 if (this.popups.length && !e.getTarget(".roo-popover")) {
22046 /// what is nothing is showing..
22055 register : function(popup)
22057 if (!Roo.bootstrap.Popover.clickHandler) {
22058 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22060 // hide other popups.
22061 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22062 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22063 this.hideAll(); //<< why?
22064 //this.popups.push(popup);
22066 hideAll : function()
22068 this.popups.forEach(function(p) {
22072 onShow : function() {
22073 Roo.bootstrap.Popover.popups.push(this);
22075 onHide : function() {
22076 Roo.bootstrap.Popover.popups.remove(this);
22081 * @class Roo.bootstrap.PopoverNav
22082 * @extends Roo.bootstrap.nav.Simplebar
22083 * @parent Roo.bootstrap.Popover
22084 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22086 * Bootstrap Popover header navigation class
22087 * FIXME? should this go under nav?
22091 * Create a new Popover Header Navigation
22092 * @param {Object} config The config object
22095 Roo.bootstrap.PopoverNav = function(config){
22096 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22099 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22102 container_method : 'getPopoverHeader'
22120 * @class Roo.bootstrap.Progress
22121 * @extends Roo.bootstrap.Component
22122 * @children Roo.bootstrap.ProgressBar
22123 * Bootstrap Progress class
22124 * @cfg {Boolean} striped striped of the progress bar
22125 * @cfg {Boolean} active animated of the progress bar
22129 * Create a new Progress
22130 * @param {Object} config The config object
22133 Roo.bootstrap.Progress = function(config){
22134 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22137 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22142 getAutoCreate : function(){
22150 cfg.cls += ' progress-striped';
22154 cfg.cls += ' active';
22173 * @class Roo.bootstrap.ProgressBar
22174 * @extends Roo.bootstrap.Component
22175 * Bootstrap ProgressBar class
22176 * @cfg {Number} aria_valuenow aria-value now
22177 * @cfg {Number} aria_valuemin aria-value min
22178 * @cfg {Number} aria_valuemax aria-value max
22179 * @cfg {String} label label for the progress bar
22180 * @cfg {String} panel (success | info | warning | danger )
22181 * @cfg {String} role role of the progress bar
22182 * @cfg {String} sr_only text
22186 * Create a new ProgressBar
22187 * @param {Object} config The config object
22190 Roo.bootstrap.ProgressBar = function(config){
22191 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22194 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22198 aria_valuemax : 100,
22204 getAutoCreate : function()
22209 cls: 'progress-bar',
22210 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22222 cfg.role = this.role;
22225 if(this.aria_valuenow){
22226 cfg['aria-valuenow'] = this.aria_valuenow;
22229 if(this.aria_valuemin){
22230 cfg['aria-valuemin'] = this.aria_valuemin;
22233 if(this.aria_valuemax){
22234 cfg['aria-valuemax'] = this.aria_valuemax;
22237 if(this.label && !this.sr_only){
22238 cfg.html = this.label;
22242 cfg.cls += ' progress-bar-' + this.panel;
22248 update : function(aria_valuenow)
22250 this.aria_valuenow = aria_valuenow;
22252 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22260 * @class Roo.bootstrap.TabGroup
22261 * @extends Roo.bootstrap.Column
22262 * @children Roo.bootstrap.TabPanel
22263 * Bootstrap Column class
22264 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22265 * @cfg {Boolean} carousel true to make the group behave like a carousel
22266 * @cfg {Boolean} bullets show bullets for the panels
22267 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22268 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22269 * @cfg {Boolean} showarrow (true|false) show arrow default true
22272 * Create a new TabGroup
22273 * @param {Object} config The config object
22276 Roo.bootstrap.TabGroup = function(config){
22277 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22279 this.navId = Roo.id();
22282 Roo.bootstrap.TabGroup.register(this);
22286 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22289 transition : false,
22294 slideOnTouch : false,
22297 getAutoCreate : function()
22299 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22301 cfg.cls += ' tab-content';
22303 if (this.carousel) {
22304 cfg.cls += ' carousel slide';
22307 cls : 'carousel-inner',
22311 if(this.bullets && !Roo.isTouch){
22314 cls : 'carousel-bullets',
22318 if(this.bullets_cls){
22319 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22326 cfg.cn[0].cn.push(bullets);
22329 if(this.showarrow){
22330 cfg.cn[0].cn.push({
22332 class : 'carousel-arrow',
22336 class : 'carousel-prev',
22340 class : 'fa fa-chevron-left'
22346 class : 'carousel-next',
22350 class : 'fa fa-chevron-right'
22363 initEvents: function()
22365 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22366 // this.el.on("touchstart", this.onTouchStart, this);
22369 if(this.autoslide){
22372 this.slideFn = window.setInterval(function() {
22373 _this.showPanelNext();
22377 if(this.showarrow){
22378 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22379 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22385 // onTouchStart : function(e, el, o)
22387 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22391 // this.showPanelNext();
22395 getChildContainer : function()
22397 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22401 * register a Navigation item
22402 * @param {Roo.bootstrap.nav.Item} the navitem to add
22404 register : function(item)
22406 this.tabs.push( item);
22407 item.navId = this.navId; // not really needed..
22412 getActivePanel : function()
22415 Roo.each(this.tabs, function(t) {
22425 getPanelByName : function(n)
22428 Roo.each(this.tabs, function(t) {
22429 if (t.tabId == n) {
22437 indexOfPanel : function(p)
22440 Roo.each(this.tabs, function(t,i) {
22441 if (t.tabId == p.tabId) {
22450 * show a specific panel
22451 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22452 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22454 showPanel : function (pan)
22456 if(this.transition || typeof(pan) == 'undefined'){
22457 Roo.log("waiting for the transitionend");
22461 if (typeof(pan) == 'number') {
22462 pan = this.tabs[pan];
22465 if (typeof(pan) == 'string') {
22466 pan = this.getPanelByName(pan);
22469 var cur = this.getActivePanel();
22472 Roo.log('pan or acitve pan is undefined');
22476 if (pan.tabId == this.getActivePanel().tabId) {
22480 if (false === cur.fireEvent('beforedeactivate')) {
22484 if(this.bullets > 0 && !Roo.isTouch){
22485 this.setActiveBullet(this.indexOfPanel(pan));
22488 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22490 //class="carousel-item carousel-item-next carousel-item-left"
22492 this.transition = true;
22493 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22494 var lr = dir == 'next' ? 'left' : 'right';
22495 pan.el.addClass(dir); // or prev
22496 pan.el.addClass('carousel-item-' + dir); // or prev
22497 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22498 cur.el.addClass(lr); // or right
22499 pan.el.addClass(lr);
22500 cur.el.addClass('carousel-item-' +lr); // or right
22501 pan.el.addClass('carousel-item-' +lr);
22505 cur.el.on('transitionend', function() {
22506 Roo.log("trans end?");
22508 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22509 pan.setActive(true);
22511 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22512 cur.setActive(false);
22514 _this.transition = false;
22516 }, this, { single: true } );
22521 cur.setActive(false);
22522 pan.setActive(true);
22527 showPanelNext : function()
22529 var i = this.indexOfPanel(this.getActivePanel());
22531 if (i >= this.tabs.length - 1 && !this.autoslide) {
22535 if (i >= this.tabs.length - 1 && this.autoslide) {
22539 this.showPanel(this.tabs[i+1]);
22542 showPanelPrev : function()
22544 var i = this.indexOfPanel(this.getActivePanel());
22546 if (i < 1 && !this.autoslide) {
22550 if (i < 1 && this.autoslide) {
22551 i = this.tabs.length;
22554 this.showPanel(this.tabs[i-1]);
22558 addBullet: function()
22560 if(!this.bullets || Roo.isTouch){
22563 var ctr = this.el.select('.carousel-bullets',true).first();
22564 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22565 var bullet = ctr.createChild({
22566 cls : 'bullet bullet-' + i
22567 },ctr.dom.lastChild);
22572 bullet.on('click', (function(e, el, o, ii, t){
22574 e.preventDefault();
22576 this.showPanel(ii);
22578 if(this.autoslide && this.slideFn){
22579 clearInterval(this.slideFn);
22580 this.slideFn = window.setInterval(function() {
22581 _this.showPanelNext();
22585 }).createDelegate(this, [i, bullet], true));
22590 setActiveBullet : function(i)
22596 Roo.each(this.el.select('.bullet', true).elements, function(el){
22597 el.removeClass('selected');
22600 var bullet = this.el.select('.bullet-' + i, true).first();
22606 bullet.addClass('selected');
22617 Roo.apply(Roo.bootstrap.TabGroup, {
22621 * register a Navigation Group
22622 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22624 register : function(navgrp)
22626 this.groups[navgrp.navId] = navgrp;
22630 * fetch a Navigation Group based on the navigation ID
22631 * if one does not exist , it will get created.
22632 * @param {string} the navgroup to add
22633 * @returns {Roo.bootstrap.nav.Group} the navgroup
22635 get: function(navId) {
22636 if (typeof(this.groups[navId]) == 'undefined') {
22637 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22639 return this.groups[navId] ;
22654 * @class Roo.bootstrap.TabPanel
22655 * @extends Roo.bootstrap.Component
22656 * @children Roo.bootstrap.Component
22657 * Bootstrap TabPanel class
22658 * @cfg {Boolean} active panel active
22659 * @cfg {String} html panel content
22660 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22661 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22662 * @cfg {String} href click to link..
22663 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22667 * Create a new TabPanel
22668 * @param {Object} config The config object
22671 Roo.bootstrap.TabPanel = function(config){
22672 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22676 * Fires when the active status changes
22677 * @param {Roo.bootstrap.TabPanel} this
22678 * @param {Boolean} state the new state
22683 * @event beforedeactivate
22684 * Fires before a tab is de-activated - can be used to do validation on a form.
22685 * @param {Roo.bootstrap.TabPanel} this
22686 * @return {Boolean} false if there is an error
22689 'beforedeactivate': true
22692 this.tabId = this.tabId || Roo.id();
22696 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22703 touchSlide : false,
22704 getAutoCreate : function(){
22709 // item is needed for carousel - not sure if it has any effect otherwise
22710 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22711 html: this.html || ''
22715 cfg.cls += ' active';
22719 cfg.tabId = this.tabId;
22727 initEvents: function()
22729 var p = this.parent();
22731 this.navId = this.navId || p.navId;
22733 if (typeof(this.navId) != 'undefined') {
22734 // not really needed.. but just in case.. parent should be a NavGroup.
22735 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22739 var i = tg.tabs.length - 1;
22741 if(this.active && tg.bullets > 0 && i < tg.bullets){
22742 tg.setActiveBullet(i);
22746 this.el.on('click', this.onClick, this);
22748 if(Roo.isTouch && this.touchSlide){
22749 this.el.on("touchstart", this.onTouchStart, this);
22750 this.el.on("touchmove", this.onTouchMove, this);
22751 this.el.on("touchend", this.onTouchEnd, this);
22756 onRender : function(ct, position)
22758 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22761 setActive : function(state)
22763 Roo.log("panel - set active " + this.tabId + "=" + state);
22765 this.active = state;
22767 this.el.removeClass('active');
22769 } else if (!this.el.hasClass('active')) {
22770 this.el.addClass('active');
22773 this.fireEvent('changed', this, state);
22776 onClick : function(e)
22778 e.preventDefault();
22780 if(!this.href.length){
22784 window.location.href = this.href;
22793 onTouchStart : function(e)
22795 this.swiping = false;
22797 this.startX = e.browserEvent.touches[0].clientX;
22798 this.startY = e.browserEvent.touches[0].clientY;
22801 onTouchMove : function(e)
22803 this.swiping = true;
22805 this.endX = e.browserEvent.touches[0].clientX;
22806 this.endY = e.browserEvent.touches[0].clientY;
22809 onTouchEnd : function(e)
22816 var tabGroup = this.parent();
22818 if(this.endX > this.startX){ // swiping right
22819 tabGroup.showPanelPrev();
22823 if(this.startX > this.endX){ // swiping left
22824 tabGroup.showPanelNext();
22843 * @class Roo.bootstrap.form.DateField
22844 * @extends Roo.bootstrap.form.Input
22845 * Bootstrap DateField class
22846 * @cfg {Number} weekStart default 0
22847 * @cfg {String} viewMode default empty, (months|years)
22848 * @cfg {String} minViewMode default empty, (months|years)
22849 * @cfg {Number} startDate default -Infinity
22850 * @cfg {Number} endDate default Infinity
22851 * @cfg {Boolean} todayHighlight default false
22852 * @cfg {Boolean} todayBtn default false
22853 * @cfg {Boolean} calendarWeeks default false
22854 * @cfg {Object} daysOfWeekDisabled default empty
22855 * @cfg {Boolean} singleMode default false (true | false)
22857 * @cfg {Boolean} keyboardNavigation default true
22858 * @cfg {String} language default en
22861 * Create a new DateField
22862 * @param {Object} config The config object
22865 Roo.bootstrap.form.DateField = function(config){
22866 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22870 * Fires when this field show.
22871 * @param {Roo.bootstrap.form.DateField} this
22872 * @param {Mixed} date The date value
22877 * Fires when this field hide.
22878 * @param {Roo.bootstrap.form.DateField} this
22879 * @param {Mixed} date The date value
22884 * Fires when select a date.
22885 * @param {Roo.bootstrap.form.DateField} this
22886 * @param {Mixed} date The date value
22890 * @event beforeselect
22891 * Fires when before select a date.
22892 * @param {Roo.bootstrap.form.DateField} this
22893 * @param {Mixed} date The date value
22895 beforeselect : true
22899 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22902 * @cfg {String} format
22903 * The default date format string which can be overriden for localization support. The format must be
22904 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22908 * @cfg {String} altFormats
22909 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22910 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22912 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22920 todayHighlight : false,
22926 keyboardNavigation: true,
22928 calendarWeeks: false,
22930 startDate: -Infinity,
22934 daysOfWeekDisabled: [],
22938 singleMode : false,
22940 UTCDate: function()
22942 return new Date(Date.UTC.apply(Date, arguments));
22945 UTCToday: function()
22947 var today = new Date();
22948 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22951 getDate: function() {
22952 var d = this.getUTCDate();
22953 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22956 getUTCDate: function() {
22960 setDate: function(d) {
22961 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22964 setUTCDate: function(d) {
22966 this.setValue(this.formatDate(this.date));
22969 onRender: function(ct, position)
22972 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22974 this.language = this.language || 'en';
22975 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22976 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22978 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22979 this.format = this.format || 'm/d/y';
22980 this.isInline = false;
22981 this.isInput = true;
22982 this.component = this.el.select('.add-on', true).first() || false;
22983 this.component = (this.component && this.component.length === 0) ? false : this.component;
22984 this.hasInput = this.component && this.inputEl().length;
22986 if (typeof(this.minViewMode === 'string')) {
22987 switch (this.minViewMode) {
22989 this.minViewMode = 1;
22992 this.minViewMode = 2;
22995 this.minViewMode = 0;
23000 if (typeof(this.viewMode === 'string')) {
23001 switch (this.viewMode) {
23014 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
23016 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
23018 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23020 this.picker().on('mousedown', this.onMousedown, this);
23021 this.picker().on('click', this.onClick, this);
23023 this.picker().addClass('datepicker-dropdown');
23025 this.startViewMode = this.viewMode;
23027 if(this.singleMode){
23028 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
23029 v.setVisibilityMode(Roo.Element.DISPLAY);
23033 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23034 v.setStyle('width', '189px');
23038 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
23039 if(!this.calendarWeeks){
23044 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23045 v.attr('colspan', function(i, val){
23046 return parseInt(val) + 1;
23051 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23053 this.setStartDate(this.startDate);
23054 this.setEndDate(this.endDate);
23056 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23063 if(this.isInline) {
23068 picker : function()
23070 return this.pickerEl;
23071 // return this.el.select('.datepicker', true).first();
23074 fillDow: function()
23076 var dowCnt = this.weekStart;
23085 if(this.calendarWeeks){
23093 while (dowCnt < this.weekStart + 7) {
23097 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23101 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23104 fillMonths: function()
23107 var months = this.picker().select('>.datepicker-months td', true).first();
23109 months.dom.innerHTML = '';
23115 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23118 months.createChild(month);
23125 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;
23127 if (this.date < this.startDate) {
23128 this.viewDate = new Date(this.startDate);
23129 } else if (this.date > this.endDate) {
23130 this.viewDate = new Date(this.endDate);
23132 this.viewDate = new Date(this.date);
23140 var d = new Date(this.viewDate),
23141 year = d.getUTCFullYear(),
23142 month = d.getUTCMonth(),
23143 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23144 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23145 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23146 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23147 currentDate = this.date && this.date.valueOf(),
23148 today = this.UTCToday();
23150 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23152 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23154 // this.picker.select('>tfoot th.today').
23155 // .text(dates[this.language].today)
23156 // .toggle(this.todayBtn !== false);
23158 this.updateNavArrows();
23161 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23163 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23165 prevMonth.setUTCDate(day);
23167 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23169 var nextMonth = new Date(prevMonth);
23171 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23173 nextMonth = nextMonth.valueOf();
23175 var fillMonths = false;
23177 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23179 while(prevMonth.valueOf() <= nextMonth) {
23182 if (prevMonth.getUTCDay() === this.weekStart) {
23184 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23192 if(this.calendarWeeks){
23193 // ISO 8601: First week contains first thursday.
23194 // ISO also states week starts on Monday, but we can be more abstract here.
23196 // Start of current week: based on weekstart/current date
23197 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23198 // Thursday of this week
23199 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23200 // First Thursday of year, year from thursday
23201 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23202 // Calendar week: ms between thursdays, div ms per day, div 7 days
23203 calWeek = (th - yth) / 864e5 / 7 + 1;
23205 fillMonths.cn.push({
23213 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23215 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23218 if (this.todayHighlight &&
23219 prevMonth.getUTCFullYear() == today.getFullYear() &&
23220 prevMonth.getUTCMonth() == today.getMonth() &&
23221 prevMonth.getUTCDate() == today.getDate()) {
23222 clsName += ' today';
23225 if (currentDate && prevMonth.valueOf() === currentDate) {
23226 clsName += ' active';
23229 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23230 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23231 clsName += ' disabled';
23234 fillMonths.cn.push({
23236 cls: 'day ' + clsName,
23237 html: prevMonth.getDate()
23240 prevMonth.setDate(prevMonth.getDate()+1);
23243 var currentYear = this.date && this.date.getUTCFullYear();
23244 var currentMonth = this.date && this.date.getUTCMonth();
23246 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23248 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23249 v.removeClass('active');
23251 if(currentYear === year && k === currentMonth){
23252 v.addClass('active');
23255 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23256 v.addClass('disabled');
23262 year = parseInt(year/10, 10) * 10;
23264 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23266 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23269 for (var i = -1; i < 11; i++) {
23270 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23272 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23280 showMode: function(dir)
23283 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23286 Roo.each(this.picker().select('>div',true).elements, function(v){
23287 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23290 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23295 if(this.isInline) {
23299 this.picker().removeClass(['bottom', 'top']);
23301 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23303 * place to the top of element!
23307 this.picker().addClass('top');
23308 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23313 this.picker().addClass('bottom');
23315 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23318 parseDate : function(value)
23320 if(!value || value instanceof Date){
23323 var v = Date.parseDate(value, this.format);
23324 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23325 v = Date.parseDate(value, 'Y-m-d');
23327 if(!v && this.altFormats){
23328 if(!this.altFormatsArray){
23329 this.altFormatsArray = this.altFormats.split("|");
23331 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23332 v = Date.parseDate(value, this.altFormatsArray[i]);
23338 formatDate : function(date, fmt)
23340 return (!date || !(date instanceof Date)) ?
23341 date : date.dateFormat(fmt || this.format);
23344 onFocus : function()
23346 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23350 onBlur : function()
23352 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23354 var d = this.inputEl().getValue();
23361 showPopup : function()
23363 this.picker().show();
23367 this.fireEvent('showpopup', this, this.date);
23370 hidePopup : function()
23372 if(this.isInline) {
23375 this.picker().hide();
23376 this.viewMode = this.startViewMode;
23379 this.fireEvent('hidepopup', this, this.date);
23383 onMousedown: function(e)
23385 e.stopPropagation();
23386 e.preventDefault();
23391 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23395 setValue: function(v)
23397 if(this.fireEvent('beforeselect', this, v) !== false){
23398 var d = new Date(this.parseDate(v) ).clearTime();
23400 if(isNaN(d.getTime())){
23401 this.date = this.viewDate = '';
23402 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23406 v = this.formatDate(d);
23408 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23410 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23414 this.fireEvent('select', this, this.date);
23418 getValue: function()
23420 return this.formatDate(this.date);
23423 fireKey: function(e)
23425 if (!this.picker().isVisible()){
23426 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23432 var dateChanged = false,
23434 newDate, newViewDate;
23439 e.preventDefault();
23443 if (!this.keyboardNavigation) {
23446 dir = e.keyCode == 37 ? -1 : 1;
23449 newDate = this.moveYear(this.date, dir);
23450 newViewDate = this.moveYear(this.viewDate, dir);
23451 } else if (e.shiftKey){
23452 newDate = this.moveMonth(this.date, dir);
23453 newViewDate = this.moveMonth(this.viewDate, dir);
23455 newDate = new Date(this.date);
23456 newDate.setUTCDate(this.date.getUTCDate() + dir);
23457 newViewDate = new Date(this.viewDate);
23458 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23460 if (this.dateWithinRange(newDate)){
23461 this.date = newDate;
23462 this.viewDate = newViewDate;
23463 this.setValue(this.formatDate(this.date));
23465 e.preventDefault();
23466 dateChanged = true;
23471 if (!this.keyboardNavigation) {
23474 dir = e.keyCode == 38 ? -1 : 1;
23476 newDate = this.moveYear(this.date, dir);
23477 newViewDate = this.moveYear(this.viewDate, dir);
23478 } else if (e.shiftKey){
23479 newDate = this.moveMonth(this.date, dir);
23480 newViewDate = this.moveMonth(this.viewDate, dir);
23482 newDate = new Date(this.date);
23483 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23484 newViewDate = new Date(this.viewDate);
23485 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23487 if (this.dateWithinRange(newDate)){
23488 this.date = newDate;
23489 this.viewDate = newViewDate;
23490 this.setValue(this.formatDate(this.date));
23492 e.preventDefault();
23493 dateChanged = true;
23497 this.setValue(this.formatDate(this.date));
23499 e.preventDefault();
23502 this.setValue(this.formatDate(this.date));
23516 onClick: function(e)
23518 e.stopPropagation();
23519 e.preventDefault();
23521 var target = e.getTarget();
23523 if(target.nodeName.toLowerCase() === 'i'){
23524 target = Roo.get(target).dom.parentNode;
23527 var nodeName = target.nodeName;
23528 var className = target.className;
23529 var html = target.innerHTML;
23530 //Roo.log(nodeName);
23532 switch(nodeName.toLowerCase()) {
23534 switch(className) {
23540 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23541 switch(this.viewMode){
23543 this.viewDate = this.moveMonth(this.viewDate, dir);
23547 this.viewDate = this.moveYear(this.viewDate, dir);
23553 var date = new Date();
23554 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23556 this.setValue(this.formatDate(this.date));
23563 if (className.indexOf('disabled') < 0) {
23564 if (!this.viewDate) {
23565 this.viewDate = new Date();
23567 this.viewDate.setUTCDate(1);
23568 if (className.indexOf('month') > -1) {
23569 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23571 var year = parseInt(html, 10) || 0;
23572 this.viewDate.setUTCFullYear(year);
23576 if(this.singleMode){
23577 this.setValue(this.formatDate(this.viewDate));
23588 //Roo.log(className);
23589 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23590 var day = parseInt(html, 10) || 1;
23591 var year = (this.viewDate || new Date()).getUTCFullYear(),
23592 month = (this.viewDate || new Date()).getUTCMonth();
23594 if (className.indexOf('old') > -1) {
23601 } else if (className.indexOf('new') > -1) {
23609 //Roo.log([year,month,day]);
23610 this.date = this.UTCDate(year, month, day,0,0,0,0);
23611 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23613 //Roo.log(this.formatDate(this.date));
23614 this.setValue(this.formatDate(this.date));
23621 setStartDate: function(startDate)
23623 this.startDate = startDate || -Infinity;
23624 if (this.startDate !== -Infinity) {
23625 this.startDate = this.parseDate(this.startDate);
23628 this.updateNavArrows();
23631 setEndDate: function(endDate)
23633 this.endDate = endDate || Infinity;
23634 if (this.endDate !== Infinity) {
23635 this.endDate = this.parseDate(this.endDate);
23638 this.updateNavArrows();
23641 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23643 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23644 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23645 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23647 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23648 return parseInt(d, 10);
23651 this.updateNavArrows();
23654 updateNavArrows: function()
23656 if(this.singleMode){
23660 var d = new Date(this.viewDate),
23661 year = d.getUTCFullYear(),
23662 month = d.getUTCMonth();
23664 Roo.each(this.picker().select('.prev', true).elements, function(v){
23666 switch (this.viewMode) {
23669 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23675 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23682 Roo.each(this.picker().select('.next', true).elements, function(v){
23684 switch (this.viewMode) {
23687 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23693 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23701 moveMonth: function(date, dir)
23706 var new_date = new Date(date.valueOf()),
23707 day = new_date.getUTCDate(),
23708 month = new_date.getUTCMonth(),
23709 mag = Math.abs(dir),
23711 dir = dir > 0 ? 1 : -1;
23714 // If going back one month, make sure month is not current month
23715 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23717 return new_date.getUTCMonth() == month;
23719 // If going forward one month, make sure month is as expected
23720 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23722 return new_date.getUTCMonth() != new_month;
23724 new_month = month + dir;
23725 new_date.setUTCMonth(new_month);
23726 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23727 if (new_month < 0 || new_month > 11) {
23728 new_month = (new_month + 12) % 12;
23731 // For magnitudes >1, move one month at a time...
23732 for (var i=0; i<mag; i++) {
23733 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23734 new_date = this.moveMonth(new_date, dir);
23736 // ...then reset the day, keeping it in the new month
23737 new_month = new_date.getUTCMonth();
23738 new_date.setUTCDate(day);
23740 return new_month != new_date.getUTCMonth();
23743 // Common date-resetting loop -- if date is beyond end of month, make it
23746 new_date.setUTCDate(--day);
23747 new_date.setUTCMonth(new_month);
23752 moveYear: function(date, dir)
23754 return this.moveMonth(date, dir*12);
23757 dateWithinRange: function(date)
23759 return date >= this.startDate && date <= this.endDate;
23765 this.picker().remove();
23768 validateValue : function(value)
23770 if(this.getVisibilityEl().hasClass('hidden')){
23774 if(value.length < 1) {
23775 if(this.allowBlank){
23781 if(value.length < this.minLength){
23784 if(value.length > this.maxLength){
23788 var vt = Roo.form.VTypes;
23789 if(!vt[this.vtype](value, this)){
23793 if(typeof this.validator == "function"){
23794 var msg = this.validator(value);
23800 if(this.regex && !this.regex.test(value)){
23804 if(typeof(this.parseDate(value)) == 'undefined'){
23808 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23812 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23822 this.date = this.viewDate = '';
23824 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23829 Roo.apply(Roo.bootstrap.form.DateField, {
23840 html: '<i class="fa fa-arrow-left"/>'
23850 html: '<i class="fa fa-arrow-right"/>'
23892 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23893 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23894 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23895 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23896 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23909 navFnc: 'FullYear',
23914 navFnc: 'FullYear',
23919 Roo.apply(Roo.bootstrap.form.DateField, {
23923 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23927 cls: 'datepicker-days',
23931 cls: 'table-condensed',
23933 Roo.bootstrap.form.DateField.head,
23937 Roo.bootstrap.form.DateField.footer
23944 cls: 'datepicker-months',
23948 cls: 'table-condensed',
23950 Roo.bootstrap.form.DateField.head,
23951 Roo.bootstrap.form.DateField.content,
23952 Roo.bootstrap.form.DateField.footer
23959 cls: 'datepicker-years',
23963 cls: 'table-condensed',
23965 Roo.bootstrap.form.DateField.head,
23966 Roo.bootstrap.form.DateField.content,
23967 Roo.bootstrap.form.DateField.footer
23986 * @class Roo.bootstrap.form.TimeField
23987 * @extends Roo.bootstrap.form.Input
23988 * Bootstrap DateField class
23992 * Create a new TimeField
23993 * @param {Object} config The config object
23996 Roo.bootstrap.form.TimeField = function(config){
23997 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
24001 * Fires when this field show.
24002 * @param {Roo.bootstrap.form.DateField} thisthis
24003 * @param {Mixed} date The date value
24008 * Fires when this field hide.
24009 * @param {Roo.bootstrap.form.DateField} this
24010 * @param {Mixed} date The date value
24015 * Fires when select a date.
24016 * @param {Roo.bootstrap.form.DateField} this
24017 * @param {Mixed} date The date value
24023 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
24026 * @cfg {String} format
24027 * The default time format string which can be overriden for localization support. The format must be
24028 * valid according to {@link Date#parseDate} (defaults to 'H:i').
24032 getAutoCreate : function()
24034 this.after = '<i class="fa far fa-clock"></i>';
24035 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
24039 onRender: function(ct, position)
24042 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
24044 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
24046 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24048 this.pop = this.picker().select('>.datepicker-time',true).first();
24049 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24051 this.picker().on('mousedown', this.onMousedown, this);
24052 this.picker().on('click', this.onClick, this);
24054 this.picker().addClass('datepicker-dropdown');
24059 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24060 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24061 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24062 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24063 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24064 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24068 fireKey: function(e){
24069 if (!this.picker().isVisible()){
24070 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24076 e.preventDefault();
24084 this.onTogglePeriod();
24087 this.onIncrementMinutes();
24090 this.onDecrementMinutes();
24099 onClick: function(e) {
24100 e.stopPropagation();
24101 e.preventDefault();
24104 picker : function()
24106 return this.pickerEl;
24109 fillTime: function()
24111 var time = this.pop.select('tbody', true).first();
24113 time.dom.innerHTML = '';
24128 cls: 'hours-up fa fas fa-chevron-up'
24148 cls: 'minutes-up fa fas fa-chevron-up'
24169 cls: 'timepicker-hour',
24184 cls: 'timepicker-minute',
24199 cls: 'btn btn-primary period',
24221 cls: 'hours-down fa fas fa-chevron-down'
24241 cls: 'minutes-down fa fas fa-chevron-down'
24259 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24266 var hours = this.time.getHours();
24267 var minutes = this.time.getMinutes();
24280 hours = hours - 12;
24284 hours = '0' + hours;
24288 minutes = '0' + minutes;
24291 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24292 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24293 this.pop.select('button', true).first().dom.innerHTML = period;
24299 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24301 var cls = ['bottom'];
24303 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24310 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24314 //this.picker().setXY(20000,20000);
24315 this.picker().addClass(cls.join('-'));
24319 Roo.each(cls, function(c){
24324 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24325 //_this.picker().setTop(_this.inputEl().getHeight());
24329 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24331 //_this.picker().setTop(0 - _this.picker().getHeight());
24336 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24340 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24348 onFocus : function()
24350 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24354 onBlur : function()
24356 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24362 this.picker().show();
24367 this.fireEvent('show', this, this.date);
24372 this.picker().hide();
24375 this.fireEvent('hide', this, this.date);
24378 setTime : function()
24381 this.setValue(this.time.format(this.format));
24383 this.fireEvent('select', this, this.date);
24388 onMousedown: function(e){
24389 e.stopPropagation();
24390 e.preventDefault();
24393 onIncrementHours: function()
24395 Roo.log('onIncrementHours');
24396 this.time = this.time.add(Date.HOUR, 1);
24401 onDecrementHours: function()
24403 Roo.log('onDecrementHours');
24404 this.time = this.time.add(Date.HOUR, -1);
24408 onIncrementMinutes: function()
24410 Roo.log('onIncrementMinutes');
24411 this.time = this.time.add(Date.MINUTE, 1);
24415 onDecrementMinutes: function()
24417 Roo.log('onDecrementMinutes');
24418 this.time = this.time.add(Date.MINUTE, -1);
24422 onTogglePeriod: function()
24424 Roo.log('onTogglePeriod');
24425 this.time = this.time.add(Date.HOUR, 12);
24433 Roo.apply(Roo.bootstrap.form.TimeField, {
24437 cls: 'datepicker dropdown-menu',
24441 cls: 'datepicker-time',
24445 cls: 'table-condensed',
24474 cls: 'btn btn-info ok',
24502 * @class Roo.bootstrap.form.MonthField
24503 * @extends Roo.bootstrap.form.Input
24504 * Bootstrap MonthField class
24506 * @cfg {String} language default en
24509 * Create a new MonthField
24510 * @param {Object} config The config object
24513 Roo.bootstrap.form.MonthField = function(config){
24514 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24519 * Fires when this field show.
24520 * @param {Roo.bootstrap.form.MonthField} this
24521 * @param {Mixed} date The date value
24526 * Fires when this field hide.
24527 * @param {Roo.bootstrap.form.MonthField} this
24528 * @param {Mixed} date The date value
24533 * Fires when select a date.
24534 * @param {Roo.bootstrap.form.MonthField} this
24535 * @param {String} oldvalue The old value
24536 * @param {String} newvalue The new value
24542 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24544 onRender: function(ct, position)
24547 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24549 this.language = this.language || 'en';
24550 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24551 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24553 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24554 this.isInline = false;
24555 this.isInput = true;
24556 this.component = this.el.select('.add-on', true).first() || false;
24557 this.component = (this.component && this.component.length === 0) ? false : this.component;
24558 this.hasInput = this.component && this.inputEL().length;
24560 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24562 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24564 this.picker().on('mousedown', this.onMousedown, this);
24565 this.picker().on('click', this.onClick, this);
24567 this.picker().addClass('datepicker-dropdown');
24569 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24570 v.setStyle('width', '189px');
24577 if(this.isInline) {
24583 setValue: function(v, suppressEvent)
24585 var o = this.getValue();
24587 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24591 if(suppressEvent !== true){
24592 this.fireEvent('select', this, o, v);
24597 getValue: function()
24602 onClick: function(e)
24604 e.stopPropagation();
24605 e.preventDefault();
24607 var target = e.getTarget();
24609 if(target.nodeName.toLowerCase() === 'i'){
24610 target = Roo.get(target).dom.parentNode;
24613 var nodeName = target.nodeName;
24614 var className = target.className;
24615 var html = target.innerHTML;
24617 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24621 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24623 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24629 picker : function()
24631 return this.pickerEl;
24634 fillMonths: function()
24637 var months = this.picker().select('>.datepicker-months td', true).first();
24639 months.dom.innerHTML = '';
24645 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24648 months.createChild(month);
24657 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24658 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24661 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24662 e.removeClass('active');
24664 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24665 e.addClass('active');
24672 if(this.isInline) {
24676 this.picker().removeClass(['bottom', 'top']);
24678 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24680 * place to the top of element!
24684 this.picker().addClass('top');
24685 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24690 this.picker().addClass('bottom');
24692 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24695 onFocus : function()
24697 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24701 onBlur : function()
24703 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24705 var d = this.inputEl().getValue();
24714 this.picker().show();
24715 this.picker().select('>.datepicker-months', true).first().show();
24719 this.fireEvent('show', this, this.date);
24724 if(this.isInline) {
24727 this.picker().hide();
24728 this.fireEvent('hide', this, this.date);
24732 onMousedown: function(e)
24734 e.stopPropagation();
24735 e.preventDefault();
24740 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24744 fireKey: function(e)
24746 if (!this.picker().isVisible()){
24747 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24758 e.preventDefault();
24762 dir = e.keyCode == 37 ? -1 : 1;
24764 this.vIndex = this.vIndex + dir;
24766 if(this.vIndex < 0){
24770 if(this.vIndex > 11){
24774 if(isNaN(this.vIndex)){
24778 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24784 dir = e.keyCode == 38 ? -1 : 1;
24786 this.vIndex = this.vIndex + dir * 4;
24788 if(this.vIndex < 0){
24792 if(this.vIndex > 11){
24796 if(isNaN(this.vIndex)){
24800 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24805 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24806 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24810 e.preventDefault();
24813 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24814 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24830 this.picker().remove();
24835 Roo.apply(Roo.bootstrap.form.MonthField, {
24854 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24855 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24860 Roo.apply(Roo.bootstrap.form.MonthField, {
24864 cls: 'datepicker dropdown-menu roo-dynamic',
24868 cls: 'datepicker-months',
24872 cls: 'table-condensed',
24874 Roo.bootstrap.form.DateField.content
24894 * @class Roo.bootstrap.form.CheckBox
24895 * @extends Roo.bootstrap.form.Input
24896 * Bootstrap CheckBox class
24898 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24899 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24900 * @cfg {String} boxLabel The text that appears beside the checkbox
24901 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24902 * @cfg {Boolean} checked initnal the element
24903 * @cfg {Boolean} inline inline the element (default false)
24904 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24905 * @cfg {String} tooltip label tooltip
24908 * Create a new CheckBox
24909 * @param {Object} config The config object
24912 Roo.bootstrap.form.CheckBox = function(config){
24913 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24918 * Fires when the element is checked or unchecked.
24919 * @param {Roo.bootstrap.form.CheckBox} this This input
24920 * @param {Boolean} checked The new checked value
24925 * Fires when the element is click.
24926 * @param {Roo.bootstrap.form.CheckBox} this This input
24933 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24935 inputType: 'checkbox',
24944 // checkbox success does not make any sense really..
24949 getAutoCreate : function()
24951 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24957 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24960 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24966 type : this.inputType,
24967 value : this.inputValue,
24968 cls : 'roo-' + this.inputType, //'form-box',
24969 placeholder : this.placeholder || ''
24973 if(this.inputType != 'radio'){
24977 cls : 'roo-hidden-value',
24978 value : this.checked ? this.inputValue : this.valueOff
24983 if (this.weight) { // Validity check?
24984 cfg.cls += " " + this.inputType + "-" + this.weight;
24987 if (this.disabled) {
24988 input.disabled=true;
24992 input.checked = this.checked;
24997 input.name = this.name;
24999 if(this.inputType != 'radio'){
25000 hidden.name = this.name;
25001 input.name = '_hidden_' + this.name;
25006 input.cls += ' input-' + this.size;
25011 ['xs','sm','md','lg'].map(function(size){
25012 if (settings[size]) {
25013 cfg.cls += ' col-' + size + '-' + settings[size];
25017 var inputblock = input;
25019 if (this.before || this.after) {
25022 cls : 'input-group',
25027 inputblock.cn.push({
25029 cls : 'input-group-addon',
25034 inputblock.cn.push(input);
25036 if(this.inputType != 'radio'){
25037 inputblock.cn.push(hidden);
25041 inputblock.cn.push({
25043 cls : 'input-group-addon',
25049 var boxLabelCfg = false;
25055 //'for': id, // box label is handled by onclick - so no for...
25057 html: this.boxLabel
25060 boxLabelCfg.tooltip = this.tooltip;
25066 if (align ==='left' && this.fieldLabel.length) {
25067 // Roo.log("left and has label");
25072 cls : 'control-label',
25073 html : this.fieldLabel
25084 cfg.cn[1].cn.push(boxLabelCfg);
25087 if(this.labelWidth > 12){
25088 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25091 if(this.labelWidth < 13 && this.labelmd == 0){
25092 this.labelmd = this.labelWidth;
25095 if(this.labellg > 0){
25096 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25097 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25100 if(this.labelmd > 0){
25101 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25102 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25105 if(this.labelsm > 0){
25106 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25107 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25110 if(this.labelxs > 0){
25111 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25112 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25115 } else if ( this.fieldLabel.length) {
25116 // Roo.log(" label");
25120 tag: this.boxLabel ? 'span' : 'label',
25122 cls: 'control-label box-input-label',
25123 //cls : 'input-group-addon',
25124 html : this.fieldLabel
25131 cfg.cn.push(boxLabelCfg);
25136 // Roo.log(" no label && no align");
25137 cfg.cn = [ inputblock ] ;
25139 cfg.cn.push(boxLabelCfg);
25147 if(this.inputType != 'radio'){
25148 cfg.cn.push(hidden);
25156 * return the real input element.
25158 inputEl: function ()
25160 return this.el.select('input.roo-' + this.inputType,true).first();
25162 hiddenEl: function ()
25164 return this.el.select('input.roo-hidden-value',true).first();
25167 labelEl: function()
25169 return this.el.select('label.control-label',true).first();
25171 /* depricated... */
25175 return this.labelEl();
25178 boxLabelEl: function()
25180 return this.el.select('label.box-label',true).first();
25183 initEvents : function()
25185 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25187 this.inputEl().on('click', this.onClick, this);
25189 if (this.boxLabel) {
25190 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25193 this.startValue = this.getValue();
25196 Roo.bootstrap.form.CheckBox.register(this);
25200 onClick : function(e)
25202 if(this.fireEvent('click', this, e) !== false){
25203 this.setChecked(!this.checked);
25208 setChecked : function(state,suppressEvent)
25210 this.startValue = this.getValue();
25212 if(this.inputType == 'radio'){
25214 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25215 e.dom.checked = false;
25218 this.inputEl().dom.checked = true;
25220 this.inputEl().dom.value = this.inputValue;
25222 if(suppressEvent !== true){
25223 this.fireEvent('check', this, true);
25231 this.checked = state;
25233 this.inputEl().dom.checked = state;
25236 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25238 if(suppressEvent !== true){
25239 this.fireEvent('check', this, state);
25245 getValue : function()
25247 if(this.inputType == 'radio'){
25248 return this.getGroupValue();
25251 return this.hiddenEl().dom.value;
25255 getGroupValue : function()
25257 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25261 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25264 setValue : function(v,suppressEvent)
25266 if(this.inputType == 'radio'){
25267 this.setGroupValue(v, suppressEvent);
25271 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25276 setGroupValue : function(v, suppressEvent)
25278 this.startValue = this.getValue();
25280 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25281 e.dom.checked = false;
25283 if(e.dom.value == v){
25284 e.dom.checked = true;
25288 if(suppressEvent !== true){
25289 this.fireEvent('check', this, true);
25297 validate : function()
25299 if(this.getVisibilityEl().hasClass('hidden')){
25305 (this.inputType == 'radio' && this.validateRadio()) ||
25306 (this.inputType == 'checkbox' && this.validateCheckbox())
25312 this.markInvalid();
25316 validateRadio : function()
25318 if(this.getVisibilityEl().hasClass('hidden')){
25322 if(this.allowBlank){
25328 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25329 if(!e.dom.checked){
25341 validateCheckbox : function()
25344 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25345 //return (this.getValue() == this.inputValue) ? true : false;
25348 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25356 for(var i in group){
25357 if(group[i].el.isVisible(true)){
25365 for(var i in group){
25370 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25377 * Mark this field as valid
25379 markValid : function()
25383 this.fireEvent('valid', this);
25385 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25388 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25395 if(this.inputType == 'radio'){
25396 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25397 var fg = e.findParent('.form-group', false, true);
25398 if (Roo.bootstrap.version == 3) {
25399 fg.removeClass([_this.invalidClass, _this.validClass]);
25400 fg.addClass(_this.validClass);
25402 fg.removeClass(['is-valid', 'is-invalid']);
25403 fg.addClass('is-valid');
25411 var fg = this.el.findParent('.form-group', false, true);
25412 if (Roo.bootstrap.version == 3) {
25413 fg.removeClass([this.invalidClass, this.validClass]);
25414 fg.addClass(this.validClass);
25416 fg.removeClass(['is-valid', 'is-invalid']);
25417 fg.addClass('is-valid');
25422 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25428 for(var i in group){
25429 var fg = group[i].el.findParent('.form-group', false, true);
25430 if (Roo.bootstrap.version == 3) {
25431 fg.removeClass([this.invalidClass, this.validClass]);
25432 fg.addClass(this.validClass);
25434 fg.removeClass(['is-valid', 'is-invalid']);
25435 fg.addClass('is-valid');
25441 * Mark this field as invalid
25442 * @param {String} msg The validation message
25444 markInvalid : function(msg)
25446 if(this.allowBlank){
25452 this.fireEvent('invalid', this, msg);
25454 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25457 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25461 label.markInvalid();
25464 if(this.inputType == 'radio'){
25466 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25467 var fg = e.findParent('.form-group', false, true);
25468 if (Roo.bootstrap.version == 3) {
25469 fg.removeClass([_this.invalidClass, _this.validClass]);
25470 fg.addClass(_this.invalidClass);
25472 fg.removeClass(['is-invalid', 'is-valid']);
25473 fg.addClass('is-invalid');
25481 var fg = this.el.findParent('.form-group', false, true);
25482 if (Roo.bootstrap.version == 3) {
25483 fg.removeClass([_this.invalidClass, _this.validClass]);
25484 fg.addClass(_this.invalidClass);
25486 fg.removeClass(['is-invalid', 'is-valid']);
25487 fg.addClass('is-invalid');
25492 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25498 for(var i in group){
25499 var fg = group[i].el.findParent('.form-group', false, true);
25500 if (Roo.bootstrap.version == 3) {
25501 fg.removeClass([_this.invalidClass, _this.validClass]);
25502 fg.addClass(_this.invalidClass);
25504 fg.removeClass(['is-invalid', 'is-valid']);
25505 fg.addClass('is-invalid');
25511 clearInvalid : function()
25513 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25515 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25517 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25519 if (label && label.iconEl) {
25520 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25521 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25525 disable : function()
25527 if(this.inputType != 'radio'){
25528 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25535 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25536 _this.getActionEl().addClass(this.disabledClass);
25537 e.dom.disabled = true;
25541 this.disabled = true;
25542 this.fireEvent("disable", this);
25546 enable : function()
25548 if(this.inputType != 'radio'){
25549 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25556 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25557 _this.getActionEl().removeClass(this.disabledClass);
25558 e.dom.disabled = false;
25562 this.disabled = false;
25563 this.fireEvent("enable", this);
25567 setBoxLabel : function(v)
25572 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25578 Roo.apply(Roo.bootstrap.form.CheckBox, {
25583 * register a CheckBox Group
25584 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25586 register : function(checkbox)
25588 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25589 this.groups[checkbox.groupId] = {};
25592 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25596 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25600 * fetch a CheckBox Group based on the group ID
25601 * @param {string} the group ID
25602 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25604 get: function(groupId) {
25605 if (typeof(this.groups[groupId]) == 'undefined') {
25609 return this.groups[groupId] ;
25622 * @class Roo.bootstrap.form.Radio
25623 * @extends Roo.bootstrap.Component
25624 * Bootstrap Radio class
25625 * @cfg {String} boxLabel - the label associated
25626 * @cfg {String} value - the value of radio
25629 * Create a new Radio
25630 * @param {Object} config The config object
25632 Roo.bootstrap.form.Radio = function(config){
25633 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25637 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25643 getAutoCreate : function()
25647 cls : 'form-group radio',
25652 html : this.boxLabel
25660 initEvents : function()
25662 this.parent().register(this);
25664 this.el.on('click', this.onClick, this);
25668 onClick : function(e)
25670 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25671 this.setChecked(true);
25675 setChecked : function(state, suppressEvent)
25677 this.parent().setValue(this.value, suppressEvent);
25681 setBoxLabel : function(v)
25686 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25701 * @class Roo.bootstrap.form.SecurePass
25702 * @extends Roo.bootstrap.form.Input
25703 * Bootstrap SecurePass class
25707 * Create a new SecurePass
25708 * @param {Object} config The config object
25711 Roo.bootstrap.form.SecurePass = function (config) {
25712 // these go here, so the translation tool can replace them..
25714 PwdEmpty: "Please type a password, and then retype it to confirm.",
25715 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25716 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25717 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25718 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25719 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25720 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25721 TooWeak: "Your password is Too Weak."
25723 this.meterLabel = "Password strength:";
25724 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25725 this.meterClass = [
25726 "roo-password-meter-tooweak",
25727 "roo-password-meter-weak",
25728 "roo-password-meter-medium",
25729 "roo-password-meter-strong",
25730 "roo-password-meter-grey"
25735 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25738 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25740 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25742 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25743 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25744 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25745 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25746 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25747 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25748 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25758 * @cfg {String/Object} Label for the strength meter (defaults to
25759 * 'Password strength:')
25764 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25765 * ['Weak', 'Medium', 'Strong'])
25768 pwdStrengths: false,
25781 initEvents: function ()
25783 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25785 if (this.el.is('input[type=password]') && Roo.isSafari) {
25786 this.el.on('keydown', this.SafariOnKeyDown, this);
25789 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25792 onRender: function (ct, position)
25794 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25795 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25796 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25798 this.trigger.createChild({
25803 cls: 'roo-password-meter-grey col-xs-12',
25806 //width: this.meterWidth + 'px'
25810 cls: 'roo-password-meter-text'
25816 if (this.hideTrigger) {
25817 this.trigger.setDisplayed(false);
25819 this.setSize(this.width || '', this.height || '');
25822 onDestroy: function ()
25824 if (this.trigger) {
25825 this.trigger.removeAllListeners();
25826 this.trigger.remove();
25829 this.wrap.remove();
25831 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25834 checkStrength: function ()
25836 var pwd = this.inputEl().getValue();
25837 if (pwd == this._lastPwd) {
25842 if (this.ClientSideStrongPassword(pwd)) {
25844 } else if (this.ClientSideMediumPassword(pwd)) {
25846 } else if (this.ClientSideWeakPassword(pwd)) {
25852 Roo.log('strength1: ' + strength);
25854 //var pm = this.trigger.child('div/div/div').dom;
25855 var pm = this.trigger.child('div/div');
25856 pm.removeClass(this.meterClass);
25857 pm.addClass(this.meterClass[strength]);
25860 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25862 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25864 this._lastPwd = pwd;
25868 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25870 this._lastPwd = '';
25872 var pm = this.trigger.child('div/div');
25873 pm.removeClass(this.meterClass);
25874 pm.addClass('roo-password-meter-grey');
25877 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25880 this.inputEl().dom.type='password';
25883 validateValue: function (value)
25885 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25888 if (value.length == 0) {
25889 if (this.allowBlank) {
25890 this.clearInvalid();
25894 this.markInvalid(this.errors.PwdEmpty);
25895 this.errorMsg = this.errors.PwdEmpty;
25903 if (!value.match(/[\x21-\x7e]+/)) {
25904 this.markInvalid(this.errors.PwdBadChar);
25905 this.errorMsg = this.errors.PwdBadChar;
25908 if (value.length < 6) {
25909 this.markInvalid(this.errors.PwdShort);
25910 this.errorMsg = this.errors.PwdShort;
25913 if (value.length > 16) {
25914 this.markInvalid(this.errors.PwdLong);
25915 this.errorMsg = this.errors.PwdLong;
25919 if (this.ClientSideStrongPassword(value)) {
25921 } else if (this.ClientSideMediumPassword(value)) {
25923 } else if (this.ClientSideWeakPassword(value)) {
25930 if (strength < 2) {
25931 //this.markInvalid(this.errors.TooWeak);
25932 this.errorMsg = this.errors.TooWeak;
25937 console.log('strength2: ' + strength);
25939 //var pm = this.trigger.child('div/div/div').dom;
25941 var pm = this.trigger.child('div/div');
25942 pm.removeClass(this.meterClass);
25943 pm.addClass(this.meterClass[strength]);
25945 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25947 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25949 this.errorMsg = '';
25953 CharacterSetChecks: function (type)
25956 this.fResult = false;
25959 isctype: function (character, type)
25962 case this.kCapitalLetter:
25963 if (character >= 'A' && character <= 'Z') {
25968 case this.kSmallLetter:
25969 if (character >= 'a' && character <= 'z') {
25975 if (character >= '0' && character <= '9') {
25980 case this.kPunctuation:
25981 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25992 IsLongEnough: function (pwd, size)
25994 return !(pwd == null || isNaN(size) || pwd.length < size);
25997 SpansEnoughCharacterSets: function (word, nb)
25999 if (!this.IsLongEnough(word, nb))
26004 var characterSetChecks = new Array(
26005 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
26006 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
26009 for (var index = 0; index < word.length; ++index) {
26010 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26011 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
26012 characterSetChecks[nCharSet].fResult = true;
26019 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
26020 if (characterSetChecks[nCharSet].fResult) {
26025 if (nCharSets < nb) {
26031 ClientSideStrongPassword: function (pwd)
26033 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
26036 ClientSideMediumPassword: function (pwd)
26038 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
26041 ClientSideWeakPassword: function (pwd)
26043 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26047 Roo.htmleditor = {};
26050 * @class Roo.htmleditor.Filter
26051 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26052 * @cfg {DomElement} node The node to iterate and filter
26053 * @cfg {boolean|String|Array} tag Tags to replace
26055 * Create a new Filter.
26056 * @param {Object} config Configuration options
26061 Roo.htmleditor.Filter = function(cfg) {
26062 Roo.apply(this.cfg);
26063 // this does not actually call walk as it's really just a abstract class
26067 Roo.htmleditor.Filter.prototype = {
26073 // overrride to do replace comments.
26074 replaceComment : false,
26076 // overrride to do replace or do stuff with tags..
26077 replaceTag : false,
26079 walk : function(dom)
26081 Roo.each( Array.from(dom.childNodes), function( e ) {
26084 case e.nodeType == 8 && this.replaceComment !== false: // comment
26085 this.replaceComment(e);
26088 case e.nodeType != 1: //not a node.
26091 case this.tag === true: // everything
26092 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26093 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26094 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26095 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26096 if (this.replaceTag && false === this.replaceTag(e)) {
26099 if (e.hasChildNodes()) {
26104 default: // tags .. that do not match.
26105 if (e.hasChildNodes()) {
26115 removeNodeKeepChildren : function( node)
26118 ar = Array.from(node.childNodes);
26119 for (var i = 0; i < ar.length; i++) {
26121 node.removeChild(ar[i]);
26122 // what if we need to walk these???
26123 node.parentNode.insertBefore(ar[i], node);
26126 node.parentNode.removeChild(node);
26131 * @class Roo.htmleditor.FilterAttributes
26132 * clean attributes and styles including http:// etc.. in attribute
26134 * Run a new Attribute Filter
26135 * @param {Object} config Configuration options
26137 Roo.htmleditor.FilterAttributes = function(cfg)
26139 Roo.apply(this, cfg);
26140 this.attrib_black = this.attrib_black || [];
26141 this.attrib_white = this.attrib_white || [];
26143 this.attrib_clean = this.attrib_clean || [];
26144 this.style_white = this.style_white || [];
26145 this.style_black = this.style_black || [];
26146 this.walk(cfg.node);
26149 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26151 tag: true, // all tags
26153 attrib_black : false, // array
26154 attrib_clean : false,
26155 attrib_white : false,
26157 style_white : false,
26158 style_black : false,
26161 replaceTag : function(node)
26163 if (!node.attributes || !node.attributes.length) {
26167 for (var i = node.attributes.length-1; i > -1 ; i--) {
26168 var a = node.attributes[i];
26170 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26171 node.removeAttribute(a.name);
26177 if (a.name.toLowerCase().substr(0,2)=='on') {
26178 node.removeAttribute(a.name);
26183 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26184 node.removeAttribute(a.name);
26187 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26188 this.cleanAttr(node,a.name,a.value); // fixme..
26191 if (a.name == 'style') {
26192 this.cleanStyle(node,a.name,a.value);
26195 /// clean up MS crap..
26196 // tecnically this should be a list of valid class'es..
26199 if (a.name == 'class') {
26200 if (a.value.match(/^Mso/)) {
26201 node.removeAttribute('class');
26204 if (a.value.match(/^body$/)) {
26205 node.removeAttribute('class');
26215 return true; // clean children
26218 cleanAttr: function(node, n,v)
26221 if (v.match(/^\./) || v.match(/^\//)) {
26224 if (v.match(/^(http|https):\/\//)
26225 || v.match(/^mailto:/)
26226 || v.match(/^ftp:/)
26227 || v.match(/^data:/)
26231 if (v.match(/^#/)) {
26234 if (v.match(/^\{/)) { // allow template editing.
26237 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26238 node.removeAttribute(n);
26241 cleanStyle : function(node, n,v)
26243 if (v.match(/expression/)) { //XSS?? should we even bother..
26244 node.removeAttribute(n);
26248 var parts = v.split(/;/);
26251 Roo.each(parts, function(p) {
26252 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26256 var l = p.split(':').shift().replace(/\s+/g,'');
26257 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26259 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26263 // only allow 'c whitelisted system attributes'
26264 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26272 if (clean.length) {
26273 node.setAttribute(n, clean.join(';'));
26275 node.removeAttribute(n);
26284 * @class Roo.htmleditor.FilterBlack
26285 * remove blacklisted elements.
26287 * Run a new Blacklisted Filter
26288 * @param {Object} config Configuration options
26291 Roo.htmleditor.FilterBlack = function(cfg)
26293 Roo.apply(this, cfg);
26294 this.walk(cfg.node);
26297 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26299 tag : true, // all elements.
26301 replaceTag : function(n)
26303 n.parentNode.removeChild(n);
26307 * @class Roo.htmleditor.FilterComment
26310 * Run a new Comments Filter
26311 * @param {Object} config Configuration options
26313 Roo.htmleditor.FilterComment = function(cfg)
26315 this.walk(cfg.node);
26318 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26321 replaceComment : function(n)
26323 n.parentNode.removeChild(n);
26326 * @class Roo.htmleditor.FilterKeepChildren
26327 * remove tags but keep children
26329 * Run a new Keep Children Filter
26330 * @param {Object} config Configuration options
26333 Roo.htmleditor.FilterKeepChildren = function(cfg)
26335 Roo.apply(this, cfg);
26336 if (this.tag === false) {
26337 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26340 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26341 this.cleanNamespace = true;
26344 this.walk(cfg.node);
26347 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26349 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26351 replaceTag : function(node)
26353 // walk children...
26354 //Roo.log(node.tagName);
26355 var ar = Array.from(node.childNodes);
26358 for (var i = 0; i < ar.length; i++) {
26360 if (e.nodeType == 1) {
26362 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26363 || // array and it matches
26364 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26366 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26368 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26370 this.replaceTag(ar[i]); // child is blacklisted as well...
26375 ar = Array.from(node.childNodes);
26376 for (var i = 0; i < ar.length; i++) {
26378 node.removeChild(ar[i]);
26379 // what if we need to walk these???
26380 node.parentNode.insertBefore(ar[i], node);
26381 if (this.tag !== false) {
26386 //Roo.log("REMOVE:" + node.tagName);
26387 node.parentNode.removeChild(node);
26388 return false; // don't walk children
26393 * @class Roo.htmleditor.FilterParagraph
26394 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26395 * like on 'push' to remove the <p> tags and replace them with line breaks.
26397 * Run a new Paragraph Filter
26398 * @param {Object} config Configuration options
26401 Roo.htmleditor.FilterParagraph = function(cfg)
26403 // no need to apply config.
26404 this.walk(cfg.node);
26407 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26414 replaceTag : function(node)
26417 if (node.childNodes.length == 1 &&
26418 node.childNodes[0].nodeType == 3 &&
26419 node.childNodes[0].textContent.trim().length < 1
26421 // remove and replace with '<BR>';
26422 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26423 return false; // no need to walk..
26425 var ar = Array.from(node.childNodes);
26426 for (var i = 0; i < ar.length; i++) {
26427 node.removeChild(ar[i]);
26428 // what if we need to walk these???
26429 node.parentNode.insertBefore(ar[i], node);
26431 // now what about this?
26435 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26436 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26437 node.parentNode.removeChild(node);
26444 * @class Roo.htmleditor.FilterSpan
26445 * filter span's with no attributes out..
26447 * Run a new Span Filter
26448 * @param {Object} config Configuration options
26451 Roo.htmleditor.FilterSpan = function(cfg)
26453 // no need to apply config.
26454 this.walk(cfg.node);
26457 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26463 replaceTag : function(node)
26465 if (node.attributes && node.attributes.length > 0) {
26466 return true; // walk if there are any.
26468 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26474 * @class Roo.htmleditor.FilterTableWidth
26475 try and remove table width data - as that frequently messes up other stuff.
26477 * was cleanTableWidths.
26479 * Quite often pasting from word etc.. results in tables with column and widths.
26480 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26483 * Run a new Table Filter
26484 * @param {Object} config Configuration options
26487 Roo.htmleditor.FilterTableWidth = function(cfg)
26489 // no need to apply config.
26490 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26491 this.walk(cfg.node);
26494 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26499 replaceTag: function(node) {
26503 if (node.hasAttribute('width')) {
26504 node.removeAttribute('width');
26508 if (node.hasAttribute("style")) {
26511 var styles = node.getAttribute("style").split(";");
26513 Roo.each(styles, function(s) {
26514 if (!s.match(/:/)) {
26517 var kv = s.split(":");
26518 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26521 // what ever is left... we allow.
26524 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26525 if (!nstyle.length) {
26526 node.removeAttribute('style');
26530 return true; // continue doing children..
26533 * @class Roo.htmleditor.FilterWord
26534 * try and clean up all the mess that Word generates.
26536 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26539 * Run a new Span Filter
26540 * @param {Object} config Configuration options
26543 Roo.htmleditor.FilterWord = function(cfg)
26545 // no need to apply config.
26546 this.replaceDocBullets(cfg.node);
26548 this.replaceAname(cfg.node);
26549 // this is disabled as the removal is done by other filters;
26550 // this.walk(cfg.node);
26555 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26561 * Clean up MS wordisms...
26563 replaceTag : function(node)
26566 // no idea what this does - span with text, replaceds with just text.
26568 node.nodeName == 'SPAN' &&
26569 !node.hasAttributes() &&
26570 node.childNodes.length == 1 &&
26571 node.firstChild.nodeName == "#text"
26573 var textNode = node.firstChild;
26574 node.removeChild(textNode);
26575 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26576 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26578 node.parentNode.insertBefore(textNode, node);
26579 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26580 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26583 node.parentNode.removeChild(node);
26584 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26589 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26590 node.parentNode.removeChild(node);
26591 return false; // dont do chidlren
26593 //Roo.log(node.tagName);
26594 // remove - but keep children..
26595 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26596 //Roo.log('-- removed');
26597 while (node.childNodes.length) {
26598 var cn = node.childNodes[0];
26599 node.removeChild(cn);
26600 node.parentNode.insertBefore(cn, node);
26601 // move node to parent - and clean it..
26602 if (cn.nodeType == 1) {
26603 this.replaceTag(cn);
26607 node.parentNode.removeChild(node);
26608 /// no need to iterate chidlren = it's got none..
26609 //this.iterateChildren(node, this.cleanWord);
26610 return false; // no need to iterate children.
26613 if (node.className.length) {
26615 var cn = node.className.split(/\W+/);
26617 Roo.each(cn, function(cls) {
26618 if (cls.match(/Mso[a-zA-Z]+/)) {
26623 node.className = cna.length ? cna.join(' ') : '';
26625 node.removeAttribute("class");
26629 if (node.hasAttribute("lang")) {
26630 node.removeAttribute("lang");
26633 if (node.hasAttribute("style")) {
26635 var styles = node.getAttribute("style").split(";");
26637 Roo.each(styles, function(s) {
26638 if (!s.match(/:/)) {
26641 var kv = s.split(":");
26642 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26645 // what ever is left... we allow.
26648 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26649 if (!nstyle.length) {
26650 node.removeAttribute('style');
26653 return true; // do children
26659 styleToObject: function(node)
26661 var styles = (node.getAttribute("style") || '').split(";");
26663 Roo.each(styles, function(s) {
26664 if (!s.match(/:/)) {
26667 var kv = s.split(":");
26669 // what ever is left... we allow.
26670 ret[kv[0].trim()] = kv[1];
26676 replaceAname : function (doc)
26678 // replace all the a/name without..
26679 var aa = Array.from(doc.getElementsByTagName('a'));
26680 for (var i = 0; i < aa.length; i++) {
26682 if (a.hasAttribute("name")) {
26683 a.removeAttribute("name");
26685 if (a.hasAttribute("href")) {
26688 // reparent children.
26689 this.removeNodeKeepChildren(a);
26699 replaceDocBullets : function(doc)
26701 // this is a bit odd - but it appears some indents use ql-indent-1
26702 //Roo.log(doc.innerHTML);
26704 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
26705 for( var i = 0; i < listpara.length; i ++) {
26706 listpara[i].className = "MsoListParagraph";
26709 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
26710 for( var i = 0; i < listpara.length; i ++) {
26711 listpara[i].className = "MsoListParagraph";
26713 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
26714 for( var i = 0; i < listpara.length; i ++) {
26715 listpara[i].className = "MsoListParagraph";
26717 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
26718 for( var i = 0; i < listpara.length; i ++) {
26719 listpara[i].className = "MsoListParagraph";
26722 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
26723 var htwo = Array.from(doc.getElementsByTagName('h2'));
26724 for( var i = 0; i < htwo.length; i ++) {
26725 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
26726 htwo[i].className = "MsoListParagraph";
26729 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
26730 for( var i = 0; i < listpara.length; i ++) {
26731 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
26732 listpara[i].className = "MsoListParagraph";
26734 listpara[i].className = "MsoNormalx";
26738 listpara = doc.getElementsByClassName('MsoListParagraph');
26739 // Roo.log(doc.innerHTML);
26743 while(listpara.length) {
26745 this.replaceDocBullet(listpara.item(0));
26752 replaceDocBullet : function(p)
26754 // gather all the siblings.
26756 parent = p.parentNode,
26757 doc = parent.ownerDocument,
26760 var listtype = 'ul';
26762 if (ns.nodeType != 1) {
26763 ns = ns.nextSibling;
26766 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26769 var spans = ns.getElementsByTagName('span');
26770 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
26772 ns = ns.nextSibling;
26774 if (spans.length && spans[0].hasAttribute('style')) {
26775 var style = this.styleToObject(spans[0]);
26776 if (typeof(style['font-family']) != 'undefined' && !style['font-family'].match(/Symbol/)) {
26783 var spans = ns.getElementsByTagName('span');
26784 if (!spans.length) {
26787 var has_list = false;
26788 for(var i = 0; i < spans.length; i++) {
26789 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
26798 ns = ns.nextSibling;
26802 if (!items.length) {
26807 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
26808 parent.insertBefore(ul, p);
26810 var stack = [ ul ];
26811 var last_li = false;
26813 var margin_to_depth = {};
26816 items.forEach(function(n, ipos) {
26817 //Roo.log("got innertHMLT=" + n.innerHTML);
26819 var spans = n.getElementsByTagName('span');
26820 if (!spans.length) {
26821 //Roo.log("No spans found");
26823 parent.removeChild(n);
26826 return; // skip it...
26832 for(var i = 0; i < spans.length; i++) {
26834 style = this.styleToObject(spans[i]);
26835 if (typeof(style['mso-list']) == 'undefined') {
26838 if (listtype == 'ol') {
26839 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
26841 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26844 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26845 style = this.styleToObject(n); // mo-list is from the parent node.
26846 if (typeof(style['mso-list']) == 'undefined') {
26847 //Roo.log("parent is missing level");
26849 parent.removeChild(n);
26854 var margin = style['margin-left'];
26855 if (typeof(margin_to_depth[margin]) == 'undefined') {
26857 margin_to_depth[margin] = max_margins;
26859 nlvl = margin_to_depth[margin] ;
26863 var nul = doc.createElement(listtype); // what about number lists...
26865 last_li = doc.createElement('li');
26866 stack[lvl].appendChild(last_li);
26868 last_li.appendChild(nul);
26874 // not starting at 1..
26875 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
26876 stack[nlvl].setAttribute("start", num);
26879 var nli = stack[nlvl].appendChild(doc.createElement('li'));
26881 nli.innerHTML = n.innerHTML;
26882 //Roo.log("innerHTML = " + n.innerHTML);
26883 parent.removeChild(n);
26899 * @class Roo.htmleditor.FilterStyleToTag
26900 * part of the word stuff... - certain 'styles' should be converted to tags.
26902 * font-weight: bold -> bold
26903 * ?? super / subscrit etc..
26906 * Run a new style to tag filter.
26907 * @param {Object} config Configuration options
26909 Roo.htmleditor.FilterStyleToTag = function(cfg)
26913 B : [ 'fontWeight' , 'bold'],
26914 I : [ 'fontStyle' , 'italic'],
26915 //pre : [ 'font-style' , 'italic'],
26916 // h1.. h6 ?? font-size?
26917 SUP : [ 'verticalAlign' , 'super' ],
26918 SUB : [ 'verticalAlign' , 'sub' ]
26923 Roo.apply(this, cfg);
26926 this.walk(cfg.node);
26933 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26935 tag: true, // all tags
26940 replaceTag : function(node)
26944 if (node.getAttribute("style") === null) {
26948 for (var k in this.tags) {
26949 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26951 node.style.removeProperty(this.tags[k][0]);
26954 if (!inject.length) {
26957 var cn = Array.from(node.childNodes);
26959 Roo.each(inject, function(t) {
26960 var nc = node.ownerDocument.createElement(t);
26961 nn.appendChild(nc);
26964 for(var i = 0;i < cn.length;cn++) {
26965 node.removeChild(cn[i]);
26966 nn.appendChild(cn[i]);
26968 return true /// iterate thru
26972 * @class Roo.htmleditor.FilterLongBr
26973 * BR/BR/BR - keep a maximum of 2...
26975 * Run a new Long BR Filter
26976 * @param {Object} config Configuration options
26979 Roo.htmleditor.FilterLongBr = function(cfg)
26981 // no need to apply config.
26982 this.walk(cfg.node);
26985 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26992 replaceTag : function(node)
26995 var ps = node.nextSibling;
26996 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26997 ps = ps.nextSibling;
27000 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27001 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27005 if (!ps || ps.nodeType != 1) {
27009 if (!ps || ps.tagName != 'BR') {
27018 if (!node.previousSibling) {
27021 var ps = node.previousSibling;
27023 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27024 ps = ps.previousSibling;
27026 if (!ps || ps.nodeType != 1) {
27029 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27030 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27034 node.parentNode.removeChild(node); // remove me...
27036 return false; // no need to do children
27043 * @class Roo.htmleditor.FilterBlock
27044 * removes id / data-block and contenteditable that are associated with blocks
27045 * usage should be done on a cloned copy of the dom
27047 * Run a new Attribute Filter { node : xxxx }}
27048 * @param {Object} config Configuration options
27050 Roo.htmleditor.FilterBlock = function(cfg)
27052 Roo.apply(this, cfg);
27053 var qa = cfg.node.querySelectorAll;
27054 this.removeAttributes('data-block');
27055 this.removeAttributes('contenteditable');
27056 this.removeAttributes('id');
27060 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27062 node: true, // all tags
27065 removeAttributes : function(attr)
27067 var ar = this.node.querySelectorAll('*[' + attr + ']');
27068 for (var i =0;i<ar.length;i++) {
27069 ar[i].removeAttribute(attr);
27078 * This is based loosely on tinymce
27079 * @class Roo.htmleditor.TidySerializer
27080 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27082 * @method Serializer
27083 * @param {Object} settings Name/value settings object.
27087 Roo.htmleditor.TidySerializer = function(settings)
27089 Roo.apply(this, settings);
27091 this.writer = new Roo.htmleditor.TidyWriter(settings);
27096 Roo.htmleditor.TidySerializer.prototype = {
27099 * @param {boolean} inner do the inner of the node.
27106 * Serializes the specified node into a string.
27109 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27110 * @method serialize
27111 * @param {DomElement} node Node instance to serialize.
27112 * @return {String} String with HTML based on DOM tree.
27114 serialize : function(node) {
27116 // = settings.validate;
27117 var writer = this.writer;
27121 3: function(node) {
27123 writer.text(node.nodeValue, node);
27126 8: function(node) {
27127 writer.comment(node.nodeValue);
27129 // Processing instruction
27130 7: function(node) {
27131 writer.pi(node.name, node.nodeValue);
27134 10: function(node) {
27135 writer.doctype(node.nodeValue);
27138 4: function(node) {
27139 writer.cdata(node.nodeValue);
27141 // Document fragment
27142 11: function(node) {
27143 node = node.firstChild;
27149 node = node.nextSibling
27154 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27155 return writer.getContent();
27158 walk: function(node)
27160 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27161 handler = this.handlers[node.nodeType];
27168 var name = node.nodeName;
27169 var isEmpty = node.childNodes.length < 1;
27171 var writer = this.writer;
27172 var attrs = node.attributes;
27175 writer.start(node.nodeName, attrs, isEmpty, node);
27179 node = node.firstChild;
27186 node = node.nextSibling;
27192 // Serialize element and treat all non elements as fragments
27197 * This is based loosely on tinymce
27198 * @class Roo.htmleditor.TidyWriter
27199 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27202 * - not tested much with 'PRE' formated elements.
27208 Roo.htmleditor.TidyWriter = function(settings)
27211 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27212 Roo.apply(this, settings);
27216 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27219 Roo.htmleditor.TidyWriter.prototype = {
27226 // part of state...
27230 last_inline : false,
27235 * Writes the a start element such as <p id="a">.
27238 * @param {String} name Name of the element.
27239 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27240 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27242 start: function(name, attrs, empty, node)
27244 var i, l, attr, value;
27246 // there are some situations where adding line break && indentation will not work. will not work.
27247 // <span / b / i ... formating?
27249 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27250 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27252 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27254 var add_lb = name == 'BR' ? false : in_inline;
27256 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27260 var indentstr = this.indentstr;
27262 // e_inline = elements that can be inline, but still allow \n before and after?
27263 // only 'BR' ??? any others?
27265 // ADD LINE BEFORE tage
27266 if (!this.in_pre) {
27269 if (name == 'BR') {
27271 } else if (this.lastElementEndsWS()) {
27274 // otherwise - no new line. (and dont indent.)
27285 this.html.push(indentstr + '<', name.toLowerCase());
27288 for (i = 0, l = attrs.length; i < l; i++) {
27290 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27296 this.html[this.html.length] = '/>';
27298 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27300 var e_inline = name == 'BR' ? false : this.in_inline;
27302 if (!e_inline && !this.in_pre) {
27309 this.html[this.html.length] = '>';
27311 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27313 if (!in_inline && !in_pre) {
27314 var cn = node.firstChild;
27316 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27320 cn = cn.nextSibling;
27328 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27330 in_inline : in_inline
27332 // add a line after if we are not in a
27334 if (!in_inline && !in_pre) {
27343 lastElementEndsWS : function()
27345 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27346 if (value === false) {
27349 return value.match(/\s+$/);
27354 * Writes the a end element such as </p>.
27357 * @param {String} name Name of the element.
27359 end: function(name) {
27362 var indentstr = '';
27363 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27365 if (!this.in_pre && !in_inline) {
27367 indentstr = this.indentstr;
27369 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27370 this.last_inline = in_inline;
27372 // pop the indent state..
27375 * Writes a text node.
27377 * In pre - we should not mess with the contents.
27381 * @param {String} text String to write out.
27382 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27384 text: function(in_text, node)
27386 // if not in whitespace critical
27387 if (in_text.length < 1) {
27390 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27393 this.html[this.html.length] = text;
27397 if (this.in_inline) {
27398 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27400 text = text.replace(/\s+/,' '); // all white space to single white space
27403 // if next tag is '<BR>', then we can trim right..
27404 if (node.nextSibling &&
27405 node.nextSibling.nodeType == 1 &&
27406 node.nextSibling.nodeName == 'BR' )
27408 text = text.replace(/\s+$/g,'');
27410 // if previous tag was a BR, we can also trim..
27411 if (node.previousSibling &&
27412 node.previousSibling.nodeType == 1 &&
27413 node.previousSibling.nodeName == 'BR' )
27415 text = this.indentstr + text.replace(/^\s+/g,'');
27417 if (text.match(/\n/)) {
27418 text = text.replace(
27419 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27421 // remoeve the last whitespace / line break.
27422 text = text.replace(/\n\s+$/,'');
27424 // repace long lines
27428 this.html[this.html.length] = text;
27431 // see if previous element was a inline element.
27432 var indentstr = this.indentstr;
27434 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27436 // should trim left?
27437 if (node.previousSibling &&
27438 node.previousSibling.nodeType == 1 &&
27439 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27445 text = text.replace(/^\s+/,''); // trim left
27448 // should trim right?
27449 if (node.nextSibling &&
27450 node.nextSibling.nodeType == 1 &&
27451 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27456 text = text.replace(/\s+$/,''); // trim right
27463 if (text.length < 1) {
27466 if (!text.match(/\n/)) {
27467 this.html.push(indentstr + text);
27471 text = this.indentstr + text.replace(
27472 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27474 // remoeve the last whitespace / line break.
27475 text = text.replace(/\s+$/,'');
27477 this.html.push(text);
27479 // split and indent..
27484 * Writes a cdata node such as <![CDATA[data]]>.
27487 * @param {String} text String to write out inside the cdata.
27489 cdata: function(text) {
27490 this.html.push('<![CDATA[', text, ']]>');
27493 * Writes a comment node such as <!-- Comment -->.
27496 * @param {String} text String to write out inside the comment.
27498 comment: function(text) {
27499 this.html.push('<!--', text, '-->');
27502 * Writes a PI node such as <?xml attr="value" ?>.
27505 * @param {String} name Name of the pi.
27506 * @param {String} text String to write out inside the pi.
27508 pi: function(name, text) {
27509 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
27510 this.indent != '' && this.html.push('\n');
27513 * Writes a doctype node such as <!DOCTYPE data>.
27516 * @param {String} text String to write out inside the doctype.
27518 doctype: function(text) {
27519 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
27522 * Resets the internal buffer if one wants to reuse the writer.
27526 reset: function() {
27527 this.html.length = 0;
27536 * Returns the contents that got serialized.
27538 * @method getContent
27539 * @return {String} HTML contents that got written down.
27541 getContent: function() {
27542 return this.html.join('').replace(/\n$/, '');
27545 pushState : function(cfg)
27547 this.state.push(cfg);
27548 Roo.apply(this, cfg);
27551 popState : function()
27553 if (this.state.length < 1) {
27554 return; // nothing to push
27561 if (this.state.length > 0) {
27562 cfg = this.state[this.state.length-1];
27564 Roo.apply(this, cfg);
27567 addLine: function()
27569 if (this.html.length < 1) {
27574 var value = this.html[this.html.length - 1];
27575 if (value.length > 0 && '\n' !== value) {
27576 this.html.push('\n');
27581 //'pre script noscript style textarea video audio iframe object code'
27582 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
27586 Roo.htmleditor.TidyWriter.inline_elements = [
27587 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
27588 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
27590 Roo.htmleditor.TidyWriter.shortend_elements = [
27591 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
27592 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
27595 Roo.htmleditor.TidyWriter.whitespace_elements = [
27596 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
27598 * This is based loosely on tinymce
27599 * @class Roo.htmleditor.TidyEntities
27601 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27603 * Not 100% sure this is actually used or needed.
27606 Roo.htmleditor.TidyEntities = {
27609 * initialize data..
27611 init : function (){
27613 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
27618 buildEntitiesLookup: function(items, radix) {
27619 var i, chr, entity, lookup = {};
27623 items = typeof(items) == 'string' ? items.split(',') : items;
27624 radix = radix || 10;
27625 // Build entities lookup table
27626 for (i = 0; i < items.length; i += 2) {
27627 chr = String.fromCharCode(parseInt(items[i], radix));
27628 // Only add non base entities
27629 if (!this.baseEntities[chr]) {
27630 entity = '&' + items[i + 1] + ';';
27631 lookup[chr] = entity;
27632 lookup[entity] = chr;
27671 // Needs to be escaped since the YUI compressor would otherwise break the code
27678 // Reverse lookup table for raw entities
27679 reverseEntities : {
27687 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27688 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
27689 rawCharsRegExp : /[<>&\"\']/g,
27690 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
27691 namedEntities : false,
27692 namedEntitiesData : [
28193 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28195 * @method encodeRaw
28196 * @param {String} text Text to encode.
28197 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28198 * @return {String} Entity encoded text.
28200 encodeRaw: function(text, attr)
28203 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28204 return t.baseEntities[chr] || chr;
28208 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28209 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28210 * and is exposed as the DOMUtils.encode function.
28212 * @method encodeAllRaw
28213 * @param {String} text Text to encode.
28214 * @return {String} Entity encoded text.
28216 encodeAllRaw: function(text) {
28218 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28219 return t.baseEntities[chr] || chr;
28223 * Encodes the specified string using numeric entities. The core entities will be
28224 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28226 * @method encodeNumeric
28227 * @param {String} text Text to encode.
28228 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28229 * @return {String} Entity encoded text.
28231 encodeNumeric: function(text, attr) {
28233 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28234 // Multi byte sequence convert it to a single entity
28235 if (chr.length > 1) {
28236 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28238 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28242 * Encodes the specified string using named entities. The core entities will be encoded
28243 * as named ones but all non lower ascii characters will be encoded into named entities.
28245 * @method encodeNamed
28246 * @param {String} text Text to encode.
28247 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28248 * @param {Object} entities Optional parameter with entities to use.
28249 * @return {String} Entity encoded text.
28251 encodeNamed: function(text, attr, entities) {
28253 entities = entities || this.namedEntities;
28254 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28255 return t.baseEntities[chr] || entities[chr] || chr;
28259 * Returns an encode function based on the name(s) and it's optional entities.
28261 * @method getEncodeFunc
28262 * @param {String} name Comma separated list of encoders for example named,numeric.
28263 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28264 * @return {function} Encode function to be used.
28266 getEncodeFunc: function(name, entities) {
28267 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28269 function encodeNamedAndNumeric(text, attr) {
28270 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28271 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28275 function encodeCustomNamed(text, attr) {
28276 return t.encodeNamed(text, attr, entities);
28278 // Replace + with , to be compatible with previous TinyMCE versions
28279 name = this.makeMap(name.replace(/\+/g, ','));
28280 // Named and numeric encoder
28281 if (name.named && name.numeric) {
28282 return this.encodeNamedAndNumeric;
28288 return encodeCustomNamed;
28290 return this.encodeNamed;
28293 if (name.numeric) {
28294 return this.encodeNumeric;
28297 return this.encodeRaw;
28300 * Decodes the specified string, this will replace entities with raw UTF characters.
28303 * @param {String} text Text to entity decode.
28304 * @return {String} Entity decoded string.
28306 decode: function(text)
28309 return text.replace(this.entityRegExp, function(all, numeric) {
28311 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28312 // Support upper UTF
28313 if (numeric > 65535) {
28315 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28317 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28319 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28322 nativeDecode : function (text) {
28325 makeMap : function (items, delim, map) {
28327 items = items || [];
28328 delim = delim || ',';
28329 if (typeof items == "string") {
28330 items = items.split(delim);
28335 map[items[i]] = {};
28343 Roo.htmleditor.TidyEntities.init();
28345 * @class Roo.htmleditor.KeyEnter
28346 * Handle Enter press..
28347 * @cfg {Roo.HtmlEditorCore} core the editor.
28349 * Create a new Filter.
28350 * @param {Object} config Configuration options
28357 Roo.htmleditor.KeyEnter = function(cfg) {
28358 Roo.apply(this, cfg);
28359 // this does not actually call walk as it's really just a abstract class
28361 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28364 //Roo.htmleditor.KeyEnter.i = 0;
28367 Roo.htmleditor.KeyEnter.prototype = {
28371 keypress : function(e)
28373 if (e.charCode != 13 && e.charCode != 10) {
28374 Roo.log([e.charCode,e]);
28377 e.preventDefault();
28378 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28379 var doc = this.core.doc;
28383 var sel = this.core.getSelection();
28384 var range = sel.getRangeAt(0);
28385 var n = range.commonAncestorContainer;
28386 var pc = range.closest([ 'ol', 'ul']);
28387 var pli = range.closest('li');
28388 if (!pc || e.ctrlKey) {
28389 // on it list, or ctrl pressed.
28391 sel.insertNode('br', 'after');
28393 // only do this if we have ctrl key..
28394 var br = doc.createElement('br');
28395 br.className = 'clear';
28396 br.setAttribute('style', 'clear: both');
28397 sel.insertNode(br, 'after');
28401 this.core.undoManager.addEvent();
28402 this.core.fireEditorEvent(e);
28406 // deal with <li> insetion
28407 if (pli.innerText.trim() == '' &&
28408 pli.previousSibling &&
28409 pli.previousSibling.nodeName == 'LI' &&
28410 pli.previousSibling.innerText.trim() == '') {
28411 pli.parentNode.removeChild(pli.previousSibling);
28412 sel.cursorAfter(pc);
28413 this.core.undoManager.addEvent();
28414 this.core.fireEditorEvent(e);
28418 var li = doc.createElement('LI');
28419 li.innerHTML = ' ';
28420 if (!pli || !pli.firstSibling) {
28421 pc.appendChild(li);
28423 pli.parentNode.insertBefore(li, pli.firstSibling);
28425 sel.cursorText (li.firstChild);
28427 this.core.undoManager.addEvent();
28428 this.core.fireEditorEvent(e);
28440 * @class Roo.htmleditor.Block
28441 * Base class for html editor blocks - do not use it directly .. extend it..
28442 * @cfg {DomElement} node The node to apply stuff to.
28443 * @cfg {String} friendly_name the name that appears in the context bar about this block
28444 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28447 * Create a new Filter.
28448 * @param {Object} config Configuration options
28451 Roo.htmleditor.Block = function(cfg)
28453 // do nothing .. should not be called really.
28456 * factory method to get the block from an element (using cache if necessary)
28458 * @param {HtmlElement} the dom element
28460 Roo.htmleditor.Block.factory = function(node)
28462 var cc = Roo.htmleditor.Block.cache;
28463 var id = Roo.get(node).id;
28464 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28465 Roo.htmleditor.Block.cache[id].readElement(node);
28466 return Roo.htmleditor.Block.cache[id];
28468 var db = node.getAttribute('data-block');
28470 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28472 var cls = Roo.htmleditor['Block' + db];
28473 if (typeof(cls) == 'undefined') {
28474 //Roo.log(node.getAttribute('data-block'));
28475 Roo.log("OOps missing block : " + 'Block' + db);
28478 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28479 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
28483 * initalize all Elements from content that are 'blockable'
28485 * @param the body element
28487 Roo.htmleditor.Block.initAll = function(body, type)
28489 if (typeof(type) == 'undefined') {
28490 var ia = Roo.htmleditor.Block.initAll;
28496 Roo.each(Roo.get(body).query(type), function(e) {
28497 Roo.htmleditor.Block.factory(e);
28500 // question goes here... do we need to clear out this cache sometimes?
28501 // or show we make it relivant to the htmleditor.
28502 Roo.htmleditor.Block.cache = {};
28504 Roo.htmleditor.Block.prototype = {
28508 // used by context menu
28509 friendly_name : 'Based Block',
28511 // text for button to delete this element
28512 deleteTitle : false,
28516 * Update a node with values from this object
28517 * @param {DomElement} node
28519 updateElement : function(node)
28521 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
28524 * convert to plain HTML for calling insertAtCursor..
28526 toHTML : function()
28528 return Roo.DomHelper.markup(this.toObject());
28531 * used by readEleemnt to extract data from a node
28532 * may need improving as it's pretty basic
28534 * @param {DomElement} node
28535 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
28536 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
28537 * @param {String} style the style property - eg. text-align
28539 getVal : function(node, tag, attr, style)
28542 if (tag !== true && n.tagName != tag.toUpperCase()) {
28543 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
28544 // but kiss for now.
28545 n = node.getElementsByTagName(tag).item(0);
28550 if (attr === false) {
28553 if (attr == 'html') {
28554 return n.innerHTML;
28556 if (attr == 'style') {
28557 return n.style[style];
28560 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
28564 * create a DomHelper friendly object - for use with
28565 * Roo.DomHelper.markup / overwrite / etc..
28568 toObject : function()
28573 * Read a node that has a 'data-block' property - and extract the values from it.
28574 * @param {DomElement} node - the node
28576 readElement : function(node)
28587 * @class Roo.htmleditor.BlockFigure
28588 * Block that has an image and a figcaption
28589 * @cfg {String} image_src the url for the image
28590 * @cfg {String} align (left|right) alignment for the block default left
28591 * @cfg {String} caption the text to appear below (and in the alt tag)
28592 * @cfg {String} caption_display (block|none) display or not the caption
28593 * @cfg {String|number} image_width the width of the image number or %?
28594 * @cfg {String|number} image_height the height of the image number or %?
28597 * Create a new Filter.
28598 * @param {Object} config Configuration options
28601 Roo.htmleditor.BlockFigure = function(cfg)
28604 this.readElement(cfg.node);
28605 this.updateElement(cfg.node);
28607 Roo.apply(this, cfg);
28609 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
28616 caption_display : 'block',
28622 // margin: '2%', not used
28624 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
28627 // used by context menu
28628 friendly_name : 'Image with caption',
28629 deleteTitle : "Delete Image and Caption",
28631 contextMenu : function(toolbar)
28634 var block = function() {
28635 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28639 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28641 var syncValue = toolbar.editorcore.syncValue;
28647 xtype : 'TextItem',
28649 xns : rooui.Toolbar //Boostrap?
28653 text: 'Change Image URL',
28656 click: function (btn, state)
28660 Roo.MessageBox.show({
28661 title : "Image Source URL",
28662 msg : "Enter the url for the image",
28663 buttons: Roo.MessageBox.OKCANCEL,
28664 fn: function(btn, val){
28671 toolbar.editorcore.onEditorEvent();
28675 //multiline: multiline,
28677 value : b.image_src
28681 xns : rooui.Toolbar
28686 text: 'Change Link URL',
28689 click: function (btn, state)
28693 Roo.MessageBox.show({
28694 title : "Link URL",
28695 msg : "Enter the url for the link - leave blank to have no link",
28696 buttons: Roo.MessageBox.OKCANCEL,
28697 fn: function(btn, val){
28704 toolbar.editorcore.onEditorEvent();
28708 //multiline: multiline,
28714 xns : rooui.Toolbar
28718 text: 'Show Video URL',
28721 click: function (btn, state)
28723 Roo.MessageBox.alert("Video URL",
28724 block().video_url == '' ? 'This image is not linked ot a video' :
28725 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
28728 xns : rooui.Toolbar
28733 xtype : 'TextItem',
28735 xns : rooui.Toolbar //Boostrap?
28738 xtype : 'ComboBox',
28739 allowBlank : false,
28740 displayField : 'val',
28743 triggerAction : 'all',
28745 valueField : 'val',
28749 select : function (combo, r, index)
28751 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28753 b.width = r.get('val');
28756 toolbar.editorcore.onEditorEvent();
28761 xtype : 'SimpleStore',
28774 xtype : 'TextItem',
28776 xns : rooui.Toolbar //Boostrap?
28779 xtype : 'ComboBox',
28780 allowBlank : false,
28781 displayField : 'val',
28784 triggerAction : 'all',
28786 valueField : 'val',
28790 select : function (combo, r, index)
28792 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28794 b.align = r.get('val');
28797 toolbar.editorcore.onEditorEvent();
28802 xtype : 'SimpleStore',
28816 text: 'Hide Caption',
28817 name : 'caption_display',
28819 enableToggle : true,
28820 setValue : function(v) {
28821 // this trigger toggle.
28823 this.setText(v ? "Hide Caption" : "Show Caption");
28824 this.setPressed(v != 'block');
28827 toggle: function (btn, state)
28830 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
28831 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
28834 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28835 toolbar.editorcore.onEditorEvent();
28838 xns : rooui.Toolbar
28844 * create a DomHelper friendly object - for use with
28845 * Roo.DomHelper.markup / overwrite / etc..
28847 toObject : function()
28849 var d = document.createElement('div');
28850 d.innerHTML = this.caption;
28852 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
28854 var iw = this.align == 'center' ? this.width : '100%';
28857 contenteditable : 'false',
28858 src : this.image_src,
28859 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
28862 maxWidth : iw + ' !important', // this is not getting rendered?
28868 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
28870 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
28875 if (this.href.length > 0) {
28879 contenteditable : 'true',
28887 if (this.video_url.length > 0) {
28892 allowfullscreen : true,
28893 width : 420, // these are for video tricks - that we replace the outer
28895 src : this.video_url,
28901 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
28902 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
28907 'data-block' : 'Figure',
28908 'data-width' : this.width,
28909 contenteditable : 'false',
28913 float : this.align ,
28914 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
28915 width : this.align == 'center' ? '100%' : this.width,
28917 padding: this.align == 'center' ? '0' : '0 10px' ,
28918 textAlign : this.align // seems to work for email..
28923 align : this.align,
28929 'data-display' : this.caption_display,
28931 textAlign : 'left',
28933 lineHeight : '24px',
28934 display : this.caption_display,
28935 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
28937 width: this.align == 'center' ? this.width : '100%'
28941 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
28946 marginTop : '16px',
28952 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
28954 contenteditable : true,
28970 readElement : function(node)
28972 // this should not really come from the link...
28973 this.video_url = this.getVal(node, 'div', 'src');
28974 this.cls = this.getVal(node, 'div', 'class');
28975 this.href = this.getVal(node, 'a', 'href');
28978 this.image_src = this.getVal(node, 'img', 'src');
28980 this.align = this.getVal(node, 'figure', 'align');
28981 var figcaption = this.getVal(node, 'figcaption', false);
28982 if (figcaption !== '') {
28983 this.caption = this.getVal(figcaption, 'i', 'html');
28987 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
28988 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
28989 this.width = this.getVal(node, true, 'data-width');
28990 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
28993 removeNode : function()
29010 * @class Roo.htmleditor.BlockTable
29011 * Block that manages a table
29014 * Create a new Filter.
29015 * @param {Object} config Configuration options
29018 Roo.htmleditor.BlockTable = function(cfg)
29021 this.readElement(cfg.node);
29022 this.updateElement(cfg.node);
29024 Roo.apply(this, cfg);
29027 for(var r = 0; r < this.no_row; r++) {
29029 for(var c = 0; c < this.no_col; c++) {
29030 this.rows[r][c] = this.emptyCell();
29037 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29046 // used by context menu
29047 friendly_name : 'Table',
29048 deleteTitle : 'Delete Table',
29049 // context menu is drawn once..
29051 contextMenu : function(toolbar)
29054 var block = function() {
29055 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29059 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29061 var syncValue = toolbar.editorcore.syncValue;
29067 xtype : 'TextItem',
29069 xns : rooui.Toolbar //Boostrap?
29072 xtype : 'ComboBox',
29073 allowBlank : false,
29074 displayField : 'val',
29077 triggerAction : 'all',
29079 valueField : 'val',
29083 select : function (combo, r, index)
29085 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29087 b.width = r.get('val');
29090 toolbar.editorcore.onEditorEvent();
29095 xtype : 'SimpleStore',
29107 xtype : 'TextItem',
29108 text : "Columns: ",
29109 xns : rooui.Toolbar //Boostrap?
29116 click : function (_self, e)
29118 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29119 block().removeColumn();
29121 toolbar.editorcore.onEditorEvent();
29124 xns : rooui.Toolbar
29130 click : function (_self, e)
29132 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29133 block().addColumn();
29135 toolbar.editorcore.onEditorEvent();
29138 xns : rooui.Toolbar
29142 xtype : 'TextItem',
29144 xns : rooui.Toolbar //Boostrap?
29151 click : function (_self, e)
29153 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29154 block().removeRow();
29156 toolbar.editorcore.onEditorEvent();
29159 xns : rooui.Toolbar
29165 click : function (_self, e)
29169 toolbar.editorcore.onEditorEvent();
29172 xns : rooui.Toolbar
29177 text: 'Reset Column Widths',
29180 click : function (_self, e)
29182 block().resetWidths();
29184 toolbar.editorcore.onEditorEvent();
29187 xns : rooui.Toolbar
29198 * create a DomHelper friendly object - for use with
29199 * Roo.DomHelper.markup / overwrite / etc..
29200 * ?? should it be called with option to hide all editing features?
29202 toObject : function()
29207 contenteditable : 'false', // this stops cell selection from picking the table.
29208 'data-block' : 'Table',
29211 border : 'solid 1px #000', // ??? hard coded?
29212 'border-collapse' : 'collapse'
29215 { tag : 'tbody' , cn : [] }
29219 // do we have a head = not really
29221 Roo.each(this.rows, function( row ) {
29226 border : 'solid 1px #000',
29232 ret.cn[0].cn.push(tr);
29233 // does the row have any properties? ?? height?
29235 Roo.each(row, function( cell ) {
29239 contenteditable : 'true',
29240 'data-block' : 'Td',
29244 if (cell.colspan > 1) {
29245 td.colspan = cell.colspan ;
29246 nc += cell.colspan;
29250 if (cell.rowspan > 1) {
29251 td.rowspan = cell.rowspan ;
29260 ncols = Math.max(nc, ncols);
29264 // add the header row..
29273 readElement : function(node)
29275 node = node ? node : this.node ;
29276 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29280 var trs = Array.from(node.rows);
29281 trs.forEach(function(tr) {
29283 this.rows.push(row);
29287 Array.from(tr.cells).forEach(function(td) {
29290 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29291 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29292 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29293 html : td.innerHTML
29295 no_column += add.colspan;
29302 this.no_col = Math.max(this.no_col, no_column);
29309 normalizeRows: function()
29313 this.rows.forEach(function(row) {
29316 row = this.normalizeRow(row);
29318 row.forEach(function(c) {
29319 while (typeof(ret[rid][cid]) != 'undefined') {
29322 if (typeof(ret[rid]) == 'undefined') {
29328 if (c.rowspan < 2) {
29332 for(var i = 1 ;i < c.rowspan; i++) {
29333 if (typeof(ret[rid+i]) == 'undefined') {
29336 ret[rid+i][cid] = c;
29344 normalizeRow: function(row)
29347 row.forEach(function(c) {
29348 if (c.colspan < 2) {
29352 for(var i =0 ;i < c.colspan; i++) {
29360 deleteColumn : function(sel)
29362 if (!sel || sel.type != 'col') {
29365 if (this.no_col < 2) {
29369 this.rows.forEach(function(row) {
29370 var cols = this.normalizeRow(row);
29371 var col = cols[sel.col];
29372 if (col.colspan > 1) {
29382 removeColumn : function()
29384 this.deleteColumn({
29386 col : this.no_col-1
29388 this.updateElement();
29392 addColumn : function()
29395 this.rows.forEach(function(row) {
29396 row.push(this.emptyCell());
29399 this.updateElement();
29402 deleteRow : function(sel)
29404 if (!sel || sel.type != 'row') {
29408 if (this.no_row < 2) {
29412 var rows = this.normalizeRows();
29415 rows[sel.row].forEach(function(col) {
29416 if (col.rowspan > 1) {
29419 col.remove = 1; // flage it as removed.
29424 this.rows.forEach(function(row) {
29426 row.forEach(function(c) {
29427 if (typeof(c.remove) == 'undefined') {
29432 if (newrow.length > 0) {
29436 this.rows = newrows;
29441 this.updateElement();
29444 removeRow : function()
29448 row : this.no_row-1
29454 addRow : function()
29458 for (var i = 0; i < this.no_col; i++ ) {
29460 row.push(this.emptyCell());
29463 this.rows.push(row);
29464 this.updateElement();
29468 // the default cell object... at present...
29469 emptyCell : function() {
29470 return (new Roo.htmleditor.BlockTd({})).toObject();
29475 removeNode : function()
29482 resetWidths : function()
29484 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
29485 var nn = Roo.htmleditor.Block.factory(n);
29487 nn.updateElement(n);
29500 * since selections really work on the table cell, then editing really should work from there
29502 * The original plan was to support merging etc... - but that may not be needed yet..
29504 * So this simple version will support:
29506 * adjust the width +/-
29507 * reset the width...
29516 * @class Roo.htmleditor.BlockTable
29517 * Block that manages a table
29520 * Create a new Filter.
29521 * @param {Object} config Configuration options
29524 Roo.htmleditor.BlockTd = function(cfg)
29527 this.readElement(cfg.node);
29528 this.updateElement(cfg.node);
29530 Roo.apply(this, cfg);
29535 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
29540 textAlign : 'left',
29547 // used by context menu
29548 friendly_name : 'Table Cell',
29549 deleteTitle : false, // use our customer delete
29551 // context menu is drawn once..
29553 contextMenu : function(toolbar)
29556 var cell = function() {
29557 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29560 var table = function() {
29561 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
29565 var saveSel = function()
29567 lr = toolbar.editorcore.getSelection().getRangeAt(0);
29569 var restoreSel = function()
29573 toolbar.editorcore.focus();
29574 var cr = toolbar.editorcore.getSelection();
29575 cr.removeAllRanges();
29577 toolbar.editorcore.onEditorEvent();
29578 }).defer(10, this);
29584 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29586 var syncValue = toolbar.editorcore.syncValue;
29593 text : 'Edit Table',
29595 click : function() {
29596 var t = toolbar.tb.selectedNode.closest('table');
29597 toolbar.editorcore.selectNode(t);
29598 toolbar.editorcore.onEditorEvent();
29607 xtype : 'TextItem',
29608 text : "Column Width: ",
29609 xns : rooui.Toolbar
29616 click : function (_self, e)
29618 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29619 cell().shrinkColumn();
29621 toolbar.editorcore.onEditorEvent();
29624 xns : rooui.Toolbar
29630 click : function (_self, e)
29632 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29633 cell().growColumn();
29635 toolbar.editorcore.onEditorEvent();
29638 xns : rooui.Toolbar
29642 xtype : 'TextItem',
29643 text : "Vertical Align: ",
29644 xns : rooui.Toolbar //Boostrap?
29647 xtype : 'ComboBox',
29648 allowBlank : false,
29649 displayField : 'val',
29652 triggerAction : 'all',
29654 valueField : 'val',
29658 select : function (combo, r, index)
29660 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29662 b.valign = r.get('val');
29665 toolbar.editorcore.onEditorEvent();
29670 xtype : 'SimpleStore',
29674 ['bottom'] // there are afew more...
29682 xtype : 'TextItem',
29683 text : "Merge Cells: ",
29684 xns : rooui.Toolbar
29693 click : function (_self, e)
29695 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29696 cell().mergeRight();
29697 //block().growColumn();
29699 toolbar.editorcore.onEditorEvent();
29702 xns : rooui.Toolbar
29709 click : function (_self, e)
29711 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29712 cell().mergeBelow();
29713 //block().growColumn();
29715 toolbar.editorcore.onEditorEvent();
29718 xns : rooui.Toolbar
29721 xtype : 'TextItem',
29723 xns : rooui.Toolbar
29731 click : function (_self, e)
29733 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29736 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29737 toolbar.editorcore.onEditorEvent();
29741 xns : rooui.Toolbar
29745 xns : rooui.Toolbar
29754 xns : rooui.Toolbar,
29763 click : function (_self, e)
29767 cell().deleteColumn();
29769 toolbar.editorcore.selectNode(t.node);
29770 toolbar.editorcore.onEditorEvent();
29779 click : function (_self, e)
29782 cell().deleteRow();
29785 toolbar.editorcore.selectNode(t.node);
29786 toolbar.editorcore.onEditorEvent();
29793 xtype : 'Separator',
29800 click : function (_self, e)
29803 var nn = t.node.nextSibling || t.node.previousSibling;
29804 t.node.parentNode.removeChild(t.node);
29806 toolbar.editorcore.selectNode(nn, true);
29808 toolbar.editorcore.onEditorEvent();
29818 // align... << fixme
29826 * create a DomHelper friendly object - for use with
29827 * Roo.DomHelper.markup / overwrite / etc..
29828 * ?? should it be called with option to hide all editing features?
29831 * create a DomHelper friendly object - for use with
29832 * Roo.DomHelper.markup / overwrite / etc..
29833 * ?? should it be called with option to hide all editing features?
29835 toObject : function()
29839 contenteditable : 'true', // this stops cell selection from picking the table.
29840 'data-block' : 'Td',
29841 valign : this.valign,
29843 'text-align' : this.textAlign,
29844 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
29845 'border-collapse' : 'collapse',
29846 padding : '6px', // 8 for desktop / 4 for mobile
29847 'vertical-align': this.valign
29851 if (this.width != '') {
29852 ret.width = this.width;
29853 ret.style.width = this.width;
29857 if (this.colspan > 1) {
29858 ret.colspan = this.colspan ;
29860 if (this.rowspan > 1) {
29861 ret.rowspan = this.rowspan ;
29870 readElement : function(node)
29872 node = node ? node : this.node ;
29873 this.width = node.style.width;
29874 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
29875 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
29876 this.html = node.innerHTML;
29877 if (node.style.textAlign != '') {
29878 this.textAlign = node.style.textAlign;
29884 // the default cell object... at present...
29885 emptyCell : function() {
29889 textAlign : 'left',
29890 html : " " // is this going to be editable now?
29895 removeNode : function()
29897 return this.node.closest('table');
29905 toTableArray : function()
29908 var tab = this.node.closest('tr').closest('table');
29909 Array.from(tab.rows).forEach(function(r, ri){
29913 this.colWidths = [];
29914 var all_auto = true;
29915 Array.from(tab.rows).forEach(function(r, ri){
29918 Array.from(r.cells).forEach(function(ce, ci){
29923 colspan : ce.colSpan,
29924 rowspan : ce.rowSpan
29926 if (ce.isEqualNode(this.node)) {
29929 // if we have been filled up by a row?
29930 if (typeof(ret[rn][cn]) != 'undefined') {
29931 while(typeof(ret[rn][cn]) != 'undefined') {
29937 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
29938 this.colWidths[cn] = ce.style.width;
29939 if (this.colWidths[cn] != '') {
29945 if (c.colspan < 2 && c.rowspan < 2 ) {
29950 for(var j = 0; j < c.rowspan; j++) {
29951 if (typeof(ret[rn+j]) == 'undefined') {
29952 continue; // we have a problem..
29955 for(var i = 0; i < c.colspan; i++) {
29956 ret[rn+j][cn+i] = c;
29965 // initalize widths.?
29966 // either all widths or no widths..
29968 this.colWidths[0] = false; // no widths flag.
29979 mergeRight: function()
29982 // get the contents of the next cell along..
29983 var tr = this.node.closest('tr');
29984 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
29985 if (i >= tr.childNodes.length - 1) {
29986 return; // no cells on right to merge with.
29988 var table = this.toTableArray();
29990 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
29991 return; // nothing right?
29993 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
29994 // right cell - must be same rowspan and on the same row.
29995 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
29996 return; // right hand side is not same rowspan.
30001 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30002 tr.removeChild(rc.cell);
30003 this.colspan += rc.colspan;
30004 this.node.setAttribute('colspan', this.colspan);
30006 var table = this.toTableArray();
30007 this.normalizeWidths(table);
30008 this.updateWidths(table);
30012 mergeBelow : function()
30014 var table = this.toTableArray();
30015 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30016 return; // no row below
30018 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30019 return; // nothing right?
30021 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30023 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30024 return; // right hand side is not same rowspan.
30026 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30027 rc.cell.parentNode.removeChild(rc.cell);
30028 this.rowspan += rc.rowspan;
30029 this.node.setAttribute('rowspan', this.rowspan);
30034 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30037 var table = this.toTableArray();
30038 var cd = this.cellData;
30042 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30045 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30046 if (r == cd.row && c == cd.col) {
30047 this.node.removeAttribute('rowspan');
30048 this.node.removeAttribute('colspan');
30051 var ntd = this.node.cloneNode(); // which col/row should be 0..
30052 ntd.removeAttribute('id');
30053 ntd.style.width = this.colWidths[c];
30054 ntd.innerHTML = '';
30055 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30059 this.redrawAllCells(table);
30065 redrawAllCells: function(table)
30069 var tab = this.node.closest('tr').closest('table');
30070 var ctr = tab.rows[0].parentNode;
30071 Array.from(tab.rows).forEach(function(r, ri){
30073 Array.from(r.cells).forEach(function(ce, ci){
30074 ce.parentNode.removeChild(ce);
30076 r.parentNode.removeChild(r);
30078 for(var r = 0 ; r < table.length; r++) {
30079 var re = tab.rows[r];
30081 var re = tab.ownerDocument.createElement('tr');
30082 ctr.appendChild(re);
30083 for(var c = 0 ; c < table[r].length; c++) {
30084 if (table[r][c].cell === false) {
30088 re.appendChild(table[r][c].cell);
30090 table[r][c].cell = false;
30095 updateWidths : function(table)
30097 for(var r = 0 ; r < table.length; r++) {
30099 for(var c = 0 ; c < table[r].length; c++) {
30100 if (table[r][c].cell === false) {
30104 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30105 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30106 el.width = Math.floor(this.colWidths[c]) +'%';
30107 el.updateElement(el.node);
30109 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30110 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30112 for(var i = 0; i < table[r][c].colspan; i ++) {
30113 width += Math.floor(this.colWidths[c + i]);
30115 el.width = width +'%';
30116 el.updateElement(el.node);
30118 table[r][c].cell = false; // done
30122 normalizeWidths : function(table)
30124 if (this.colWidths[0] === false) {
30125 var nw = 100.0 / this.colWidths.length;
30126 this.colWidths.forEach(function(w,i) {
30127 this.colWidths[i] = nw;
30132 var t = 0, missing = [];
30134 this.colWidths.forEach(function(w,i) {
30136 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30137 var add = this.colWidths[i];
30146 var nc = this.colWidths.length;
30147 if (missing.length) {
30148 var mult = (nc - missing.length) / (1.0 * nc);
30150 var ew = (100 -t) / (1.0 * missing.length);
30151 this.colWidths.forEach(function(w,i) {
30153 this.colWidths[i] = w * mult;
30157 this.colWidths[i] = ew;
30159 // have to make up numbers..
30162 // now we should have all the widths..
30167 shrinkColumn : function()
30169 var table = this.toTableArray();
30170 this.normalizeWidths(table);
30171 var col = this.cellData.col;
30172 var nw = this.colWidths[col] * 0.8;
30176 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30177 this.colWidths.forEach(function(w,i) {
30179 this.colWidths[i] = nw;
30182 this.colWidths[i] += otherAdd
30184 this.updateWidths(table);
30187 growColumn : function()
30189 var table = this.toTableArray();
30190 this.normalizeWidths(table);
30191 var col = this.cellData.col;
30192 var nw = this.colWidths[col] * 1.2;
30196 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30197 this.colWidths.forEach(function(w,i) {
30199 this.colWidths[i] = nw;
30202 this.colWidths[i] -= otherSub
30204 this.updateWidths(table);
30207 deleteRow : function()
30209 // delete this rows 'tr'
30210 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30211 // then reduce the rowspan.
30212 var table = this.toTableArray();
30213 // this.cellData.row;
30214 for (var i =0;i< table[this.cellData.row].length ; i++) {
30215 var c = table[this.cellData.row][i];
30216 if (c.row != this.cellData.row) {
30219 c.cell.setAttribute('rowspan', c.rowspan);
30222 if (c.rowspan > 1) {
30224 c.cell.setAttribute('rowspan', c.rowspan);
30227 table.splice(this.cellData.row,1);
30228 this.redrawAllCells(table);
30231 deleteColumn : function()
30233 var table = this.toTableArray();
30235 for (var i =0;i< table.length ; i++) {
30236 var c = table[i][this.cellData.col];
30237 if (c.col != this.cellData.col) {
30238 table[i][this.cellData.col].colspan--;
30239 } else if (c.colspan > 1) {
30241 c.cell.setAttribute('colspan', c.colspan);
30243 table[i].splice(this.cellData.col,1);
30246 this.redrawAllCells(table);
30254 //<script type="text/javascript">
30257 * Based Ext JS Library 1.1.1
30258 * Copyright(c) 2006-2007, Ext JS, LLC.
30264 * @class Roo.HtmlEditorCore
30265 * @extends Roo.Component
30266 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30268 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30271 Roo.HtmlEditorCore = function(config){
30274 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30279 * @event initialize
30280 * Fires when the editor is fully initialized (including the iframe)
30281 * @param {Roo.HtmlEditorCore} this
30286 * Fires when the editor is first receives the focus. Any insertion must wait
30287 * until after this event.
30288 * @param {Roo.HtmlEditorCore} this
30292 * @event beforesync
30293 * Fires before the textarea is updated with content from the editor iframe. Return false
30294 * to cancel the sync.
30295 * @param {Roo.HtmlEditorCore} this
30296 * @param {String} html
30300 * @event beforepush
30301 * Fires before the iframe editor is updated with content from the textarea. Return false
30302 * to cancel the push.
30303 * @param {Roo.HtmlEditorCore} this
30304 * @param {String} html
30309 * Fires when the textarea is updated with content from the editor iframe.
30310 * @param {Roo.HtmlEditorCore} this
30311 * @param {String} html
30316 * Fires when the iframe editor is updated with content from the textarea.
30317 * @param {Roo.HtmlEditorCore} this
30318 * @param {String} html
30323 * @event editorevent
30324 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30325 * @param {Roo.HtmlEditorCore} this
30332 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30334 // defaults : white / black...
30335 this.applyBlacklists();
30342 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30346 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30352 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
30357 * @cfg {Number} height (in pixels)
30361 * @cfg {Number} width (in pixels)
30365 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30366 * if you are doing an email editor, this probably needs disabling, it's designed
30371 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30373 enableBlocks : true,
30375 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30378 stylesheets: false,
30380 * @cfg {String} language default en - language of text (usefull for rtl languages)
30386 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30387 * - by default they are stripped - if you are editing email you may need this.
30389 allowComments: false,
30393 // private properties
30394 validationEvent : false,
30396 initialized : false,
30398 sourceEditMode : false,
30399 onFocus : Roo.emptyFn,
30401 hideMode:'offsets',
30405 // blacklist + whitelisted elements..
30412 undoManager : false,
30414 * Protected method that will not generally be called directly. It
30415 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30416 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30418 getDocMarkup : function(){
30422 // inherit styels from page...??
30423 if (this.stylesheets === false) {
30425 Roo.get(document.head).select('style').each(function(node) {
30426 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30429 Roo.get(document.head).select('link').each(function(node) {
30430 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30433 } else if (!this.stylesheets.length) {
30435 st = '<style type="text/css">' +
30436 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30439 for (var i in this.stylesheets) {
30440 if (typeof(this.stylesheets[i]) != 'string') {
30443 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30448 st += '<style type="text/css">' +
30449 'IMG { cursor: pointer } ' +
30452 st += '<meta name="google" content="notranslate">';
30454 var cls = 'notranslate roo-htmleditor-body';
30456 if(this.bodyCls.length){
30457 cls += ' ' + this.bodyCls;
30460 return '<html class="notranslate" translate="no"><head>' + st +
30461 //<style type="text/css">' +
30462 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30464 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30468 onRender : function(ct, position)
30471 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30472 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30475 this.el.dom.style.border = '0 none';
30476 this.el.dom.setAttribute('tabIndex', -1);
30477 this.el.addClass('x-hidden hide');
30481 if(Roo.isIE){ // fix IE 1px bogus margin
30482 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
30486 this.frameId = Roo.id();
30490 var iframe = this.owner.wrap.createChild({
30492 cls: 'form-control', // bootstrap..
30494 name: this.frameId,
30495 frameBorder : 'no',
30496 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
30501 this.iframe = iframe.dom;
30503 this.assignDocWin();
30505 this.doc.designMode = 'on';
30508 this.doc.write(this.getDocMarkup());
30512 var task = { // must defer to wait for browser to be ready
30514 //console.log("run task?" + this.doc.readyState);
30515 this.assignDocWin();
30516 if(this.doc.body || this.doc.readyState == 'complete'){
30518 this.doc.designMode="on";
30523 Roo.TaskMgr.stop(task);
30524 this.initEditor.defer(10, this);
30531 Roo.TaskMgr.start(task);
30536 onResize : function(w, h)
30538 Roo.log('resize: ' +w + ',' + h );
30539 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
30543 if(typeof w == 'number'){
30545 this.iframe.style.width = w + 'px';
30547 if(typeof h == 'number'){
30549 this.iframe.style.height = h + 'px';
30551 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
30558 * Toggles the editor between standard and source edit mode.
30559 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30561 toggleSourceEdit : function(sourceEditMode){
30563 this.sourceEditMode = sourceEditMode === true;
30565 if(this.sourceEditMode){
30567 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
30570 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
30571 //this.iframe.className = '';
30574 //this.setSize(this.owner.wrap.getSize());
30575 //this.fireEvent('editmodechange', this, this.sourceEditMode);
30582 * Protected method that will not generally be called directly. If you need/want
30583 * custom HTML cleanup, this is the method you should override.
30584 * @param {String} html The HTML to be cleaned
30585 * return {String} The cleaned HTML
30587 cleanHtml : function(html)
30589 html = String(html);
30590 if(html.length > 5){
30591 if(Roo.isSafari){ // strip safari nonsense
30592 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
30595 if(html == ' '){
30602 * HTML Editor -> Textarea
30603 * Protected method that will not generally be called directly. Syncs the contents
30604 * of the editor iframe with the textarea.
30606 syncValue : function()
30608 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
30609 if(this.initialized){
30611 if (this.undoManager) {
30612 this.undoManager.addEvent();
30616 var bd = (this.doc.body || this.doc.documentElement);
30619 var sel = this.win.getSelection();
30621 var div = document.createElement('div');
30622 div.innerHTML = bd.innerHTML;
30623 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
30624 if (gtx.length > 0) {
30625 var rm = gtx.item(0).parentNode;
30626 rm.parentNode.removeChild(rm);
30630 if (this.enableBlocks) {
30631 new Roo.htmleditor.FilterBlock({ node : div });
30634 var html = div.innerHTML;
30637 if (this.autoClean) {
30639 new Roo.htmleditor.FilterAttributes({
30660 attrib_clean : ['href', 'src' ]
30663 var tidy = new Roo.htmleditor.TidySerializer({
30666 html = tidy.serialize(div);
30672 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
30673 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
30675 html = '<div style="'+m[0]+'">' + html + '</div>';
30678 html = this.cleanHtml(html);
30679 // fix up the special chars.. normaly like back quotes in word...
30680 // however we do not want to do this with chinese..
30681 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
30683 var cc = match.charCodeAt();
30685 // Get the character value, handling surrogate pairs
30686 if (match.length == 2) {
30687 // It's a surrogate pair, calculate the Unicode code point
30688 var high = match.charCodeAt(0) - 0xD800;
30689 var low = match.charCodeAt(1) - 0xDC00;
30690 cc = (high * 0x400) + low + 0x10000;
30692 (cc >= 0x4E00 && cc < 0xA000 ) ||
30693 (cc >= 0x3400 && cc < 0x4E00 ) ||
30694 (cc >= 0xf900 && cc < 0xfb00 )
30699 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
30700 return "&#" + cc + ";";
30707 if(this.owner.fireEvent('beforesync', this, html) !== false){
30708 this.el.dom.value = html;
30709 this.owner.fireEvent('sync', this, html);
30715 * TEXTAREA -> EDITABLE
30716 * Protected method that will not generally be called directly. Pushes the value of the textarea
30717 * into the iframe editor.
30719 pushValue : function()
30721 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
30722 if(this.initialized){
30723 var v = this.el.dom.value.trim();
30726 if(this.owner.fireEvent('beforepush', this, v) !== false){
30727 var d = (this.doc.body || this.doc.documentElement);
30730 this.el.dom.value = d.innerHTML;
30731 this.owner.fireEvent('push', this, v);
30733 if (this.autoClean) {
30734 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
30735 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
30737 if (this.enableBlocks) {
30738 Roo.htmleditor.Block.initAll(this.doc.body);
30741 this.updateLanguage();
30743 var lc = this.doc.body.lastChild;
30744 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
30745 // add an extra line at the end.
30746 this.doc.body.appendChild(this.doc.createElement('br'));
30754 deferFocus : function(){
30755 this.focus.defer(10, this);
30759 focus : function(){
30760 if(this.win && !this.sourceEditMode){
30767 assignDocWin: function()
30769 var iframe = this.iframe;
30772 this.doc = iframe.contentWindow.document;
30773 this.win = iframe.contentWindow;
30775 // if (!Roo.get(this.frameId)) {
30778 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30779 // this.win = Roo.get(this.frameId).dom.contentWindow;
30781 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
30785 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
30786 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
30791 initEditor : function(){
30792 //console.log("INIT EDITOR");
30793 this.assignDocWin();
30797 this.doc.designMode="on";
30799 this.doc.write(this.getDocMarkup());
30802 var dbody = (this.doc.body || this.doc.documentElement);
30803 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
30804 // this copies styles from the containing element into thsi one..
30805 // not sure why we need all of this..
30806 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
30808 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
30809 //ss['background-attachment'] = 'fixed'; // w3c
30810 dbody.bgProperties = 'fixed'; // ie
30811 dbody.setAttribute("translate", "no");
30813 //Roo.DomHelper.applyStyles(dbody, ss);
30814 Roo.EventManager.on(this.doc, {
30816 'mouseup': this.onEditorEvent,
30817 'dblclick': this.onEditorEvent,
30818 'click': this.onEditorEvent,
30819 'keyup': this.onEditorEvent,
30824 Roo.EventManager.on(this.doc, {
30825 'paste': this.onPasteEvent,
30829 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
30832 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
30833 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
30835 this.initialized = true;
30838 // initialize special key events - enter
30839 new Roo.htmleditor.KeyEnter({core : this});
30843 this.owner.fireEvent('initialize', this);
30846 // this is to prevent a href clicks resulting in a redirect?
30848 onPasteEvent : function(e,v)
30850 // I think we better assume paste is going to be a dirty load of rubish from word..
30852 // even pasting into a 'email version' of this widget will have to clean up that mess.
30853 var cd = (e.browserEvent.clipboardData || window.clipboardData);
30855 // check what type of paste - if it's an image, then handle it differently.
30856 if (cd.files && cd.files.length > 0) {
30858 var urlAPI = (window.createObjectURL && window) ||
30859 (window.URL && URL.revokeObjectURL && URL) ||
30860 (window.webkitURL && webkitURL);
30862 var url = urlAPI.createObjectURL( cd.files[0]);
30863 this.insertAtCursor('<img src=" + url + ">');
30866 if (cd.types.indexOf('text/html') < 0 ) {
30870 var html = cd.getData('text/html'); // clipboard event
30871 if (cd.types.indexOf('text/rtf') > -1) {
30872 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
30873 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
30878 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
30879 .map(function(g) { return g.toDataURL(); })
30880 .filter(function(g) { return g != 'about:blank'; });
30883 html = this.cleanWordChars(html);
30885 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
30888 var sn = this.getParentElement();
30889 // check if d contains a table, and prevent nesting??
30890 //Roo.log(d.getElementsByTagName('table'));
30892 //Roo.log(sn.closest('table'));
30893 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
30894 e.preventDefault();
30895 this.insertAtCursor("You can not nest tables");
30896 //Roo.log("prevent?"); // fixme -
30902 if (images.length > 0) {
30903 // replace all v:imagedata - with img.
30904 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
30905 Roo.each(ar, function(node) {
30906 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
30907 node.parentNode.removeChild(node);
30911 Roo.each(d.getElementsByTagName('img'), function(img, i) {
30912 img.setAttribute('src', images[i]);
30915 if (this.autoClean) {
30916 new Roo.htmleditor.FilterWord({ node : d });
30918 new Roo.htmleditor.FilterStyleToTag({ node : d });
30919 new Roo.htmleditor.FilterAttributes({
30921 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
30922 attrib_clean : ['href', 'src' ]
30924 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
30925 // should be fonts..
30926 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
30927 new Roo.htmleditor.FilterParagraph({ node : d });
30928 new Roo.htmleditor.FilterSpan({ node : d });
30929 new Roo.htmleditor.FilterLongBr({ node : d });
30930 new Roo.htmleditor.FilterComment({ node : d });
30934 if (this.enableBlocks) {
30936 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
30937 if (img.closest('figure')) { // assume!! that it's aready
30940 var fig = new Roo.htmleditor.BlockFigure({
30941 image_src : img.src
30943 fig.updateElement(img); // replace it..
30949 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
30950 if (this.enableBlocks) {
30951 Roo.htmleditor.Block.initAll(this.doc.body);
30955 e.preventDefault();
30957 // default behaveiour should be our local cleanup paste? (optional?)
30958 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
30959 //this.owner.fireEvent('paste', e, v);
30962 onDestroy : function(){
30968 //for (var i =0; i < this.toolbars.length;i++) {
30969 // // fixme - ask toolbars for heights?
30970 // this.toolbars[i].onDestroy();
30973 //this.wrap.dom.innerHTML = '';
30974 //this.wrap.remove();
30979 onFirstFocus : function(){
30981 this.assignDocWin();
30982 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
30984 this.activated = true;
30987 if(Roo.isGecko){ // prevent silly gecko errors
30989 var s = this.win.getSelection();
30990 if(!s.focusNode || s.focusNode.nodeType != 3){
30991 var r = s.getRangeAt(0);
30992 r.selectNodeContents((this.doc.body || this.doc.documentElement));
30997 this.execCmd('useCSS', true);
30998 this.execCmd('styleWithCSS', false);
31001 this.owner.fireEvent('activate', this);
31005 adjustFont: function(btn){
31006 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31007 //if(Roo.isSafari){ // safari
31010 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31011 if(Roo.isSafari){ // safari
31012 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31013 v = (v < 10) ? 10 : v;
31014 v = (v > 48) ? 48 : v;
31015 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31020 v = Math.max(1, v+adjust);
31022 this.execCmd('FontSize', v );
31025 onEditorEvent : function(e)
31029 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31030 return; // we do not handle this.. (undo manager does..)
31032 // in theory this detects if the last element is not a br, then we try and do that.
31033 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31035 e.target.nodeName == 'BODY' &&
31036 e.type == "mouseup" &&
31037 this.doc.body.lastChild
31039 var lc = this.doc.body.lastChild;
31040 // gtx-trans is google translate plugin adding crap.
31041 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31042 lc = lc.previousSibling;
31044 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31045 // if last element is <BR> - then dont do anything.
31047 var ns = this.doc.createElement('br');
31048 this.doc.body.appendChild(ns);
31049 range = this.doc.createRange();
31050 range.setStartAfter(ns);
31051 range.collapse(true);
31052 var sel = this.win.getSelection();
31053 sel.removeAllRanges();
31054 sel.addRange(range);
31060 this.fireEditorEvent(e);
31061 // this.updateToolbar();
31062 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31065 fireEditorEvent: function(e)
31067 this.owner.fireEvent('editorevent', this, e);
31070 insertTag : function(tg)
31072 // could be a bit smarter... -> wrap the current selected tRoo..
31073 if (tg.toLowerCase() == 'span' ||
31074 tg.toLowerCase() == 'code' ||
31075 tg.toLowerCase() == 'sup' ||
31076 tg.toLowerCase() == 'sub'
31079 range = this.createRange(this.getSelection());
31080 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31081 wrappingNode.appendChild(range.extractContents());
31082 range.insertNode(wrappingNode);
31089 this.execCmd("formatblock", tg);
31090 this.undoManager.addEvent();
31093 insertText : function(txt)
31097 var range = this.createRange();
31098 range.deleteContents();
31099 //alert(Sender.getAttribute('label'));
31101 range.insertNode(this.doc.createTextNode(txt));
31102 this.undoManager.addEvent();
31108 * Executes a Midas editor command on the editor document and performs necessary focus and
31109 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31110 * @param {String} cmd The Midas command
31111 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31113 relayCmd : function(cmd, value)
31117 case 'justifyleft':
31118 case 'justifyright':
31119 case 'justifycenter':
31120 // if we are in a cell, then we will adjust the
31121 var n = this.getParentElement();
31122 var td = n.closest('td');
31124 var bl = Roo.htmleditor.Block.factory(td);
31125 bl.textAlign = cmd.replace('justify','');
31126 bl.updateElement();
31127 this.owner.fireEvent('editorevent', this);
31130 this.execCmd('styleWithCSS', true); //
31134 // if there is no selection, then we insert, and set the curson inside it..
31135 this.execCmd('styleWithCSS', false);
31145 this.execCmd(cmd, value);
31146 this.owner.fireEvent('editorevent', this);
31147 //this.updateToolbar();
31148 this.owner.deferFocus();
31152 * Executes a Midas editor command directly on the editor document.
31153 * For visual commands, you should use {@link #relayCmd} instead.
31154 * <b>This should only be called after the editor is initialized.</b>
31155 * @param {String} cmd The Midas command
31156 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31158 execCmd : function(cmd, value){
31159 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31166 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31168 * @param {String} text | dom node..
31170 insertAtCursor : function(text)
31173 if(!this.activated){
31177 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31181 // from jquery ui (MIT licenced)
31183 var win = this.win;
31185 if (win.getSelection && win.getSelection().getRangeAt) {
31187 // delete the existing?
31189 this.createRange(this.getSelection()).deleteContents();
31190 range = win.getSelection().getRangeAt(0);
31191 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31192 range.insertNode(node);
31193 range = range.cloneRange();
31194 range.collapse(false);
31196 win.getSelection().removeAllRanges();
31197 win.getSelection().addRange(range);
31201 } else if (win.document.selection && win.document.selection.createRange) {
31202 // no firefox support
31203 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31204 win.document.selection.createRange().pasteHTML(txt);
31207 // no firefox support
31208 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31209 this.execCmd('InsertHTML', txt);
31217 mozKeyPress : function(e){
31219 var c = e.getCharCode(), cmd;
31222 c = String.fromCharCode(c).toLowerCase();
31236 // this.cleanUpPaste.defer(100, this);
31242 this.relayCmd(cmd);
31243 //this.win.focus();
31244 //this.execCmd(cmd);
31245 //this.deferFocus();
31246 e.preventDefault();
31254 fixKeys : function(){ // load time branching for fastest keydown performance
31258 return function(e){
31259 var k = e.getKey(), r;
31262 r = this.doc.selection.createRange();
31265 r.pasteHTML('    ');
31270 /// this is handled by Roo.htmleditor.KeyEnter
31273 r = this.doc.selection.createRange();
31275 var target = r.parentElement();
31276 if(!target || target.tagName.toLowerCase() != 'li'){
31278 r.pasteHTML('<br/>');
31285 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31286 // this.cleanUpPaste.defer(100, this);
31292 }else if(Roo.isOpera){
31293 return function(e){
31294 var k = e.getKey();
31298 this.execCmd('InsertHTML','    ');
31302 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31303 // this.cleanUpPaste.defer(100, this);
31308 }else if(Roo.isSafari){
31309 return function(e){
31310 var k = e.getKey();
31314 this.execCmd('InsertText','\t');
31318 this.mozKeyPress(e);
31320 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31321 // this.cleanUpPaste.defer(100, this);
31329 getAllAncestors: function()
31331 var p = this.getSelectedNode();
31334 a.push(p); // push blank onto stack..
31335 p = this.getParentElement();
31339 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31343 a.push(this.doc.body);
31347 lastSelNode : false,
31350 getSelection : function()
31352 this.assignDocWin();
31353 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31356 * Select a dom node
31357 * @param {DomElement} node the node to select
31359 selectNode : function(node, collapse)
31361 var nodeRange = node.ownerDocument.createRange();
31363 nodeRange.selectNode(node);
31365 nodeRange.selectNodeContents(node);
31367 if (collapse === true) {
31368 nodeRange.collapse(true);
31371 var s = this.win.getSelection();
31372 s.removeAllRanges();
31373 s.addRange(nodeRange);
31376 getSelectedNode: function()
31378 // this may only work on Gecko!!!
31380 // should we cache this!!!!
31384 var range = this.createRange(this.getSelection()).cloneRange();
31387 var parent = range.parentElement();
31389 var testRange = range.duplicate();
31390 testRange.moveToElementText(parent);
31391 if (testRange.inRange(range)) {
31394 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31397 parent = parent.parentElement;
31402 // is ancestor a text element.
31403 var ac = range.commonAncestorContainer;
31404 if (ac.nodeType == 3) {
31405 ac = ac.parentNode;
31408 var ar = ac.childNodes;
31411 var other_nodes = [];
31412 var has_other_nodes = false;
31413 for (var i=0;i<ar.length;i++) {
31414 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31417 // fullly contained node.
31419 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31424 // probably selected..
31425 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31426 other_nodes.push(ar[i]);
31430 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31435 has_other_nodes = true;
31437 if (!nodes.length && other_nodes.length) {
31438 nodes= other_nodes;
31440 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31448 createRange: function(sel)
31450 // this has strange effects when using with
31451 // top toolbar - not sure if it's a great idea.
31452 //this.editor.contentWindow.focus();
31453 if (typeof sel != "undefined") {
31455 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
31457 return this.doc.createRange();
31460 return this.doc.createRange();
31463 getParentElement: function()
31466 this.assignDocWin();
31467 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
31469 var range = this.createRange(sel);
31472 var p = range.commonAncestorContainer;
31473 while (p.nodeType == 3) { // text node
31484 * Range intersection.. the hard stuff...
31488 * [ -- selected range --- ]
31492 * if end is before start or hits it. fail.
31493 * if start is after end or hits it fail.
31495 * if either hits (but other is outside. - then it's not
31501 // @see http://www.thismuchiknow.co.uk/?p=64.
31502 rangeIntersectsNode : function(range, node)
31504 var nodeRange = node.ownerDocument.createRange();
31506 nodeRange.selectNode(node);
31508 nodeRange.selectNodeContents(node);
31511 var rangeStartRange = range.cloneRange();
31512 rangeStartRange.collapse(true);
31514 var rangeEndRange = range.cloneRange();
31515 rangeEndRange.collapse(false);
31517 var nodeStartRange = nodeRange.cloneRange();
31518 nodeStartRange.collapse(true);
31520 var nodeEndRange = nodeRange.cloneRange();
31521 nodeEndRange.collapse(false);
31523 return rangeStartRange.compareBoundaryPoints(
31524 Range.START_TO_START, nodeEndRange) == -1 &&
31525 rangeEndRange.compareBoundaryPoints(
31526 Range.START_TO_START, nodeStartRange) == 1;
31530 rangeCompareNode : function(range, node)
31532 var nodeRange = node.ownerDocument.createRange();
31534 nodeRange.selectNode(node);
31536 nodeRange.selectNodeContents(node);
31540 range.collapse(true);
31542 nodeRange.collapse(true);
31544 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
31545 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
31547 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
31549 var nodeIsBefore = ss == 1;
31550 var nodeIsAfter = ee == -1;
31552 if (nodeIsBefore && nodeIsAfter) {
31555 if (!nodeIsBefore && nodeIsAfter) {
31556 return 1; //right trailed.
31559 if (nodeIsBefore && !nodeIsAfter) {
31560 return 2; // left trailed.
31566 cleanWordChars : function(input) {// change the chars to hex code
31569 [ 8211, "–" ],
31570 [ 8212, "—" ],
31578 var output = input;
31579 Roo.each(swapCodes, function(sw) {
31580 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
31582 output = output.replace(swapper, sw[1]);
31592 cleanUpChild : function (node)
31595 new Roo.htmleditor.FilterComment({node : node});
31596 new Roo.htmleditor.FilterAttributes({
31598 attrib_black : this.ablack,
31599 attrib_clean : this.aclean,
31600 style_white : this.cwhite,
31601 style_black : this.cblack
31603 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
31604 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
31610 * Clean up MS wordisms...
31611 * @deprecated - use filter directly
31613 cleanWord : function(node)
31615 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
31616 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
31623 * @deprecated - use filters
31625 cleanTableWidths : function(node)
31627 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
31634 applyBlacklists : function()
31636 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
31637 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
31639 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
31640 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
31641 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
31645 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
31646 if (b.indexOf(tag) > -1) {
31649 this.white.push(tag);
31653 Roo.each(w, function(tag) {
31654 if (b.indexOf(tag) > -1) {
31657 if (this.white.indexOf(tag) > -1) {
31660 this.white.push(tag);
31665 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
31666 if (w.indexOf(tag) > -1) {
31669 this.black.push(tag);
31673 Roo.each(b, function(tag) {
31674 if (w.indexOf(tag) > -1) {
31677 if (this.black.indexOf(tag) > -1) {
31680 this.black.push(tag);
31685 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
31686 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
31690 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
31691 if (b.indexOf(tag) > -1) {
31694 this.cwhite.push(tag);
31698 Roo.each(w, function(tag) {
31699 if (b.indexOf(tag) > -1) {
31702 if (this.cwhite.indexOf(tag) > -1) {
31705 this.cwhite.push(tag);
31710 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
31711 if (w.indexOf(tag) > -1) {
31714 this.cblack.push(tag);
31718 Roo.each(b, function(tag) {
31719 if (w.indexOf(tag) > -1) {
31722 if (this.cblack.indexOf(tag) > -1) {
31725 this.cblack.push(tag);
31730 setStylesheets : function(stylesheets)
31732 if(typeof(stylesheets) == 'string'){
31733 Roo.get(this.iframe.contentDocument.head).createChild({
31735 rel : 'stylesheet',
31744 Roo.each(stylesheets, function(s) {
31749 Roo.get(_this.iframe.contentDocument.head).createChild({
31751 rel : 'stylesheet',
31761 updateLanguage : function()
31763 if (!this.iframe || !this.iframe.contentDocument) {
31766 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
31770 removeStylesheets : function()
31774 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
31779 setStyle : function(style)
31781 Roo.get(this.iframe.contentDocument.head).createChild({
31790 // hide stuff that is not compatible
31804 * @event specialkey
31808 * @cfg {String} fieldClass @hide
31811 * @cfg {String} focusClass @hide
31814 * @cfg {String} autoCreate @hide
31817 * @cfg {String} inputType @hide
31820 * @cfg {String} invalidClass @hide
31823 * @cfg {String} invalidText @hide
31826 * @cfg {String} msgFx @hide
31829 * @cfg {String} validateOnBlur @hide
31833 Roo.HtmlEditorCore.white = [
31834 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
31836 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
31837 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
31838 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
31839 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
31840 'TABLE', 'UL', 'XMP',
31842 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
31845 'DIR', 'MENU', 'OL', 'UL', 'DL',
31851 Roo.HtmlEditorCore.black = [
31852 // 'embed', 'object', // enable - backend responsiblity to clean thiese
31854 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
31855 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
31856 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
31857 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
31858 //'FONT' // CLEAN LATER..
31859 'COLGROUP', 'COL' // messy tables.
31863 Roo.HtmlEditorCore.clean = [ // ?? needed???
31864 'SCRIPT', 'STYLE', 'TITLE', 'XML'
31866 Roo.HtmlEditorCore.tag_remove = [
31871 Roo.HtmlEditorCore.ablack = [
31875 Roo.HtmlEditorCore.aclean = [
31876 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
31880 Roo.HtmlEditorCore.pwhite= [
31881 'http', 'https', 'mailto'
31884 // white listed style attributes.
31885 Roo.HtmlEditorCore.cwhite= [
31886 // 'text-align', /// default is to allow most things..
31892 // black listed style attributes.
31893 Roo.HtmlEditorCore.cblack= [
31894 // 'font-size' -- this can be set by the project
31908 * @class Roo.bootstrap.form.HtmlEditor
31909 * @extends Roo.bootstrap.form.TextArea
31910 * Bootstrap HtmlEditor class
31913 * Create a new HtmlEditor
31914 * @param {Object} config The config object
31917 Roo.bootstrap.form.HtmlEditor = function(config){
31918 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
31919 if (!this.toolbars) {
31920 this.toolbars = [];
31923 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
31926 * @event initialize
31927 * Fires when the editor is fully initialized (including the iframe)
31928 * @param {HtmlEditor} this
31933 * Fires when the editor is first receives the focus. Any insertion must wait
31934 * until after this event.
31935 * @param {HtmlEditor} this
31939 * @event beforesync
31940 * Fires before the textarea is updated with content from the editor iframe. Return false
31941 * to cancel the sync.
31942 * @param {HtmlEditor} this
31943 * @param {String} html
31947 * @event beforepush
31948 * Fires before the iframe editor is updated with content from the textarea. Return false
31949 * to cancel the push.
31950 * @param {HtmlEditor} this
31951 * @param {String} html
31956 * Fires when the textarea is updated with content from the editor iframe.
31957 * @param {HtmlEditor} this
31958 * @param {String} html
31963 * Fires when the iframe editor is updated with content from the textarea.
31964 * @param {HtmlEditor} this
31965 * @param {String} html
31969 * @event editmodechange
31970 * Fires when the editor switches edit modes
31971 * @param {HtmlEditor} this
31972 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
31974 editmodechange: true,
31976 * @event editorevent
31977 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
31978 * @param {HtmlEditor} this
31982 * @event firstfocus
31983 * Fires when on first focus - needed by toolbars..
31984 * @param {HtmlEditor} this
31989 * Auto save the htmlEditor value as a file into Events
31990 * @param {HtmlEditor} this
31994 * @event savedpreview
31995 * preview the saved version of htmlEditor
31996 * @param {HtmlEditor} this
32003 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32007 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32012 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32017 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
32022 * @cfg {Number} height (in pixels)
32026 * @cfg {Number} width (in pixels)
32031 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32034 stylesheets: false,
32039 // private properties
32040 validationEvent : false,
32042 initialized : false,
32045 onFocus : Roo.emptyFn,
32047 hideMode:'offsets',
32049 tbContainer : false,
32053 toolbarContainer :function() {
32054 return this.wrap.select('.x-html-editor-tb',true).first();
32058 * Protected method that will not generally be called directly. It
32059 * is called when the editor creates its toolbar. Override this method if you need to
32060 * add custom toolbar buttons.
32061 * @param {HtmlEditor} editor
32063 createToolbar : function(){
32064 Roo.log('renewing');
32065 Roo.log("create toolbars");
32067 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32068 this.toolbars[0].render(this.toolbarContainer());
32072 // if (!editor.toolbars || !editor.toolbars.length) {
32073 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32076 // for (var i =0 ; i < editor.toolbars.length;i++) {
32077 // editor.toolbars[i] = Roo.factory(
32078 // typeof(editor.toolbars[i]) == 'string' ?
32079 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
32080 // Roo.bootstrap.form.HtmlEditor);
32081 // editor.toolbars[i].init(editor);
32087 onRender : function(ct, position)
32089 // Roo.log("Call onRender: " + this.xtype);
32091 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32093 this.wrap = this.inputEl().wrap({
32094 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32097 this.editorcore.onRender(ct, position);
32099 if (this.resizable) {
32100 this.resizeEl = new Roo.Resizable(this.wrap, {
32104 minHeight : this.height,
32105 height: this.height,
32106 handles : this.resizable,
32109 resize : function(r, w, h) {
32110 _t.onResize(w,h); // -something
32116 this.createToolbar(this);
32119 if(!this.width && this.resizable){
32120 this.setSize(this.wrap.getSize());
32122 if (this.resizeEl) {
32123 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
32124 // should trigger onReize..
32130 onResize : function(w, h)
32132 Roo.log('resize: ' +w + ',' + h );
32133 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32137 if(this.inputEl() ){
32138 if(typeof w == 'number'){
32139 var aw = w - this.wrap.getFrameWidth('lr');
32140 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32143 if(typeof h == 'number'){
32144 var tbh = -11; // fixme it needs to tool bar size!
32145 for (var i =0; i < this.toolbars.length;i++) {
32146 // fixme - ask toolbars for heights?
32147 tbh += this.toolbars[i].el.getHeight();
32148 //if (this.toolbars[i].footer) {
32149 // tbh += this.toolbars[i].footer.el.getHeight();
32157 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32158 ah -= 5; // knock a few pixes off for look..
32159 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32163 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32164 this.editorcore.onResize(ew,eh);
32169 * Toggles the editor between standard and source edit mode.
32170 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32172 toggleSourceEdit : function(sourceEditMode)
32174 this.editorcore.toggleSourceEdit(sourceEditMode);
32176 if(this.editorcore.sourceEditMode){
32177 Roo.log('editor - showing textarea');
32180 // Roo.log(this.syncValue());
32182 this.inputEl().removeClass(['hide', 'x-hidden']);
32183 this.inputEl().dom.removeAttribute('tabIndex');
32184 this.inputEl().focus();
32186 Roo.log('editor - hiding textarea');
32188 // Roo.log(this.pushValue());
32191 this.inputEl().addClass(['hide', 'x-hidden']);
32192 this.inputEl().dom.setAttribute('tabIndex', -1);
32193 //this.deferFocus();
32196 if(this.resizable){
32197 this.setSize(this.wrap.getSize());
32200 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32203 // private (for BoxComponent)
32204 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32206 // private (for BoxComponent)
32207 getResizeEl : function(){
32211 // private (for BoxComponent)
32212 getPositionEl : function(){
32217 initEvents : function(){
32218 this.originalValue = this.getValue();
32222 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32225 // markInvalid : Roo.emptyFn,
32227 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32230 // clearInvalid : Roo.emptyFn,
32232 setValue : function(v){
32233 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32234 this.editorcore.pushValue();
32239 deferFocus : function(){
32240 this.focus.defer(10, this);
32244 focus : function(){
32245 this.editorcore.focus();
32251 onDestroy : function(){
32257 for (var i =0; i < this.toolbars.length;i++) {
32258 // fixme - ask toolbars for heights?
32259 this.toolbars[i].onDestroy();
32262 this.wrap.dom.innerHTML = '';
32263 this.wrap.remove();
32268 onFirstFocus : function(){
32269 //Roo.log("onFirstFocus");
32270 this.editorcore.onFirstFocus();
32271 for (var i =0; i < this.toolbars.length;i++) {
32272 this.toolbars[i].onFirstFocus();
32278 syncValue : function()
32280 this.editorcore.syncValue();
32283 pushValue : function()
32285 this.editorcore.pushValue();
32289 // hide stuff that is not compatible
32303 * @event specialkey
32307 * @cfg {String} fieldClass @hide
32310 * @cfg {String} focusClass @hide
32313 * @cfg {String} autoCreate @hide
32316 * @cfg {String} inputType @hide
32320 * @cfg {String} invalidText @hide
32323 * @cfg {String} msgFx @hide
32326 * @cfg {String} validateOnBlur @hide
32335 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32337 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32338 * @parent Roo.bootstrap.form.HtmlEditor
32339 * @extends Roo.bootstrap.nav.Simplebar
32345 new Roo.bootstrap.form.HtmlEditor({
32348 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32349 disable : { fonts: 1 , format: 1, ..., ... , ...],
32355 * @cfg {Object} disable List of elements to disable..
32356 * @cfg {Array} btns List of additional buttons.
32360 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32363 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32366 Roo.apply(this, config);
32368 // default disabled, based on 'good practice'..
32369 this.disable = this.disable || {};
32370 Roo.applyIf(this.disable, {
32373 specialElements : true
32375 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32377 this.editor = config.editor;
32378 this.editorcore = config.editor.editorcore;
32380 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32382 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32383 // dont call parent... till later.
32385 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
32390 editorcore : false,
32395 "h1","h2","h3","h4","h5","h6",
32397 "abbr", "acronym", "address", "cite", "samp", "var",
32401 onRender : function(ct, position)
32403 // Roo.log("Call onRender: " + this.xtype);
32405 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32407 this.el.dom.style.marginBottom = '0';
32409 var editorcore = this.editorcore;
32410 var editor= this.editor;
32413 var btn = function(id,cmd , toggle, handler, html){
32415 var event = toggle ? 'toggle' : 'click';
32420 xns: Roo.bootstrap,
32424 enableToggle:toggle !== false,
32426 pressed : toggle ? false : null,
32429 a.listeners[toggle ? 'toggle' : 'click'] = function() {
32430 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
32436 // var cb_box = function...
32441 xns: Roo.bootstrap,
32446 xns: Roo.bootstrap,
32450 Roo.each(this.formats, function(f) {
32451 style.menu.items.push({
32453 xns: Roo.bootstrap,
32454 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32459 editorcore.insertTag(this.tagname);
32466 children.push(style);
32468 btn('bold',false,true);
32469 btn('italic',false,true);
32470 btn('align-left', 'justifyleft',true);
32471 btn('align-center', 'justifycenter',true);
32472 btn('align-right' , 'justifyright',true);
32473 btn('link', false, false, function(btn) {
32474 //Roo.log("create link?");
32475 var url = prompt(this.createLinkText, this.defaultLinkValue);
32476 if(url && url != 'http:/'+'/'){
32477 this.editorcore.relayCmd('createlink', url);
32480 btn('list','insertunorderedlist',true);
32481 btn('pencil', false,true, function(btn){
32483 this.toggleSourceEdit(btn.pressed);
32486 if (this.editor.btns.length > 0) {
32487 for (var i = 0; i<this.editor.btns.length; i++) {
32488 children.push(this.editor.btns[i]);
32496 xns: Roo.bootstrap,
32501 xns: Roo.bootstrap,
32506 cog.menu.items.push({
32508 xns: Roo.bootstrap,
32509 html : Clean styles,
32514 editorcore.insertTag(this.tagname);
32523 this.xtype = 'NavSimplebar';
32525 for(var i=0;i< children.length;i++) {
32527 this.buttons.add(this.addxtypeChild(children[i]));
32531 editor.on('editorevent', this.updateToolbar, this);
32533 onBtnClick : function(id)
32535 this.editorcore.relayCmd(id);
32536 this.editorcore.focus();
32540 * Protected method that will not generally be called directly. It triggers
32541 * a toolbar update by reading the markup state of the current selection in the editor.
32543 updateToolbar: function(){
32545 if(!this.editorcore.activated){
32546 this.editor.onFirstFocus(); // is this neeed?
32550 var btns = this.buttons;
32551 var doc = this.editorcore.doc;
32552 btns.get('bold').setActive(doc.queryCommandState('bold'));
32553 btns.get('italic').setActive(doc.queryCommandState('italic'));
32554 //btns.get('underline').setActive(doc.queryCommandState('underline'));
32556 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
32557 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
32558 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
32560 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
32561 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
32564 var ans = this.editorcore.getAllAncestors();
32565 if (this.formatCombo) {
32568 var store = this.formatCombo.store;
32569 this.formatCombo.setValue("");
32570 for (var i =0; i < ans.length;i++) {
32571 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
32573 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
32581 // hides menus... - so this cant be on a menu...
32582 Roo.bootstrap.MenuMgr.hideAll();
32584 Roo.bootstrap.menu.Manager.hideAll();
32585 //this.editorsyncValue();
32587 onFirstFocus: function() {
32588 this.buttons.each(function(item){
32592 toggleSourceEdit : function(sourceEditMode){
32595 if(sourceEditMode){
32596 Roo.log("disabling buttons");
32597 this.buttons.each( function(item){
32598 if(item.cmd != 'pencil'){
32604 Roo.log("enabling buttons");
32605 if(this.editorcore.initialized){
32606 this.buttons.each( function(item){
32612 Roo.log("calling toggole on editor");
32613 // tell the editor that it's been pressed..
32614 this.editor.toggleSourceEdit(sourceEditMode);
32628 * @class Roo.bootstrap.form.Markdown
32629 * @extends Roo.bootstrap.form.TextArea
32630 * Bootstrap Showdown editable area
32631 * @cfg {string} content
32634 * Create a new Showdown
32637 Roo.bootstrap.form.Markdown = function(config){
32638 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
32642 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
32646 initEvents : function()
32649 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
32650 this.markdownEl = this.el.createChild({
32651 cls : 'roo-markdown-area'
32653 this.inputEl().addClass('d-none');
32654 if (this.getValue() == '') {
32655 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32658 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32660 this.markdownEl.on('click', this.toggleTextEdit, this);
32661 this.on('blur', this.toggleTextEdit, this);
32662 this.on('specialkey', this.resizeTextArea, this);
32665 toggleTextEdit : function()
32667 var sh = this.markdownEl.getHeight();
32668 this.inputEl().addClass('d-none');
32669 this.markdownEl.addClass('d-none');
32670 if (!this.editing) {
32672 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
32673 this.inputEl().removeClass('d-none');
32674 this.inputEl().focus();
32675 this.editing = true;
32678 // show showdown...
32679 this.updateMarkdown();
32680 this.markdownEl.removeClass('d-none');
32681 this.editing = false;
32684 updateMarkdown : function()
32686 if (this.getValue() == '') {
32687 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
32691 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
32694 resizeTextArea: function () {
32697 Roo.log([sh, this.getValue().split("\n").length * 30]);
32698 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
32700 setValue : function(val)
32702 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
32703 if (!this.editing) {
32704 this.updateMarkdown();
32710 if (!this.editing) {
32711 this.toggleTextEdit();
32719 * Ext JS Library 1.1.1
32720 * Copyright(c) 2006-2007, Ext JS, LLC.
32722 * Originally Released Under LGPL - original licence link has changed is not relivant.
32725 * <script type="text/javascript">
32729 * @class Roo.bootstrap.PagingToolbar
32730 * @extends Roo.bootstrap.nav.Simplebar
32731 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
32733 * Create a new PagingToolbar
32734 * @param {Object} config The config object
32735 * @param {Roo.data.Store} store
32737 Roo.bootstrap.PagingToolbar = function(config)
32739 // old args format still supported... - xtype is prefered..
32740 // created from xtype...
32742 this.ds = config.dataSource;
32744 if (config.store && !this.ds) {
32745 this.store= Roo.factory(config.store, Roo.data);
32746 this.ds = this.store;
32747 this.ds.xmodule = this.xmodule || false;
32750 this.toolbarItems = [];
32751 if (config.items) {
32752 this.toolbarItems = config.items;
32755 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
32760 this.bind(this.ds);
32763 if (Roo.bootstrap.version == 4) {
32764 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
32766 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
32771 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
32773 * @cfg {Roo.bootstrap.Button} buttons[]
32774 * Buttons for the toolbar
32777 * @cfg {Roo.data.Store} store
32778 * The underlying data store providing the paged data
32781 * @cfg {String/HTMLElement/Element} container
32782 * container The id or element that will contain the toolbar
32785 * @cfg {Boolean} displayInfo
32786 * True to display the displayMsg (defaults to false)
32789 * @cfg {Number} pageSize
32790 * The number of records to display per page (defaults to 20)
32794 * @cfg {String} displayMsg
32795 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
32797 displayMsg : 'Displaying {0} - {1} of {2}',
32799 * @cfg {String} emptyMsg
32800 * The message to display when no records are found (defaults to "No data to display")
32802 emptyMsg : 'No data to display',
32804 * Customizable piece of the default paging text (defaults to "Page")
32807 beforePageText : "Page",
32809 * Customizable piece of the default paging text (defaults to "of %0")
32812 afterPageText : "of {0}",
32814 * Customizable piece of the default paging text (defaults to "First Page")
32817 firstText : "First Page",
32819 * Customizable piece of the default paging text (defaults to "Previous Page")
32822 prevText : "Previous Page",
32824 * Customizable piece of the default paging text (defaults to "Next Page")
32827 nextText : "Next Page",
32829 * Customizable piece of the default paging text (defaults to "Last Page")
32832 lastText : "Last Page",
32834 * Customizable piece of the default paging text (defaults to "Refresh")
32837 refreshText : "Refresh",
32841 onRender : function(ct, position)
32843 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
32844 this.navgroup.parentId = this.id;
32845 this.navgroup.onRender(this.el, null);
32846 // add the buttons to the navgroup
32848 if(this.displayInfo){
32849 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
32850 this.displayEl = this.el.select('.x-paging-info', true).first();
32851 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
32852 // this.displayEl = navel.el.select('span',true).first();
32858 Roo.each(_this.buttons, function(e){ // this might need to use render????
32859 Roo.factory(e).render(_this.el);
32863 Roo.each(_this.toolbarItems, function(e) {
32864 _this.navgroup.addItem(e);
32868 this.first = this.navgroup.addItem({
32869 tooltip: this.firstText,
32870 cls: "prev btn-outline-secondary",
32871 html : ' <i class="fa fa-step-backward"></i>',
32873 preventDefault: true,
32874 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
32877 this.prev = this.navgroup.addItem({
32878 tooltip: this.prevText,
32879 cls: "prev btn-outline-secondary",
32880 html : ' <i class="fa fa-backward"></i>',
32882 preventDefault: true,
32883 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
32885 //this.addSeparator();
32888 var field = this.navgroup.addItem( {
32890 cls : 'x-paging-position btn-outline-secondary',
32892 html : this.beforePageText +
32893 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
32894 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
32897 this.field = field.el.select('input', true).first();
32898 this.field.on("keydown", this.onPagingKeydown, this);
32899 this.field.on("focus", function(){this.dom.select();});
32902 this.afterTextEl = field.el.select('.x-paging-after',true).first();
32903 //this.field.setHeight(18);
32904 //this.addSeparator();
32905 this.next = this.navgroup.addItem({
32906 tooltip: this.nextText,
32907 cls: "next btn-outline-secondary",
32908 html : ' <i class="fa fa-forward"></i>',
32910 preventDefault: true,
32911 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
32913 this.last = this.navgroup.addItem({
32914 tooltip: this.lastText,
32915 html : ' <i class="fa fa-step-forward"></i>',
32916 cls: "next btn-outline-secondary",
32918 preventDefault: true,
32919 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
32921 //this.addSeparator();
32922 this.loading = this.navgroup.addItem({
32923 tooltip: this.refreshText,
32924 cls: "btn-outline-secondary",
32925 html : ' <i class="fa fa-refresh"></i>',
32926 preventDefault: true,
32927 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
32933 updateInfo : function(){
32934 if(this.displayEl){
32935 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
32936 var msg = count == 0 ?
32940 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
32942 this.displayEl.update(msg);
32947 onLoad : function(ds, r, o)
32949 this.cursor = o.params && o.params.start ? o.params.start : 0;
32951 var d = this.getPageData(),
32956 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
32957 this.field.dom.value = ap;
32958 this.first.setDisabled(ap == 1);
32959 this.prev.setDisabled(ap == 1);
32960 this.next.setDisabled(ap == ps);
32961 this.last.setDisabled(ap == ps);
32962 this.loading.enable();
32967 getPageData : function(){
32968 var total = this.ds.getTotalCount();
32971 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
32972 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
32977 onLoadError : function(proxy, o){
32978 this.loading.enable();
32979 if (this.ds.events.loadexception.listeners.length < 2) {
32980 // nothing has been assigned to loadexception except this...
32982 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
32988 onPagingKeydown : function(e){
32989 var k = e.getKey();
32990 var d = this.getPageData();
32992 var v = this.field.dom.value, pageNum;
32993 if(!v || isNaN(pageNum = parseInt(v, 10))){
32994 this.field.dom.value = d.activePage;
32997 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
32998 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33001 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))
33003 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33004 this.field.dom.value = pageNum;
33005 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33008 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33010 var v = this.field.dom.value, pageNum;
33011 var increment = (e.shiftKey) ? 10 : 1;
33012 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33015 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33016 this.field.dom.value = d.activePage;
33019 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33021 this.field.dom.value = parseInt(v, 10) + increment;
33022 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33023 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33030 beforeLoad : function(){
33032 this.loading.disable();
33037 onClick : function(which){
33046 ds.load({params:{start: 0, limit: this.pageSize}});
33049 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33052 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33055 var total = ds.getTotalCount();
33056 var extra = total % this.pageSize;
33057 var lastStart = extra ? (total - extra) : total-this.pageSize;
33058 ds.load({params:{start: lastStart, limit: this.pageSize}});
33061 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33067 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33068 * @param {Roo.data.Store} store The data store to unbind
33070 unbind : function(ds){
33071 ds.un("beforeload", this.beforeLoad, this);
33072 ds.un("load", this.onLoad, this);
33073 ds.un("loadexception", this.onLoadError, this);
33074 ds.un("remove", this.updateInfo, this);
33075 ds.un("add", this.updateInfo, this);
33076 this.ds = undefined;
33080 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33081 * @param {Roo.data.Store} store The data store to bind
33083 bind : function(ds){
33084 ds.on("beforeload", this.beforeLoad, this);
33085 ds.on("load", this.onLoad, this);
33086 ds.on("loadexception", this.onLoadError, this);
33087 ds.on("remove", this.updateInfo, this);
33088 ds.on("add", this.updateInfo, this);
33099 * @class Roo.bootstrap.MessageBar
33100 * @extends Roo.bootstrap.Component
33101 * Bootstrap MessageBar class
33102 * @cfg {String} html contents of the MessageBar
33103 * @cfg {String} weight (info | success | warning | danger) default info
33104 * @cfg {String} beforeClass insert the bar before the given class
33105 * @cfg {Boolean} closable (true | false) default false
33106 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33109 * Create a new Element
33110 * @param {Object} config The config object
33113 Roo.bootstrap.MessageBar = function(config){
33114 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33117 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33123 beforeClass: 'bootstrap-sticky-wrap',
33125 getAutoCreate : function(){
33129 cls: 'alert alert-dismissable alert-' + this.weight,
33134 html: this.html || ''
33140 cfg.cls += ' alert-messages-fixed';
33154 onRender : function(ct, position)
33156 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33159 var cfg = Roo.apply({}, this.getAutoCreate());
33163 cfg.cls += ' ' + this.cls;
33166 cfg.style = this.style;
33168 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33170 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33173 this.el.select('>button.close').on('click', this.hide, this);
33179 if (!this.rendered) {
33185 this.fireEvent('show', this);
33191 if (!this.rendered) {
33197 this.fireEvent('hide', this);
33200 update : function()
33202 // var e = this.el.dom.firstChild;
33204 // if(this.closable){
33205 // e = e.nextSibling;
33208 // e.data = this.html || '';
33210 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33226 * @class Roo.bootstrap.Graph
33227 * @extends Roo.bootstrap.Component
33228 * Bootstrap Graph class
33232 @cfg {String} graphtype bar | vbar | pie
33233 @cfg {number} g_x coodinator | centre x (pie)
33234 @cfg {number} g_y coodinator | centre y (pie)
33235 @cfg {number} g_r radius (pie)
33236 @cfg {number} g_height height of the chart (respected by all elements in the set)
33237 @cfg {number} g_width width of the chart (respected by all elements in the set)
33238 @cfg {Object} title The title of the chart
33241 -opts (object) options for the chart
33243 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33244 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33246 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.
33247 o stacked (boolean) whether or not to tread values as in a stacked bar chart
33249 o stretch (boolean)
33251 -opts (object) options for the pie
33254 o startAngle (number)
33255 o endAngle (number)
33259 * Create a new Input
33260 * @param {Object} config The config object
33263 Roo.bootstrap.Graph = function(config){
33264 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33270 * The img click event for the img.
33271 * @param {Roo.EventObject} e
33277 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
33288 //g_colors: this.colors,
33295 getAutoCreate : function(){
33306 onRender : function(ct,position){
33309 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33311 if (typeof(Raphael) == 'undefined') {
33312 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33316 this.raphael = Raphael(this.el.dom);
33318 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33319 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33320 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33321 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33323 r.text(160, 10, "Single Series Chart").attr(txtattr);
33324 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33325 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33326 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33328 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33329 r.barchart(330, 10, 300, 220, data1);
33330 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33331 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33334 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33335 // r.barchart(30, 30, 560, 250, xdata, {
33336 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33337 // axis : "0 0 1 1",
33338 // axisxlabels : xdata
33339 // //yvalues : cols,
33342 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33344 // this.load(null,xdata,{
33345 // axis : "0 0 1 1",
33346 // axisxlabels : xdata
33351 load : function(graphtype,xdata,opts)
33353 this.raphael.clear();
33355 graphtype = this.graphtype;
33360 var r = this.raphael,
33361 fin = function () {
33362 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33364 fout = function () {
33365 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33367 pfin = function() {
33368 this.sector.stop();
33369 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33372 this.label[0].stop();
33373 this.label[0].attr({ r: 7.5 });
33374 this.label[1].attr({ "font-weight": 800 });
33377 pfout = function() {
33378 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33381 this.label[0].animate({ r: 5 }, 500, "bounce");
33382 this.label[1].attr({ "font-weight": 400 });
33388 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33391 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33394 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
33395 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33397 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33404 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33409 setTitle: function(o)
33414 initEvents: function() {
33417 this.el.on('click', this.onClick, this);
33421 onClick : function(e)
33423 Roo.log('img onclick');
33424 this.fireEvent('click', this, e);
33430 Roo.bootstrap.dash = {};/*
33436 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33439 * @class Roo.bootstrap.dash.NumberBox
33440 * @extends Roo.bootstrap.Component
33441 * Bootstrap NumberBox class
33442 * @cfg {String} headline Box headline
33443 * @cfg {String} content Box content
33444 * @cfg {String} icon Box icon
33445 * @cfg {String} footer Footer text
33446 * @cfg {String} fhref Footer href
33449 * Create a new NumberBox
33450 * @param {Object} config The config object
33454 Roo.bootstrap.dash.NumberBox = function(config){
33455 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33459 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
33468 getAutoCreate : function(){
33472 cls : 'small-box ',
33480 cls : 'roo-headline',
33481 html : this.headline
33485 cls : 'roo-content',
33486 html : this.content
33500 cls : 'ion ' + this.icon
33509 cls : 'small-box-footer',
33510 href : this.fhref || '#',
33514 cfg.cn.push(footer);
33521 onRender : function(ct,position){
33522 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
33529 setHeadline: function (value)
33531 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
33534 setFooter: function (value, href)
33536 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
33539 this.el.select('a.small-box-footer',true).first().attr('href', href);
33544 setContent: function (value)
33546 this.el.select('.roo-content',true).first().dom.innerHTML = value;
33549 initEvents: function()
33563 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33566 * @class Roo.bootstrap.dash.TabBox
33567 * @extends Roo.bootstrap.Component
33568 * @children Roo.bootstrap.dash.TabPane
33569 * Bootstrap TabBox class
33570 * @cfg {String} title Title of the TabBox
33571 * @cfg {String} icon Icon of the TabBox
33572 * @cfg {Boolean} showtabs (true|false) show the tabs default true
33573 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
33576 * Create a new TabBox
33577 * @param {Object} config The config object
33581 Roo.bootstrap.dash.TabBox = function(config){
33582 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
33587 * When a pane is added
33588 * @param {Roo.bootstrap.dash.TabPane} pane
33592 * @event activatepane
33593 * When a pane is activated
33594 * @param {Roo.bootstrap.dash.TabPane} pane
33596 "activatepane" : true
33604 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
33609 tabScrollable : false,
33611 getChildContainer : function()
33613 return this.el.select('.tab-content', true).first();
33616 getAutoCreate : function(){
33620 cls: 'pull-left header',
33628 cls: 'fa ' + this.icon
33634 cls: 'nav nav-tabs pull-right',
33640 if(this.tabScrollable){
33647 cls: 'nav nav-tabs pull-right',
33658 cls: 'nav-tabs-custom',
33663 cls: 'tab-content no-padding',
33671 initEvents : function()
33673 //Roo.log('add add pane handler');
33674 this.on('addpane', this.onAddPane, this);
33677 * Updates the box title
33678 * @param {String} html to set the title to.
33680 setTitle : function(value)
33682 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
33684 onAddPane : function(pane)
33686 this.panes.push(pane);
33687 //Roo.log('addpane');
33689 // tabs are rendere left to right..
33690 if(!this.showtabs){
33694 var ctr = this.el.select('.nav-tabs', true).first();
33697 var existing = ctr.select('.nav-tab',true);
33698 var qty = existing.getCount();;
33701 var tab = ctr.createChild({
33703 cls : 'nav-tab' + (qty ? '' : ' active'),
33711 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
33714 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
33716 pane.el.addClass('active');
33721 onTabClick : function(ev,un,ob,pane)
33723 //Roo.log('tab - prev default');
33724 ev.preventDefault();
33727 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
33728 pane.tab.addClass('active');
33729 //Roo.log(pane.title);
33730 this.getChildContainer().select('.tab-pane',true).removeClass('active');
33731 // technically we should have a deactivate event.. but maybe add later.
33732 // and it should not de-activate the selected tab...
33733 this.fireEvent('activatepane', pane);
33734 pane.el.addClass('active');
33735 pane.fireEvent('activate');
33740 getActivePane : function()
33743 Roo.each(this.panes, function(p) {
33744 if(p.el.hasClass('active')){
33765 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33767 * @class Roo.bootstrap.TabPane
33768 * @extends Roo.bootstrap.Component
33769 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
33770 * Bootstrap TabPane class
33771 * @cfg {Boolean} active (false | true) Default false
33772 * @cfg {String} title title of panel
33776 * Create a new TabPane
33777 * @param {Object} config The config object
33780 Roo.bootstrap.dash.TabPane = function(config){
33781 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
33787 * When a pane is activated
33788 * @param {Roo.bootstrap.dash.TabPane} pane
33795 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
33800 // the tabBox that this is attached to.
33803 getAutoCreate : function()
33811 cfg.cls += ' active';
33816 initEvents : function()
33818 //Roo.log('trigger add pane handler');
33819 this.parent().fireEvent('addpane', this)
33823 * Updates the tab title
33824 * @param {String} html to set the title to.
33826 setTitle: function(str)
33832 this.tab.select('a', true).first().dom.innerHTML = str;
33851 * @class Roo.bootstrap.Tooltip
33852 * Bootstrap Tooltip class
33853 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
33854 * to determine which dom element triggers the tooltip.
33856 * It needs to add support for additional attributes like tooltip-position
33859 * Create a new Toolti
33860 * @param {Object} config The config object
33863 Roo.bootstrap.Tooltip = function(config){
33864 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
33866 this.alignment = Roo.bootstrap.Tooltip.alignment;
33868 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
33869 this.alignment = config.alignment;
33874 Roo.apply(Roo.bootstrap.Tooltip, {
33876 * @function init initialize tooltip monitoring.
33880 currentTip : false,
33881 currentRegion : false,
33887 Roo.get(document).on('mouseover', this.enter ,this);
33888 Roo.get(document).on('mouseout', this.leave, this);
33891 this.currentTip = new Roo.bootstrap.Tooltip();
33894 enter : function(ev)
33896 var dom = ev.getTarget();
33898 //Roo.log(['enter',dom]);
33899 var el = Roo.fly(dom);
33900 if (this.currentEl) {
33902 //Roo.log(this.currentEl);
33903 //Roo.log(this.currentEl.contains(dom));
33904 if (this.currentEl == el) {
33907 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
33913 if (this.currentTip.el) {
33914 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
33918 if(!el || el.dom == document){
33924 if (!el.attr('tooltip')) {
33925 pel = el.findParent("[tooltip]");
33927 bindEl = Roo.get(pel);
33933 // you can not look for children, as if el is the body.. then everythign is the child..
33934 if (!pel && !el.attr('tooltip')) { //
33935 if (!el.select("[tooltip]").elements.length) {
33938 // is the mouse over this child...?
33939 bindEl = el.select("[tooltip]").first();
33940 var xy = ev.getXY();
33941 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
33942 //Roo.log("not in region.");
33945 //Roo.log("child element over..");
33948 this.currentEl = el;
33949 this.currentTip.bind(bindEl);
33950 this.currentRegion = Roo.lib.Region.getRegion(dom);
33951 this.currentTip.enter();
33954 leave : function(ev)
33956 var dom = ev.getTarget();
33957 //Roo.log(['leave',dom]);
33958 if (!this.currentEl) {
33963 if (dom != this.currentEl.dom) {
33966 var xy = ev.getXY();
33967 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
33970 // only activate leave if mouse cursor is outside... bounding box..
33975 if (this.currentTip) {
33976 this.currentTip.leave();
33978 //Roo.log('clear currentEl');
33979 this.currentEl = false;
33984 'left' : ['r-l', [-2,0], 'right'],
33985 'right' : ['l-r', [2,0], 'left'],
33986 'bottom' : ['t-b', [0,2], 'top'],
33987 'top' : [ 'b-t', [0,-2], 'bottom']
33993 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
33998 delay : null, // can be { show : 300 , hide: 500}
34002 hoverState : null, //???
34004 placement : 'bottom',
34008 getAutoCreate : function(){
34015 cls : 'tooltip-arrow arrow'
34018 cls : 'tooltip-inner'
34025 bind : function(el)
34030 initEvents : function()
34032 this.arrowEl = this.el.select('.arrow', true).first();
34033 this.innerEl = this.el.select('.tooltip-inner', true).first();
34036 enter : function () {
34038 if (this.timeout != null) {
34039 clearTimeout(this.timeout);
34042 this.hoverState = 'in';
34043 //Roo.log("enter - show");
34044 if (!this.delay || !this.delay.show) {
34049 this.timeout = setTimeout(function () {
34050 if (_t.hoverState == 'in') {
34053 }, this.delay.show);
34057 clearTimeout(this.timeout);
34059 this.hoverState = 'out';
34060 if (!this.delay || !this.delay.hide) {
34066 this.timeout = setTimeout(function () {
34067 //Roo.log("leave - timeout");
34069 if (_t.hoverState == 'out') {
34071 Roo.bootstrap.Tooltip.currentEl = false;
34076 show : function (msg)
34079 this.render(document.body);
34082 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34084 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34086 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34088 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34089 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34091 if(this.bindEl.attr('tooltip-class')) {
34092 this.el.addClass(this.bindEl.attr('tooltip-class'));
34095 var placement = typeof this.placement == 'function' ?
34096 this.placement.call(this, this.el, on_el) :
34099 if(this.bindEl.attr('tooltip-placement')) {
34100 placement = this.bindEl.attr('tooltip-placement');
34103 var autoToken = /\s?auto?\s?/i;
34104 var autoPlace = autoToken.test(placement);
34106 placement = placement.replace(autoToken, '') || 'top';
34110 //this.el.setXY([0,0]);
34112 //this.el.dom.style.display='block';
34114 //this.el.appendTo(on_el);
34116 var p = this.getPosition();
34117 var box = this.el.getBox();
34123 var align = this.alignment[placement];
34125 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34127 if(placement == 'top' || placement == 'bottom'){
34129 placement = 'right';
34132 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34133 placement = 'left';
34136 var scroll = Roo.select('body', true).first().getScroll();
34138 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34142 align = this.alignment[placement];
34144 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34148 var elems = document.getElementsByTagName('div');
34149 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34150 for (var i = 0; i < elems.length; i++) {
34151 var zindex = Number.parseInt(
34152 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34155 if (zindex > highest) {
34162 this.el.dom.style.zIndex = highest;
34164 this.el.alignTo(this.bindEl, align[0],align[1]);
34165 //var arrow = this.el.select('.arrow',true).first();
34166 //arrow.set(align[2],
34168 this.el.addClass(placement);
34169 this.el.addClass("bs-tooltip-"+ placement);
34171 this.el.addClass('in fade show');
34173 this.hoverState = null;
34175 if (this.el.hasClass('fade')) {
34190 //this.el.setXY([0,0]);
34191 if(this.bindEl.attr('tooltip-class')) {
34192 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34194 this.el.removeClass(['show', 'in']);
34210 * @class Roo.bootstrap.LocationPicker
34211 * @extends Roo.bootstrap.Component
34212 * Bootstrap LocationPicker class
34213 * @cfg {Number} latitude Position when init default 0
34214 * @cfg {Number} longitude Position when init default 0
34215 * @cfg {Number} zoom default 15
34216 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34217 * @cfg {Boolean} mapTypeControl default false
34218 * @cfg {Boolean} disableDoubleClickZoom default false
34219 * @cfg {Boolean} scrollwheel default true
34220 * @cfg {Boolean} streetViewControl default false
34221 * @cfg {Number} radius default 0
34222 * @cfg {String} locationName
34223 * @cfg {Boolean} draggable default true
34224 * @cfg {Boolean} enableAutocomplete default false
34225 * @cfg {Boolean} enableReverseGeocode default true
34226 * @cfg {String} markerTitle
34229 * Create a new LocationPicker
34230 * @param {Object} config The config object
34234 Roo.bootstrap.LocationPicker = function(config){
34236 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34241 * Fires when the picker initialized.
34242 * @param {Roo.bootstrap.LocationPicker} this
34243 * @param {Google Location} location
34247 * @event positionchanged
34248 * Fires when the picker position changed.
34249 * @param {Roo.bootstrap.LocationPicker} this
34250 * @param {Google Location} location
34252 positionchanged : true,
34255 * Fires when the map resize.
34256 * @param {Roo.bootstrap.LocationPicker} this
34261 * Fires when the map show.
34262 * @param {Roo.bootstrap.LocationPicker} this
34267 * Fires when the map hide.
34268 * @param {Roo.bootstrap.LocationPicker} this
34273 * Fires when click the map.
34274 * @param {Roo.bootstrap.LocationPicker} this
34275 * @param {Map event} e
34279 * @event mapRightClick
34280 * Fires when right click the map.
34281 * @param {Roo.bootstrap.LocationPicker} this
34282 * @param {Map event} e
34284 mapRightClick : true,
34286 * @event markerClick
34287 * Fires when click the marker.
34288 * @param {Roo.bootstrap.LocationPicker} this
34289 * @param {Map event} e
34291 markerClick : true,
34293 * @event markerRightClick
34294 * Fires when right click the marker.
34295 * @param {Roo.bootstrap.LocationPicker} this
34296 * @param {Map event} e
34298 markerRightClick : true,
34300 * @event OverlayViewDraw
34301 * Fires when OverlayView Draw
34302 * @param {Roo.bootstrap.LocationPicker} this
34304 OverlayViewDraw : true,
34306 * @event OverlayViewOnAdd
34307 * Fires when OverlayView Draw
34308 * @param {Roo.bootstrap.LocationPicker} this
34310 OverlayViewOnAdd : true,
34312 * @event OverlayViewOnRemove
34313 * Fires when OverlayView Draw
34314 * @param {Roo.bootstrap.LocationPicker} this
34316 OverlayViewOnRemove : true,
34318 * @event OverlayViewShow
34319 * Fires when OverlayView Draw
34320 * @param {Roo.bootstrap.LocationPicker} this
34321 * @param {Pixel} cpx
34323 OverlayViewShow : true,
34325 * @event OverlayViewHide
34326 * Fires when OverlayView Draw
34327 * @param {Roo.bootstrap.LocationPicker} this
34329 OverlayViewHide : true,
34331 * @event loadexception
34332 * Fires when load google lib failed.
34333 * @param {Roo.bootstrap.LocationPicker} this
34335 loadexception : true
34340 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
34342 gMapContext: false,
34348 mapTypeControl: false,
34349 disableDoubleClickZoom: false,
34351 streetViewControl: false,
34355 enableAutocomplete: false,
34356 enableReverseGeocode: true,
34359 getAutoCreate: function()
34364 cls: 'roo-location-picker'
34370 initEvents: function(ct, position)
34372 if(!this.el.getWidth() || this.isApplied()){
34376 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34381 initial: function()
34383 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34384 this.fireEvent('loadexception', this);
34388 if(!this.mapTypeId){
34389 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34392 this.gMapContext = this.GMapContext();
34394 this.initOverlayView();
34396 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34400 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34401 _this.setPosition(_this.gMapContext.marker.position);
34404 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34405 _this.fireEvent('mapClick', this, event);
34409 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34410 _this.fireEvent('mapRightClick', this, event);
34414 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34415 _this.fireEvent('markerClick', this, event);
34419 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34420 _this.fireEvent('markerRightClick', this, event);
34424 this.setPosition(this.gMapContext.location);
34426 this.fireEvent('initial', this, this.gMapContext.location);
34429 initOverlayView: function()
34433 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34437 _this.fireEvent('OverlayViewDraw', _this);
34442 _this.fireEvent('OverlayViewOnAdd', _this);
34445 onRemove: function()
34447 _this.fireEvent('OverlayViewOnRemove', _this);
34450 show: function(cpx)
34452 _this.fireEvent('OverlayViewShow', _this, cpx);
34457 _this.fireEvent('OverlayViewHide', _this);
34463 fromLatLngToContainerPixel: function(event)
34465 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34468 isApplied: function()
34470 return this.getGmapContext() == false ? false : true;
34473 getGmapContext: function()
34475 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
34478 GMapContext: function()
34480 var position = new google.maps.LatLng(this.latitude, this.longitude);
34482 var _map = new google.maps.Map(this.el.dom, {
34485 mapTypeId: this.mapTypeId,
34486 mapTypeControl: this.mapTypeControl,
34487 disableDoubleClickZoom: this.disableDoubleClickZoom,
34488 scrollwheel: this.scrollwheel,
34489 streetViewControl: this.streetViewControl,
34490 locationName: this.locationName,
34491 draggable: this.draggable,
34492 enableAutocomplete: this.enableAutocomplete,
34493 enableReverseGeocode: this.enableReverseGeocode
34496 var _marker = new google.maps.Marker({
34497 position: position,
34499 title: this.markerTitle,
34500 draggable: this.draggable
34507 location: position,
34508 radius: this.radius,
34509 locationName: this.locationName,
34510 addressComponents: {
34511 formatted_address: null,
34512 addressLine1: null,
34513 addressLine2: null,
34515 streetNumber: null,
34519 stateOrProvince: null
34522 domContainer: this.el.dom,
34523 geodecoder: new google.maps.Geocoder()
34527 drawCircle: function(center, radius, options)
34529 if (this.gMapContext.circle != null) {
34530 this.gMapContext.circle.setMap(null);
34534 options = Roo.apply({}, options, {
34535 strokeColor: "#0000FF",
34536 strokeOpacity: .35,
34538 fillColor: "#0000FF",
34542 options.map = this.gMapContext.map;
34543 options.radius = radius;
34544 options.center = center;
34545 this.gMapContext.circle = new google.maps.Circle(options);
34546 return this.gMapContext.circle;
34552 setPosition: function(location)
34554 this.gMapContext.location = location;
34555 this.gMapContext.marker.setPosition(location);
34556 this.gMapContext.map.panTo(location);
34557 this.drawCircle(location, this.gMapContext.radius, {});
34561 if (this.gMapContext.settings.enableReverseGeocode) {
34562 this.gMapContext.geodecoder.geocode({
34563 latLng: this.gMapContext.location
34564 }, function(results, status) {
34566 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
34567 _this.gMapContext.locationName = results[0].formatted_address;
34568 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
34570 _this.fireEvent('positionchanged', this, location);
34577 this.fireEvent('positionchanged', this, location);
34582 google.maps.event.trigger(this.gMapContext.map, "resize");
34584 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
34586 this.fireEvent('resize', this);
34589 setPositionByLatLng: function(latitude, longitude)
34591 this.setPosition(new google.maps.LatLng(latitude, longitude));
34594 getCurrentPosition: function()
34597 latitude: this.gMapContext.location.lat(),
34598 longitude: this.gMapContext.location.lng()
34602 getAddressName: function()
34604 return this.gMapContext.locationName;
34607 getAddressComponents: function()
34609 return this.gMapContext.addressComponents;
34612 address_component_from_google_geocode: function(address_components)
34616 for (var i = 0; i < address_components.length; i++) {
34617 var component = address_components[i];
34618 if (component.types.indexOf("postal_code") >= 0) {
34619 result.postalCode = component.short_name;
34620 } else if (component.types.indexOf("street_number") >= 0) {
34621 result.streetNumber = component.short_name;
34622 } else if (component.types.indexOf("route") >= 0) {
34623 result.streetName = component.short_name;
34624 } else if (component.types.indexOf("neighborhood") >= 0) {
34625 result.city = component.short_name;
34626 } else if (component.types.indexOf("locality") >= 0) {
34627 result.city = component.short_name;
34628 } else if (component.types.indexOf("sublocality") >= 0) {
34629 result.district = component.short_name;
34630 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
34631 result.stateOrProvince = component.short_name;
34632 } else if (component.types.indexOf("country") >= 0) {
34633 result.country = component.short_name;
34637 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
34638 result.addressLine2 = "";
34642 setZoomLevel: function(zoom)
34644 this.gMapContext.map.setZoom(zoom);
34657 this.fireEvent('show', this);
34668 this.fireEvent('hide', this);
34673 Roo.apply(Roo.bootstrap.LocationPicker, {
34675 OverlayView : function(map, options)
34677 options = options || {};
34684 * @class Roo.bootstrap.Alert
34685 * @extends Roo.bootstrap.Component
34686 * Bootstrap Alert class - shows an alert area box
34688 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
34689 Enter a valid email address
34692 * @cfg {String} title The title of alert
34693 * @cfg {String} html The content of alert
34694 * @cfg {String} weight (success|info|warning|danger) Weight of the message
34695 * @cfg {String} fa font-awesomeicon
34696 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
34697 * @cfg {Boolean} close true to show a x closer
34701 * Create a new alert
34702 * @param {Object} config The config object
34706 Roo.bootstrap.Alert = function(config){
34707 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
34711 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
34717 faicon: false, // BC
34721 getAutoCreate : function()
34733 style : this.close ? '' : 'display:none'
34737 cls : 'roo-alert-icon'
34742 cls : 'roo-alert-title',
34747 cls : 'roo-alert-text',
34754 cfg.cn[0].cls += ' fa ' + this.faicon;
34757 cfg.cn[0].cls += ' fa ' + this.fa;
34761 cfg.cls += ' alert-' + this.weight;
34767 initEvents: function()
34769 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34770 this.titleEl = this.el.select('.roo-alert-title',true).first();
34771 this.iconEl = this.el.select('.roo-alert-icon',true).first();
34772 this.htmlEl = this.el.select('.roo-alert-text',true).first();
34773 if (this.seconds > 0) {
34774 this.hide.defer(this.seconds, this);
34778 * Set the Title Message HTML
34779 * @param {String} html
34781 setTitle : function(str)
34783 this.titleEl.dom.innerHTML = str;
34787 * Set the Body Message HTML
34788 * @param {String} html
34790 setHtml : function(str)
34792 this.htmlEl.dom.innerHTML = str;
34795 * Set the Weight of the alert
34796 * @param {String} (success|info|warning|danger) weight
34799 setWeight : function(weight)
34802 this.el.removeClass('alert-' + this.weight);
34805 this.weight = weight;
34807 this.el.addClass('alert-' + this.weight);
34810 * Set the Icon of the alert
34811 * @param {String} see fontawsome names (name without the 'fa-' bit)
34813 setIcon : function(icon)
34816 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
34819 this.faicon = icon;
34821 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
34846 * @class Roo.bootstrap.UploadCropbox
34847 * @extends Roo.bootstrap.Component
34848 * Bootstrap UploadCropbox class
34849 * @cfg {String} emptyText show when image has been loaded
34850 * @cfg {String} rotateNotify show when image too small to rotate
34851 * @cfg {Number} errorTimeout default 3000
34852 * @cfg {Number} minWidth default 300
34853 * @cfg {Number} minHeight default 300
34854 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
34855 * @cfg {Boolean} isDocument (true|false) default false
34856 * @cfg {String} url action url
34857 * @cfg {String} paramName default 'imageUpload'
34858 * @cfg {String} method default POST
34859 * @cfg {Boolean} loadMask (true|false) default true
34860 * @cfg {Boolean} loadingText default 'Loading...'
34863 * Create a new UploadCropbox
34864 * @param {Object} config The config object
34867 Roo.bootstrap.UploadCropbox = function(config){
34868 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
34872 * @event beforeselectfile
34873 * Fire before select file
34874 * @param {Roo.bootstrap.UploadCropbox} this
34876 "beforeselectfile" : true,
34879 * Fire after initEvent
34880 * @param {Roo.bootstrap.UploadCropbox} this
34885 * Fire after initEvent
34886 * @param {Roo.bootstrap.UploadCropbox} this
34887 * @param {String} data
34892 * Fire when preparing the file data
34893 * @param {Roo.bootstrap.UploadCropbox} this
34894 * @param {Object} file
34899 * Fire when get exception
34900 * @param {Roo.bootstrap.UploadCropbox} this
34901 * @param {XMLHttpRequest} xhr
34903 "exception" : true,
34905 * @event beforeloadcanvas
34906 * Fire before load the canvas
34907 * @param {Roo.bootstrap.UploadCropbox} this
34908 * @param {String} src
34910 "beforeloadcanvas" : true,
34913 * Fire when trash image
34914 * @param {Roo.bootstrap.UploadCropbox} this
34919 * Fire when download the image
34920 * @param {Roo.bootstrap.UploadCropbox} this
34924 * @event footerbuttonclick
34925 * Fire when footerbuttonclick
34926 * @param {Roo.bootstrap.UploadCropbox} this
34927 * @param {String} type
34929 "footerbuttonclick" : true,
34933 * @param {Roo.bootstrap.UploadCropbox} this
34938 * Fire when rotate the image
34939 * @param {Roo.bootstrap.UploadCropbox} this
34940 * @param {String} pos
34945 * Fire when inspect the file
34946 * @param {Roo.bootstrap.UploadCropbox} this
34947 * @param {Object} file
34952 * Fire when xhr upload the file
34953 * @param {Roo.bootstrap.UploadCropbox} this
34954 * @param {Object} data
34959 * Fire when arrange the file data
34960 * @param {Roo.bootstrap.UploadCropbox} this
34961 * @param {Object} formData
34966 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
34969 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
34971 emptyText : 'Click to upload image',
34972 rotateNotify : 'Image is too small to rotate',
34973 errorTimeout : 3000,
34987 cropType : 'image/jpeg',
34989 canvasLoaded : false,
34990 isDocument : false,
34992 paramName : 'imageUpload',
34994 loadingText : 'Loading...',
34997 getAutoCreate : function()
35001 cls : 'roo-upload-cropbox',
35005 cls : 'roo-upload-cropbox-selector',
35010 cls : 'roo-upload-cropbox-body',
35011 style : 'cursor:pointer',
35015 cls : 'roo-upload-cropbox-preview'
35019 cls : 'roo-upload-cropbox-thumb'
35023 cls : 'roo-upload-cropbox-empty-notify',
35024 html : this.emptyText
35028 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35029 html : this.rotateNotify
35035 cls : 'roo-upload-cropbox-footer',
35038 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35048 onRender : function(ct, position)
35050 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35052 if (this.buttons.length) {
35054 Roo.each(this.buttons, function(bb) {
35056 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35058 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35064 this.maskEl = this.el;
35068 initEvents : function()
35070 this.urlAPI = (window.createObjectURL && window) ||
35071 (window.URL && URL.revokeObjectURL && URL) ||
35072 (window.webkitURL && webkitURL);
35074 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35075 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35077 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35078 this.selectorEl.hide();
35080 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35081 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35083 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35084 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35085 this.thumbEl.hide();
35087 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35088 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35090 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35091 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35092 this.errorEl.hide();
35094 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35095 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35096 this.footerEl.hide();
35098 this.setThumbBoxSize();
35104 this.fireEvent('initial', this);
35111 window.addEventListener("resize", function() { _this.resize(); } );
35113 this.bodyEl.on('click', this.beforeSelectFile, this);
35116 this.bodyEl.on('touchstart', this.onTouchStart, this);
35117 this.bodyEl.on('touchmove', this.onTouchMove, this);
35118 this.bodyEl.on('touchend', this.onTouchEnd, this);
35122 this.bodyEl.on('mousedown', this.onMouseDown, this);
35123 this.bodyEl.on('mousemove', this.onMouseMove, this);
35124 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35125 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35126 Roo.get(document).on('mouseup', this.onMouseUp, this);
35129 this.selectorEl.on('change', this.onFileSelected, this);
35135 this.baseScale = 1;
35137 this.baseRotate = 1;
35138 this.dragable = false;
35139 this.pinching = false;
35142 this.cropData = false;
35143 this.notifyEl.dom.innerHTML = this.emptyText;
35145 this.selectorEl.dom.value = '';
35149 resize : function()
35151 if(this.fireEvent('resize', this) != false){
35152 this.setThumbBoxPosition();
35153 this.setCanvasPosition();
35157 onFooterButtonClick : function(e, el, o, type)
35160 case 'rotate-left' :
35161 this.onRotateLeft(e);
35163 case 'rotate-right' :
35164 this.onRotateRight(e);
35167 this.beforeSelectFile(e);
35182 this.fireEvent('footerbuttonclick', this, type);
35185 beforeSelectFile : function(e)
35187 e.preventDefault();
35189 if(this.fireEvent('beforeselectfile', this) != false){
35190 this.selectorEl.dom.click();
35194 onFileSelected : function(e)
35196 e.preventDefault();
35198 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35202 var file = this.selectorEl.dom.files[0];
35204 if(this.fireEvent('inspect', this, file) != false){
35205 this.prepare(file);
35210 trash : function(e)
35212 this.fireEvent('trash', this);
35215 download : function(e)
35217 this.fireEvent('download', this);
35220 loadCanvas : function(src)
35222 if(this.fireEvent('beforeloadcanvas', this, src) != false){
35226 this.imageEl = document.createElement('img');
35230 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35232 this.imageEl.src = src;
35236 onLoadCanvas : function()
35238 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35239 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35241 this.bodyEl.un('click', this.beforeSelectFile, this);
35243 this.notifyEl.hide();
35244 this.thumbEl.show();
35245 this.footerEl.show();
35247 this.baseRotateLevel();
35249 if(this.isDocument){
35250 this.setThumbBoxSize();
35253 this.setThumbBoxPosition();
35255 this.baseScaleLevel();
35261 this.canvasLoaded = true;
35264 this.maskEl.unmask();
35269 setCanvasPosition : function()
35271 if(!this.canvasEl){
35275 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35276 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35278 this.previewEl.setLeft(pw);
35279 this.previewEl.setTop(ph);
35283 onMouseDown : function(e)
35287 this.dragable = true;
35288 this.pinching = false;
35290 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35291 this.dragable = false;
35295 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35296 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35300 onMouseMove : function(e)
35304 if(!this.canvasLoaded){
35308 if (!this.dragable){
35312 var minX = Math.ceil(this.thumbEl.getLeft(true));
35313 var minY = Math.ceil(this.thumbEl.getTop(true));
35315 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35316 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35318 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35319 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35321 x = x - this.mouseX;
35322 y = y - this.mouseY;
35324 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35325 var bgY = Math.ceil(y + this.previewEl.getTop(true));
35327 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35328 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35330 this.previewEl.setLeft(bgX);
35331 this.previewEl.setTop(bgY);
35333 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35334 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35337 onMouseUp : function(e)
35341 this.dragable = false;
35344 onMouseWheel : function(e)
35348 this.startScale = this.scale;
35350 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35352 if(!this.zoomable()){
35353 this.scale = this.startScale;
35362 zoomable : function()
35364 var minScale = this.thumbEl.getWidth() / this.minWidth;
35366 if(this.minWidth < this.minHeight){
35367 minScale = this.thumbEl.getHeight() / this.minHeight;
35370 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35371 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35375 (this.rotate == 0 || this.rotate == 180) &&
35377 width > this.imageEl.OriginWidth ||
35378 height > this.imageEl.OriginHeight ||
35379 (width < this.minWidth && height < this.minHeight)
35387 (this.rotate == 90 || this.rotate == 270) &&
35389 width > this.imageEl.OriginWidth ||
35390 height > this.imageEl.OriginHeight ||
35391 (width < this.minHeight && height < this.minWidth)
35398 !this.isDocument &&
35399 (this.rotate == 0 || this.rotate == 180) &&
35401 width < this.minWidth ||
35402 width > this.imageEl.OriginWidth ||
35403 height < this.minHeight ||
35404 height > this.imageEl.OriginHeight
35411 !this.isDocument &&
35412 (this.rotate == 90 || this.rotate == 270) &&
35414 width < this.minHeight ||
35415 width > this.imageEl.OriginWidth ||
35416 height < this.minWidth ||
35417 height > this.imageEl.OriginHeight
35427 onRotateLeft : function(e)
35429 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35431 var minScale = this.thumbEl.getWidth() / this.minWidth;
35433 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35434 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35436 this.startScale = this.scale;
35438 while (this.getScaleLevel() < minScale){
35440 this.scale = this.scale + 1;
35442 if(!this.zoomable()){
35447 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35448 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35453 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35460 this.scale = this.startScale;
35462 this.onRotateFail();
35467 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35469 if(this.isDocument){
35470 this.setThumbBoxSize();
35471 this.setThumbBoxPosition();
35472 this.setCanvasPosition();
35477 this.fireEvent('rotate', this, 'left');
35481 onRotateRight : function(e)
35483 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35485 var minScale = this.thumbEl.getWidth() / this.minWidth;
35487 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35488 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35490 this.startScale = this.scale;
35492 while (this.getScaleLevel() < minScale){
35494 this.scale = this.scale + 1;
35496 if(!this.zoomable()){
35501 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35502 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35507 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35514 this.scale = this.startScale;
35516 this.onRotateFail();
35521 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
35523 if(this.isDocument){
35524 this.setThumbBoxSize();
35525 this.setThumbBoxPosition();
35526 this.setCanvasPosition();
35531 this.fireEvent('rotate', this, 'right');
35534 onRotateFail : function()
35536 this.errorEl.show(true);
35540 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
35545 this.previewEl.dom.innerHTML = '';
35547 var canvasEl = document.createElement("canvas");
35549 var contextEl = canvasEl.getContext("2d");
35551 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35552 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35553 var center = this.imageEl.OriginWidth / 2;
35555 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
35556 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35557 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35558 center = this.imageEl.OriginHeight / 2;
35561 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
35563 contextEl.translate(center, center);
35564 contextEl.rotate(this.rotate * Math.PI / 180);
35566 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35568 this.canvasEl = document.createElement("canvas");
35570 this.contextEl = this.canvasEl.getContext("2d");
35572 switch (this.rotate) {
35575 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35576 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35578 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35583 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35584 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35586 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35587 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);
35591 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35596 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
35597 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
35599 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35600 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);
35604 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);
35609 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
35610 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
35612 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35613 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
35617 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);
35624 this.previewEl.appendChild(this.canvasEl);
35626 this.setCanvasPosition();
35631 if(!this.canvasLoaded){
35635 var imageCanvas = document.createElement("canvas");
35637 var imageContext = imageCanvas.getContext("2d");
35639 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35640 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
35642 var center = imageCanvas.width / 2;
35644 imageContext.translate(center, center);
35646 imageContext.rotate(this.rotate * Math.PI / 180);
35648 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
35650 var canvas = document.createElement("canvas");
35652 var context = canvas.getContext("2d");
35654 canvas.width = this.minWidth;
35655 canvas.height = this.minHeight;
35657 switch (this.rotate) {
35660 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35661 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35663 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35664 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35666 var targetWidth = this.minWidth - 2 * x;
35667 var targetHeight = this.minHeight - 2 * y;
35671 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35672 scale = targetWidth / width;
35675 if(x > 0 && y == 0){
35676 scale = targetHeight / height;
35679 if(x > 0 && y > 0){
35680 scale = targetWidth / width;
35682 if(width < height){
35683 scale = targetHeight / height;
35687 context.scale(scale, scale);
35689 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35690 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35692 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35693 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35695 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35700 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35701 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35703 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35704 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35706 var targetWidth = this.minWidth - 2 * x;
35707 var targetHeight = this.minHeight - 2 * y;
35711 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35712 scale = targetWidth / width;
35715 if(x > 0 && y == 0){
35716 scale = targetHeight / height;
35719 if(x > 0 && y > 0){
35720 scale = targetWidth / width;
35722 if(width < height){
35723 scale = targetHeight / height;
35727 context.scale(scale, scale);
35729 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35730 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35732 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35733 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35735 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35737 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35742 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
35743 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
35745 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35746 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35748 var targetWidth = this.minWidth - 2 * x;
35749 var targetHeight = this.minHeight - 2 * y;
35753 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35754 scale = targetWidth / width;
35757 if(x > 0 && y == 0){
35758 scale = targetHeight / height;
35761 if(x > 0 && y > 0){
35762 scale = targetWidth / width;
35764 if(width < height){
35765 scale = targetHeight / height;
35769 context.scale(scale, scale);
35771 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35772 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35774 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35775 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35777 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35778 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
35780 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35785 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
35786 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
35788 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
35789 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
35791 var targetWidth = this.minWidth - 2 * x;
35792 var targetHeight = this.minHeight - 2 * y;
35796 if((x == 0 && y == 0) || (x == 0 && y > 0)){
35797 scale = targetWidth / width;
35800 if(x > 0 && y == 0){
35801 scale = targetHeight / height;
35804 if(x > 0 && y > 0){
35805 scale = targetWidth / width;
35807 if(width < height){
35808 scale = targetHeight / height;
35812 context.scale(scale, scale);
35814 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
35815 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
35817 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
35818 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
35820 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
35822 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
35829 this.cropData = canvas.toDataURL(this.cropType);
35831 if(this.fireEvent('crop', this, this.cropData) !== false){
35832 this.process(this.file, this.cropData);
35839 setThumbBoxSize : function()
35843 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
35844 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
35845 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
35847 this.minWidth = width;
35848 this.minHeight = height;
35850 if(this.rotate == 90 || this.rotate == 270){
35851 this.minWidth = height;
35852 this.minHeight = width;
35857 width = Math.ceil(this.minWidth * height / this.minHeight);
35859 if(this.minWidth > this.minHeight){
35861 height = Math.ceil(this.minHeight * width / this.minWidth);
35864 this.thumbEl.setStyle({
35865 width : width + 'px',
35866 height : height + 'px'
35873 setThumbBoxPosition : function()
35875 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
35876 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
35878 this.thumbEl.setLeft(x);
35879 this.thumbEl.setTop(y);
35883 baseRotateLevel : function()
35885 this.baseRotate = 1;
35888 typeof(this.exif) != 'undefined' &&
35889 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
35890 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
35892 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
35895 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
35899 baseScaleLevel : function()
35903 if(this.isDocument){
35905 if(this.baseRotate == 6 || this.baseRotate == 8){
35907 height = this.thumbEl.getHeight();
35908 this.baseScale = height / this.imageEl.OriginWidth;
35910 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
35911 width = this.thumbEl.getWidth();
35912 this.baseScale = width / this.imageEl.OriginHeight;
35918 height = this.thumbEl.getHeight();
35919 this.baseScale = height / this.imageEl.OriginHeight;
35921 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
35922 width = this.thumbEl.getWidth();
35923 this.baseScale = width / this.imageEl.OriginWidth;
35929 if(this.baseRotate == 6 || this.baseRotate == 8){
35931 width = this.thumbEl.getHeight();
35932 this.baseScale = width / this.imageEl.OriginHeight;
35934 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
35935 height = this.thumbEl.getWidth();
35936 this.baseScale = height / this.imageEl.OriginHeight;
35939 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35940 height = this.thumbEl.getWidth();
35941 this.baseScale = height / this.imageEl.OriginHeight;
35943 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
35944 width = this.thumbEl.getHeight();
35945 this.baseScale = width / this.imageEl.OriginWidth;
35952 width = this.thumbEl.getWidth();
35953 this.baseScale = width / this.imageEl.OriginWidth;
35955 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
35956 height = this.thumbEl.getHeight();
35957 this.baseScale = height / this.imageEl.OriginHeight;
35960 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
35962 height = this.thumbEl.getHeight();
35963 this.baseScale = height / this.imageEl.OriginHeight;
35965 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
35966 width = this.thumbEl.getWidth();
35967 this.baseScale = width / this.imageEl.OriginWidth;
35975 getScaleLevel : function()
35977 return this.baseScale * Math.pow(1.1, this.scale);
35980 onTouchStart : function(e)
35982 if(!this.canvasLoaded){
35983 this.beforeSelectFile(e);
35987 var touches = e.browserEvent.touches;
35993 if(touches.length == 1){
35994 this.onMouseDown(e);
35998 if(touches.length != 2){
36004 for(var i = 0, finger; finger = touches[i]; i++){
36005 coords.push(finger.pageX, finger.pageY);
36008 var x = Math.pow(coords[0] - coords[2], 2);
36009 var y = Math.pow(coords[1] - coords[3], 2);
36011 this.startDistance = Math.sqrt(x + y);
36013 this.startScale = this.scale;
36015 this.pinching = true;
36016 this.dragable = false;
36020 onTouchMove : function(e)
36022 if(!this.pinching && !this.dragable){
36026 var touches = e.browserEvent.touches;
36033 this.onMouseMove(e);
36039 for(var i = 0, finger; finger = touches[i]; i++){
36040 coords.push(finger.pageX, finger.pageY);
36043 var x = Math.pow(coords[0] - coords[2], 2);
36044 var y = Math.pow(coords[1] - coords[3], 2);
36046 this.endDistance = Math.sqrt(x + y);
36048 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36050 if(!this.zoomable()){
36051 this.scale = this.startScale;
36059 onTouchEnd : function(e)
36061 this.pinching = false;
36062 this.dragable = false;
36066 process : function(file, crop)
36069 this.maskEl.mask(this.loadingText);
36072 this.xhr = new XMLHttpRequest();
36074 file.xhr = this.xhr;
36076 this.xhr.open(this.method, this.url, true);
36079 "Accept": "application/json",
36080 "Cache-Control": "no-cache",
36081 "X-Requested-With": "XMLHttpRequest"
36084 for (var headerName in headers) {
36085 var headerValue = headers[headerName];
36087 this.xhr.setRequestHeader(headerName, headerValue);
36093 this.xhr.onload = function()
36095 _this.xhrOnLoad(_this.xhr);
36098 this.xhr.onerror = function()
36100 _this.xhrOnError(_this.xhr);
36103 var formData = new FormData();
36105 formData.append('returnHTML', 'NO');
36108 formData.append('crop', crop);
36111 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36112 formData.append(this.paramName, file, file.name);
36115 if(typeof(file.filename) != 'undefined'){
36116 formData.append('filename', file.filename);
36119 if(typeof(file.mimetype) != 'undefined'){
36120 formData.append('mimetype', file.mimetype);
36123 if(this.fireEvent('arrange', this, formData) != false){
36124 this.xhr.send(formData);
36128 xhrOnLoad : function(xhr)
36131 this.maskEl.unmask();
36134 if (xhr.readyState !== 4) {
36135 this.fireEvent('exception', this, xhr);
36139 var response = Roo.decode(xhr.responseText);
36141 if(!response.success){
36142 this.fireEvent('exception', this, xhr);
36146 var response = Roo.decode(xhr.responseText);
36148 this.fireEvent('upload', this, response);
36152 xhrOnError : function()
36155 this.maskEl.unmask();
36158 Roo.log('xhr on error');
36160 var response = Roo.decode(xhr.responseText);
36166 prepare : function(file)
36169 this.maskEl.mask(this.loadingText);
36175 if(typeof(file) === 'string'){
36176 this.loadCanvas(file);
36180 if(!file || !this.urlAPI){
36185 this.cropType = file.type;
36189 if(this.fireEvent('prepare', this, this.file) != false){
36191 var reader = new FileReader();
36193 reader.onload = function (e) {
36194 if (e.target.error) {
36195 Roo.log(e.target.error);
36199 var buffer = e.target.result,
36200 dataView = new DataView(buffer),
36202 maxOffset = dataView.byteLength - 4,
36206 if (dataView.getUint16(0) === 0xffd8) {
36207 while (offset < maxOffset) {
36208 markerBytes = dataView.getUint16(offset);
36210 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36211 markerLength = dataView.getUint16(offset + 2) + 2;
36212 if (offset + markerLength > dataView.byteLength) {
36213 Roo.log('Invalid meta data: Invalid segment size.');
36217 if(markerBytes == 0xffe1){
36218 _this.parseExifData(
36225 offset += markerLength;
36235 var url = _this.urlAPI.createObjectURL(_this.file);
36237 _this.loadCanvas(url);
36242 reader.readAsArrayBuffer(this.file);
36248 parseExifData : function(dataView, offset, length)
36250 var tiffOffset = offset + 10,
36254 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36255 // No Exif data, might be XMP data instead
36259 // Check for the ASCII code for "Exif" (0x45786966):
36260 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36261 // No Exif data, might be XMP data instead
36264 if (tiffOffset + 8 > dataView.byteLength) {
36265 Roo.log('Invalid Exif data: Invalid segment size.');
36268 // Check for the two null bytes:
36269 if (dataView.getUint16(offset + 8) !== 0x0000) {
36270 Roo.log('Invalid Exif data: Missing byte alignment offset.');
36273 // Check the byte alignment:
36274 switch (dataView.getUint16(tiffOffset)) {
36276 littleEndian = true;
36279 littleEndian = false;
36282 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36285 // Check for the TIFF tag marker (0x002A):
36286 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36287 Roo.log('Invalid Exif data: Missing TIFF marker.');
36290 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36291 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36293 this.parseExifTags(
36296 tiffOffset + dirOffset,
36301 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36306 if (dirOffset + 6 > dataView.byteLength) {
36307 Roo.log('Invalid Exif data: Invalid directory offset.');
36310 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36311 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36312 if (dirEndOffset + 4 > dataView.byteLength) {
36313 Roo.log('Invalid Exif data: Invalid directory size.');
36316 for (i = 0; i < tagsNumber; i += 1) {
36320 dirOffset + 2 + 12 * i, // tag offset
36324 // Return the offset to the next directory:
36325 return dataView.getUint32(dirEndOffset, littleEndian);
36328 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
36330 var tag = dataView.getUint16(offset, littleEndian);
36332 this.exif[tag] = this.getExifValue(
36336 dataView.getUint16(offset + 2, littleEndian), // tag type
36337 dataView.getUint32(offset + 4, littleEndian), // tag length
36342 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36344 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36353 Roo.log('Invalid Exif data: Invalid tag type.');
36357 tagSize = tagType.size * length;
36358 // Determine if the value is contained in the dataOffset bytes,
36359 // or if the value at the dataOffset is a pointer to the actual data:
36360 dataOffset = tagSize > 4 ?
36361 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36362 if (dataOffset + tagSize > dataView.byteLength) {
36363 Roo.log('Invalid Exif data: Invalid data offset.');
36366 if (length === 1) {
36367 return tagType.getValue(dataView, dataOffset, littleEndian);
36370 for (i = 0; i < length; i += 1) {
36371 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36374 if (tagType.ascii) {
36376 // Concatenate the chars:
36377 for (i = 0; i < values.length; i += 1) {
36379 // Ignore the terminating NULL byte(s):
36380 if (c === '\u0000') {
36392 Roo.apply(Roo.bootstrap.UploadCropbox, {
36394 'Orientation': 0x0112
36398 1: 0, //'top-left',
36400 3: 180, //'bottom-right',
36401 // 4: 'bottom-left',
36403 6: 90, //'right-top',
36404 // 7: 'right-bottom',
36405 8: 270 //'left-bottom'
36409 // byte, 8-bit unsigned int:
36411 getValue: function (dataView, dataOffset) {
36412 return dataView.getUint8(dataOffset);
36416 // ascii, 8-bit byte:
36418 getValue: function (dataView, dataOffset) {
36419 return String.fromCharCode(dataView.getUint8(dataOffset));
36424 // short, 16 bit int:
36426 getValue: function (dataView, dataOffset, littleEndian) {
36427 return dataView.getUint16(dataOffset, littleEndian);
36431 // long, 32 bit int:
36433 getValue: function (dataView, dataOffset, littleEndian) {
36434 return dataView.getUint32(dataOffset, littleEndian);
36438 // rational = two long values, first is numerator, second is denominator:
36440 getValue: function (dataView, dataOffset, littleEndian) {
36441 return dataView.getUint32(dataOffset, littleEndian) /
36442 dataView.getUint32(dataOffset + 4, littleEndian);
36446 // slong, 32 bit signed int:
36448 getValue: function (dataView, dataOffset, littleEndian) {
36449 return dataView.getInt32(dataOffset, littleEndian);
36453 // srational, two slongs, first is numerator, second is denominator:
36455 getValue: function (dataView, dataOffset, littleEndian) {
36456 return dataView.getInt32(dataOffset, littleEndian) /
36457 dataView.getInt32(dataOffset + 4, littleEndian);
36467 cls : 'btn-group roo-upload-cropbox-rotate-left',
36468 action : 'rotate-left',
36472 cls : 'btn btn-default',
36473 html : '<i class="fa fa-undo"></i>'
36479 cls : 'btn-group roo-upload-cropbox-picture',
36480 action : 'picture',
36484 cls : 'btn btn-default',
36485 html : '<i class="fa fa-picture-o"></i>'
36491 cls : 'btn-group roo-upload-cropbox-rotate-right',
36492 action : 'rotate-right',
36496 cls : 'btn btn-default',
36497 html : '<i class="fa fa-repeat"></i>'
36505 cls : 'btn-group roo-upload-cropbox-rotate-left',
36506 action : 'rotate-left',
36510 cls : 'btn btn-default',
36511 html : '<i class="fa fa-undo"></i>'
36517 cls : 'btn-group roo-upload-cropbox-download',
36518 action : 'download',
36522 cls : 'btn btn-default',
36523 html : '<i class="fa fa-download"></i>'
36529 cls : 'btn-group roo-upload-cropbox-crop',
36534 cls : 'btn btn-default',
36535 html : '<i class="fa fa-crop"></i>'
36541 cls : 'btn-group roo-upload-cropbox-trash',
36546 cls : 'btn btn-default',
36547 html : '<i class="fa fa-trash"></i>'
36553 cls : 'btn-group roo-upload-cropbox-rotate-right',
36554 action : 'rotate-right',
36558 cls : 'btn btn-default',
36559 html : '<i class="fa fa-repeat"></i>'
36567 cls : 'btn-group roo-upload-cropbox-rotate-left',
36568 action : 'rotate-left',
36572 cls : 'btn btn-default',
36573 html : '<i class="fa fa-undo"></i>'
36579 cls : 'btn-group roo-upload-cropbox-rotate-right',
36580 action : 'rotate-right',
36584 cls : 'btn btn-default',
36585 html : '<i class="fa fa-repeat"></i>'
36598 * @class Roo.bootstrap.DocumentManager
36599 * @extends Roo.bootstrap.Component
36600 * Bootstrap DocumentManager class
36601 * @cfg {String} paramName default 'imageUpload'
36602 * @cfg {String} toolTipName default 'filename'
36603 * @cfg {String} method default POST
36604 * @cfg {String} url action url
36605 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
36606 * @cfg {Boolean} multiple multiple upload default true
36607 * @cfg {Number} thumbSize default 300
36608 * @cfg {String} fieldLabel
36609 * @cfg {Number} labelWidth default 4
36610 * @cfg {String} labelAlign (left|top) default left
36611 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
36612 * @cfg {Number} labellg set the width of label (1-12)
36613 * @cfg {Number} labelmd set the width of label (1-12)
36614 * @cfg {Number} labelsm set the width of label (1-12)
36615 * @cfg {Number} labelxs set the width of label (1-12)
36618 * Create a new DocumentManager
36619 * @param {Object} config The config object
36622 Roo.bootstrap.DocumentManager = function(config){
36623 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
36626 this.delegates = [];
36631 * Fire when initial the DocumentManager
36632 * @param {Roo.bootstrap.DocumentManager} this
36637 * inspect selected file
36638 * @param {Roo.bootstrap.DocumentManager} this
36639 * @param {File} file
36644 * Fire when xhr load exception
36645 * @param {Roo.bootstrap.DocumentManager} this
36646 * @param {XMLHttpRequest} xhr
36648 "exception" : true,
36650 * @event afterupload
36651 * Fire when xhr load exception
36652 * @param {Roo.bootstrap.DocumentManager} this
36653 * @param {XMLHttpRequest} xhr
36655 "afterupload" : true,
36658 * prepare the form data
36659 * @param {Roo.bootstrap.DocumentManager} this
36660 * @param {Object} formData
36665 * Fire when remove the file
36666 * @param {Roo.bootstrap.DocumentManager} this
36667 * @param {Object} file
36672 * Fire after refresh the file
36673 * @param {Roo.bootstrap.DocumentManager} this
36678 * Fire after click the image
36679 * @param {Roo.bootstrap.DocumentManager} this
36680 * @param {Object} file
36685 * Fire when upload a image and editable set to true
36686 * @param {Roo.bootstrap.DocumentManager} this
36687 * @param {Object} file
36691 * @event beforeselectfile
36692 * Fire before select file
36693 * @param {Roo.bootstrap.DocumentManager} this
36695 "beforeselectfile" : true,
36698 * Fire before process file
36699 * @param {Roo.bootstrap.DocumentManager} this
36700 * @param {Object} file
36704 * @event previewrendered
36705 * Fire when preview rendered
36706 * @param {Roo.bootstrap.DocumentManager} this
36707 * @param {Object} file
36709 "previewrendered" : true,
36712 "previewResize" : true
36717 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
36726 paramName : 'imageUpload',
36727 toolTipName : 'filename',
36730 labelAlign : 'left',
36740 getAutoCreate : function()
36742 var managerWidget = {
36744 cls : 'roo-document-manager',
36748 cls : 'roo-document-manager-selector',
36753 cls : 'roo-document-manager-uploader',
36757 cls : 'roo-document-manager-upload-btn',
36758 html : '<i class="fa fa-plus"></i>'
36769 cls : 'column col-md-12',
36774 if(this.fieldLabel.length){
36779 cls : 'column col-md-12',
36780 html : this.fieldLabel
36784 cls : 'column col-md-12',
36789 if(this.labelAlign == 'left'){
36794 html : this.fieldLabel
36803 if(this.labelWidth > 12){
36804 content[0].style = "width: " + this.labelWidth + 'px';
36807 if(this.labelWidth < 13 && this.labelmd == 0){
36808 this.labelmd = this.labelWidth;
36811 if(this.labellg > 0){
36812 content[0].cls += ' col-lg-' + this.labellg;
36813 content[1].cls += ' col-lg-' + (12 - this.labellg);
36816 if(this.labelmd > 0){
36817 content[0].cls += ' col-md-' + this.labelmd;
36818 content[1].cls += ' col-md-' + (12 - this.labelmd);
36821 if(this.labelsm > 0){
36822 content[0].cls += ' col-sm-' + this.labelsm;
36823 content[1].cls += ' col-sm-' + (12 - this.labelsm);
36826 if(this.labelxs > 0){
36827 content[0].cls += ' col-xs-' + this.labelxs;
36828 content[1].cls += ' col-xs-' + (12 - this.labelxs);
36836 cls : 'row clearfix',
36844 initEvents : function()
36846 this.managerEl = this.el.select('.roo-document-manager', true).first();
36847 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36849 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
36850 this.selectorEl.hide();
36853 this.selectorEl.attr('multiple', 'multiple');
36856 this.selectorEl.on('change', this.onFileSelected, this);
36858 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
36859 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
36861 this.uploader.on('click', this.onUploaderClick, this);
36863 this.renderProgressDialog();
36867 window.addEventListener("resize", function() { _this.refresh(); } );
36869 this.fireEvent('initial', this);
36872 renderProgressDialog : function()
36876 this.progressDialog = new Roo.bootstrap.Modal({
36877 cls : 'roo-document-manager-progress-dialog',
36878 allow_close : false,
36889 btnclick : function() {
36890 _this.uploadCancel();
36896 this.progressDialog.render(Roo.get(document.body));
36898 this.progress = new Roo.bootstrap.Progress({
36899 cls : 'roo-document-manager-progress',
36904 this.progress.render(this.progressDialog.getChildContainer());
36906 this.progressBar = new Roo.bootstrap.ProgressBar({
36907 cls : 'roo-document-manager-progress-bar',
36910 aria_valuemax : 12,
36914 this.progressBar.render(this.progress.getChildContainer());
36917 onUploaderClick : function(e)
36919 e.preventDefault();
36921 if(this.fireEvent('beforeselectfile', this) != false){
36922 this.selectorEl.dom.click();
36927 onFileSelected : function(e)
36929 e.preventDefault();
36931 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
36935 Roo.each(this.selectorEl.dom.files, function(file){
36936 if(this.fireEvent('inspect', this, file) != false){
36937 this.files.push(file);
36947 this.selectorEl.dom.value = '';
36949 if(!this.files || !this.files.length){
36953 if(this.boxes > 0 && this.files.length > this.boxes){
36954 this.files = this.files.slice(0, this.boxes);
36957 this.uploader.show();
36959 if(this.boxes > 0 && this.files.length > this.boxes - 1){
36960 this.uploader.hide();
36969 Roo.each(this.files, function(file){
36971 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
36972 var f = this.renderPreview(file);
36977 if(file.type.indexOf('image') != -1){
36978 this.delegates.push(
36980 _this.process(file);
36981 }).createDelegate(this)
36989 _this.process(file);
36990 }).createDelegate(this)
36995 this.files = files;
36997 this.delegates = this.delegates.concat(docs);
36999 if(!this.delegates.length){
37004 this.progressBar.aria_valuemax = this.delegates.length;
37011 arrange : function()
37013 if(!this.delegates.length){
37014 this.progressDialog.hide();
37019 var delegate = this.delegates.shift();
37021 this.progressDialog.show();
37023 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37025 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37030 refresh : function()
37032 this.uploader.show();
37034 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37035 this.uploader.hide();
37038 Roo.isTouch ? this.closable(false) : this.closable(true);
37040 this.fireEvent('refresh', this);
37043 onRemove : function(e, el, o)
37045 e.preventDefault();
37047 this.fireEvent('remove', this, o);
37051 remove : function(o)
37055 Roo.each(this.files, function(file){
37056 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37065 this.files = files;
37072 Roo.each(this.files, function(file){
37077 file.target.remove();
37086 onClick : function(e, el, o)
37088 e.preventDefault();
37090 this.fireEvent('click', this, o);
37094 closable : function(closable)
37096 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37098 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37110 xhrOnLoad : function(xhr)
37112 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37116 if (xhr.readyState !== 4) {
37118 this.fireEvent('exception', this, xhr);
37122 var response = Roo.decode(xhr.responseText);
37124 if(!response.success){
37126 this.fireEvent('exception', this, xhr);
37130 var file = this.renderPreview(response.data);
37132 this.files.push(file);
37136 this.fireEvent('afterupload', this, xhr);
37140 xhrOnError : function(xhr)
37142 Roo.log('xhr on error');
37144 var response = Roo.decode(xhr.responseText);
37151 process : function(file)
37153 if(this.fireEvent('process', this, file) !== false){
37154 if(this.editable && file.type.indexOf('image') != -1){
37155 this.fireEvent('edit', this, file);
37159 this.uploadStart(file, false);
37166 uploadStart : function(file, crop)
37168 this.xhr = new XMLHttpRequest();
37170 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37175 file.xhr = this.xhr;
37177 this.managerEl.createChild({
37179 cls : 'roo-document-manager-loading',
37183 tooltip : file.name,
37184 cls : 'roo-document-manager-thumb',
37185 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37191 this.xhr.open(this.method, this.url, true);
37194 "Accept": "application/json",
37195 "Cache-Control": "no-cache",
37196 "X-Requested-With": "XMLHttpRequest"
37199 for (var headerName in headers) {
37200 var headerValue = headers[headerName];
37202 this.xhr.setRequestHeader(headerName, headerValue);
37208 this.xhr.onload = function()
37210 _this.xhrOnLoad(_this.xhr);
37213 this.xhr.onerror = function()
37215 _this.xhrOnError(_this.xhr);
37218 var formData = new FormData();
37220 formData.append('returnHTML', 'NO');
37223 formData.append('crop', crop);
37226 formData.append(this.paramName, file, file.name);
37233 if(this.fireEvent('prepare', this, formData, options) != false){
37235 if(options.manually){
37239 this.xhr.send(formData);
37243 this.uploadCancel();
37246 uploadCancel : function()
37252 this.delegates = [];
37254 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37261 renderPreview : function(file)
37263 if(typeof(file.target) != 'undefined' && file.target){
37267 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37269 var previewEl = this.managerEl.createChild({
37271 cls : 'roo-document-manager-preview',
37275 tooltip : file[this.toolTipName],
37276 cls : 'roo-document-manager-thumb',
37277 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37282 html : '<i class="fa fa-times-circle"></i>'
37287 var close = previewEl.select('button.close', true).first();
37289 close.on('click', this.onRemove, this, file);
37291 file.target = previewEl;
37293 var image = previewEl.select('img', true).first();
37297 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37299 image.on('click', this.onClick, this, file);
37301 this.fireEvent('previewrendered', this, file);
37307 onPreviewLoad : function(file, image)
37309 if(typeof(file.target) == 'undefined' || !file.target){
37313 var width = image.dom.naturalWidth || image.dom.width;
37314 var height = image.dom.naturalHeight || image.dom.height;
37316 if(!this.previewResize) {
37320 if(width > height){
37321 file.target.addClass('wide');
37325 file.target.addClass('tall');
37330 uploadFromSource : function(file, crop)
37332 this.xhr = new XMLHttpRequest();
37334 this.managerEl.createChild({
37336 cls : 'roo-document-manager-loading',
37340 tooltip : file.name,
37341 cls : 'roo-document-manager-thumb',
37342 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37348 this.xhr.open(this.method, this.url, true);
37351 "Accept": "application/json",
37352 "Cache-Control": "no-cache",
37353 "X-Requested-With": "XMLHttpRequest"
37356 for (var headerName in headers) {
37357 var headerValue = headers[headerName];
37359 this.xhr.setRequestHeader(headerName, headerValue);
37365 this.xhr.onload = function()
37367 _this.xhrOnLoad(_this.xhr);
37370 this.xhr.onerror = function()
37372 _this.xhrOnError(_this.xhr);
37375 var formData = new FormData();
37377 formData.append('returnHTML', 'NO');
37379 formData.append('crop', crop);
37381 if(typeof(file.filename) != 'undefined'){
37382 formData.append('filename', file.filename);
37385 if(typeof(file.mimetype) != 'undefined'){
37386 formData.append('mimetype', file.mimetype);
37391 if(this.fireEvent('prepare', this, formData) != false){
37392 this.xhr.send(formData);
37402 * @class Roo.bootstrap.DocumentViewer
37403 * @extends Roo.bootstrap.Component
37404 * Bootstrap DocumentViewer class
37405 * @cfg {Boolean} showDownload (true|false) show download button (default true)
37406 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37409 * Create a new DocumentViewer
37410 * @param {Object} config The config object
37413 Roo.bootstrap.DocumentViewer = function(config){
37414 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37419 * Fire after initEvent
37420 * @param {Roo.bootstrap.DocumentViewer} this
37426 * @param {Roo.bootstrap.DocumentViewer} this
37431 * Fire after download button
37432 * @param {Roo.bootstrap.DocumentViewer} this
37437 * Fire after trash button
37438 * @param {Roo.bootstrap.DocumentViewer} this
37445 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
37447 showDownload : true,
37451 getAutoCreate : function()
37455 cls : 'roo-document-viewer',
37459 cls : 'roo-document-viewer-body',
37463 cls : 'roo-document-viewer-thumb',
37467 cls : 'roo-document-viewer-image'
37475 cls : 'roo-document-viewer-footer',
37478 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
37482 cls : 'btn-group roo-document-viewer-download',
37486 cls : 'btn btn-default',
37487 html : '<i class="fa fa-download"></i>'
37493 cls : 'btn-group roo-document-viewer-trash',
37497 cls : 'btn btn-default',
37498 html : '<i class="fa fa-trash"></i>'
37511 initEvents : function()
37513 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
37514 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37516 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
37517 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37519 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
37520 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37522 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
37523 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
37525 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
37526 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
37528 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
37529 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
37531 this.bodyEl.on('click', this.onClick, this);
37532 this.downloadBtn.on('click', this.onDownload, this);
37533 this.trashBtn.on('click', this.onTrash, this);
37535 this.downloadBtn.hide();
37536 this.trashBtn.hide();
37538 if(this.showDownload){
37539 this.downloadBtn.show();
37542 if(this.showTrash){
37543 this.trashBtn.show();
37546 if(!this.showDownload && !this.showTrash) {
37547 this.footerEl.hide();
37552 initial : function()
37554 this.fireEvent('initial', this);
37558 onClick : function(e)
37560 e.preventDefault();
37562 this.fireEvent('click', this);
37565 onDownload : function(e)
37567 e.preventDefault();
37569 this.fireEvent('download', this);
37572 onTrash : function(e)
37574 e.preventDefault();
37576 this.fireEvent('trash', this);
37588 * @class Roo.bootstrap.form.FieldLabel
37589 * @extends Roo.bootstrap.Component
37590 * Bootstrap FieldLabel class
37591 * @cfg {String} html contents of the element
37592 * @cfg {String} tag tag of the element default label
37593 * @cfg {String} cls class of the element
37594 * @cfg {String} target label target
37595 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
37596 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
37597 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
37598 * @cfg {String} iconTooltip default "This field is required"
37599 * @cfg {String} indicatorpos (left|right) default left
37602 * Create a new FieldLabel
37603 * @param {Object} config The config object
37606 Roo.bootstrap.form.FieldLabel = function(config){
37607 Roo.bootstrap.Element.superclass.constructor.call(this, config);
37612 * Fires after the field has been marked as invalid.
37613 * @param {Roo.form.FieldLabel} this
37614 * @param {String} msg The validation message
37619 * Fires after the field has been validated with no errors.
37620 * @param {Roo.form.FieldLabel} this
37626 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
37633 invalidClass : 'has-warning',
37634 validClass : 'has-success',
37635 iconTooltip : 'This field is required',
37636 indicatorpos : 'left',
37638 getAutoCreate : function(){
37641 if (!this.allowBlank) {
37647 cls : 'roo-bootstrap-field-label ' + this.cls,
37652 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
37653 tooltip : this.iconTooltip
37662 if(this.indicatorpos == 'right'){
37665 cls : 'roo-bootstrap-field-label ' + this.cls,
37674 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
37675 tooltip : this.iconTooltip
37684 initEvents: function()
37686 Roo.bootstrap.Element.superclass.initEvents.call(this);
37688 this.indicator = this.indicatorEl();
37690 if(this.indicator){
37691 this.indicator.removeClass('visible');
37692 this.indicator.addClass('invisible');
37695 Roo.bootstrap.form.FieldLabel.register(this);
37698 indicatorEl : function()
37700 var indicator = this.el.select('i.roo-required-indicator',true).first();
37711 * Mark this field as valid
37713 markValid : function()
37715 if(this.indicator){
37716 this.indicator.removeClass('visible');
37717 this.indicator.addClass('invisible');
37719 if (Roo.bootstrap.version == 3) {
37720 this.el.removeClass(this.invalidClass);
37721 this.el.addClass(this.validClass);
37723 this.el.removeClass('is-invalid');
37724 this.el.addClass('is-valid');
37728 this.fireEvent('valid', this);
37732 * Mark this field as invalid
37733 * @param {String} msg The validation message
37735 markInvalid : function(msg)
37737 if(this.indicator){
37738 this.indicator.removeClass('invisible');
37739 this.indicator.addClass('visible');
37741 if (Roo.bootstrap.version == 3) {
37742 this.el.removeClass(this.validClass);
37743 this.el.addClass(this.invalidClass);
37745 this.el.removeClass('is-valid');
37746 this.el.addClass('is-invalid');
37750 this.fireEvent('invalid', this, msg);
37756 Roo.apply(Roo.bootstrap.form.FieldLabel, {
37761 * register a FieldLabel Group
37762 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
37764 register : function(label)
37766 if(this.groups.hasOwnProperty(label.target)){
37770 this.groups[label.target] = label;
37774 * fetch a FieldLabel Group based on the target
37775 * @param {string} target
37776 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
37778 get: function(target) {
37779 if (typeof(this.groups[target]) == 'undefined') {
37783 return this.groups[target] ;
37792 * page DateSplitField.
37798 * @class Roo.bootstrap.form.DateSplitField
37799 * @extends Roo.bootstrap.Component
37800 * Bootstrap DateSplitField class
37801 * @cfg {string} fieldLabel - the label associated
37802 * @cfg {Number} labelWidth set the width of label (0-12)
37803 * @cfg {String} labelAlign (top|left)
37804 * @cfg {Boolean} dayAllowBlank (true|false) default false
37805 * @cfg {Boolean} monthAllowBlank (true|false) default false
37806 * @cfg {Boolean} yearAllowBlank (true|false) default false
37807 * @cfg {string} dayPlaceholder
37808 * @cfg {string} monthPlaceholder
37809 * @cfg {string} yearPlaceholder
37810 * @cfg {string} dayFormat default 'd'
37811 * @cfg {string} monthFormat default 'm'
37812 * @cfg {string} yearFormat default 'Y'
37813 * @cfg {Number} labellg set the width of label (1-12)
37814 * @cfg {Number} labelmd set the width of label (1-12)
37815 * @cfg {Number} labelsm set the width of label (1-12)
37816 * @cfg {Number} labelxs set the width of label (1-12)
37820 * Create a new DateSplitField
37821 * @param {Object} config The config object
37824 Roo.bootstrap.form.DateSplitField = function(config){
37825 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
37831 * getting the data of years
37832 * @param {Roo.bootstrap.form.DateSplitField} this
37833 * @param {Object} years
37838 * getting the data of days
37839 * @param {Roo.bootstrap.form.DateSplitField} this
37840 * @param {Object} days
37845 * Fires after the field has been marked as invalid.
37846 * @param {Roo.form.Field} this
37847 * @param {String} msg The validation message
37852 * Fires after the field has been validated with no errors.
37853 * @param {Roo.form.Field} this
37859 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
37862 labelAlign : 'top',
37864 dayAllowBlank : false,
37865 monthAllowBlank : false,
37866 yearAllowBlank : false,
37867 dayPlaceholder : '',
37868 monthPlaceholder : '',
37869 yearPlaceholder : '',
37873 isFormField : true,
37879 getAutoCreate : function()
37883 cls : 'row roo-date-split-field-group',
37888 cls : 'form-hidden-field roo-date-split-field-group-value',
37894 var labelCls = 'col-md-12';
37895 var contentCls = 'col-md-4';
37897 if(this.fieldLabel){
37901 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
37905 html : this.fieldLabel
37910 if(this.labelAlign == 'left'){
37912 if(this.labelWidth > 12){
37913 label.style = "width: " + this.labelWidth + 'px';
37916 if(this.labelWidth < 13 && this.labelmd == 0){
37917 this.labelmd = this.labelWidth;
37920 if(this.labellg > 0){
37921 labelCls = ' col-lg-' + this.labellg;
37922 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
37925 if(this.labelmd > 0){
37926 labelCls = ' col-md-' + this.labelmd;
37927 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
37930 if(this.labelsm > 0){
37931 labelCls = ' col-sm-' + this.labelsm;
37932 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
37935 if(this.labelxs > 0){
37936 labelCls = ' col-xs-' + this.labelxs;
37937 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
37941 label.cls += ' ' + labelCls;
37943 cfg.cn.push(label);
37946 Roo.each(['day', 'month', 'year'], function(t){
37949 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
37956 inputEl: function ()
37958 return this.el.select('.roo-date-split-field-group-value', true).first();
37961 onRender : function(ct, position)
37965 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
37967 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
37969 this.dayField = new Roo.bootstrap.form.ComboBox({
37970 allowBlank : this.dayAllowBlank,
37971 alwaysQuery : true,
37972 displayField : 'value',
37975 forceSelection : true,
37977 placeholder : this.dayPlaceholder,
37978 selectOnFocus : true,
37979 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
37980 triggerAction : 'all',
37982 valueField : 'value',
37983 store : new Roo.data.SimpleStore({
37984 data : (function() {
37986 _this.fireEvent('days', _this, days);
37989 fields : [ 'value' ]
37992 select : function (_self, record, index)
37994 _this.setValue(_this.getValue());
37999 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38001 this.monthField = new Roo.bootstrap.form.MonthField({
38002 after : '<i class=\"fa fa-calendar\"></i>',
38003 allowBlank : this.monthAllowBlank,
38004 placeholder : this.monthPlaceholder,
38007 render : function (_self)
38009 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38010 e.preventDefault();
38014 select : function (_self, oldvalue, newvalue)
38016 _this.setValue(_this.getValue());
38021 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38023 this.yearField = new Roo.bootstrap.form.ComboBox({
38024 allowBlank : this.yearAllowBlank,
38025 alwaysQuery : true,
38026 displayField : 'value',
38029 forceSelection : true,
38031 placeholder : this.yearPlaceholder,
38032 selectOnFocus : true,
38033 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38034 triggerAction : 'all',
38036 valueField : 'value',
38037 store : new Roo.data.SimpleStore({
38038 data : (function() {
38040 _this.fireEvent('years', _this, years);
38043 fields : [ 'value' ]
38046 select : function (_self, record, index)
38048 _this.setValue(_this.getValue());
38053 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38056 setValue : function(v, format)
38058 this.inputEl.dom.value = v;
38060 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38062 var d = Date.parseDate(v, f);
38069 this.setDay(d.format(this.dayFormat));
38070 this.setMonth(d.format(this.monthFormat));
38071 this.setYear(d.format(this.yearFormat));
38078 setDay : function(v)
38080 this.dayField.setValue(v);
38081 this.inputEl.dom.value = this.getValue();
38086 setMonth : function(v)
38088 this.monthField.setValue(v, true);
38089 this.inputEl.dom.value = this.getValue();
38094 setYear : function(v)
38096 this.yearField.setValue(v);
38097 this.inputEl.dom.value = this.getValue();
38102 getDay : function()
38104 return this.dayField.getValue();
38107 getMonth : function()
38109 return this.monthField.getValue();
38112 getYear : function()
38114 return this.yearField.getValue();
38117 getValue : function()
38119 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38121 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38131 this.inputEl.dom.value = '';
38136 validate : function()
38138 var d = this.dayField.validate();
38139 var m = this.monthField.validate();
38140 var y = this.yearField.validate();
38145 (!this.dayAllowBlank && !d) ||
38146 (!this.monthAllowBlank && !m) ||
38147 (!this.yearAllowBlank && !y)
38152 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38161 this.markInvalid();
38166 markValid : function()
38169 var label = this.el.select('label', true).first();
38170 var icon = this.el.select('i.fa-star', true).first();
38176 this.fireEvent('valid', this);
38180 * Mark this field as invalid
38181 * @param {String} msg The validation message
38183 markInvalid : function(msg)
38186 var label = this.el.select('label', true).first();
38187 var icon = this.el.select('i.fa-star', true).first();
38189 if(label && !icon){
38190 this.el.select('.roo-date-split-field-label', true).createChild({
38192 cls : 'text-danger fa fa-lg fa-star',
38193 tooltip : 'This field is required',
38194 style : 'margin-right:5px;'
38198 this.fireEvent('invalid', this, msg);
38201 clearInvalid : function()
38203 var label = this.el.select('label', true).first();
38204 var icon = this.el.select('i.fa-star', true).first();
38210 this.fireEvent('valid', this);
38213 getName: function()
38223 * @class Roo.bootstrap.LayoutMasonry
38224 * @extends Roo.bootstrap.Component
38225 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38226 * Bootstrap Layout Masonry class
38229 * http://masonry.desandro.com
38231 * The idea is to render all the bricks based on vertical width...
38233 * The original code extends 'outlayer' - we might need to use that....
38236 * Create a new Element
38237 * @param {Object} config The config object
38240 Roo.bootstrap.LayoutMasonry = function(config){
38242 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38246 Roo.bootstrap.LayoutMasonry.register(this);
38252 * Fire after layout the items
38253 * @param {Roo.bootstrap.LayoutMasonry} this
38254 * @param {Roo.EventObject} e
38261 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
38264 * @cfg {Boolean} isLayoutInstant = no animation?
38266 isLayoutInstant : false, // needed?
38269 * @cfg {Number} boxWidth width of the columns
38274 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
38279 * @cfg {Number} padWidth padding below box..
38284 * @cfg {Number} gutter gutter width..
38289 * @cfg {Number} maxCols maximum number of columns
38295 * @cfg {Boolean} isAutoInitial defalut true
38297 isAutoInitial : true,
38302 * @cfg {Boolean} isHorizontal defalut false
38304 isHorizontal : false,
38306 currentSize : null,
38312 bricks: null, //CompositeElement
38316 _isLayoutInited : false,
38318 // isAlternative : false, // only use for vertical layout...
38321 * @cfg {Number} alternativePadWidth padding below box..
38323 alternativePadWidth : 50,
38325 selectedBrick : [],
38327 getAutoCreate : function(){
38329 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38333 cls: 'blog-masonary-wrapper ' + this.cls,
38335 cls : 'mas-boxes masonary'
38342 getChildContainer: function( )
38344 if (this.boxesEl) {
38345 return this.boxesEl;
38348 this.boxesEl = this.el.select('.mas-boxes').first();
38350 return this.boxesEl;
38354 initEvents : function()
38358 if(this.isAutoInitial){
38359 Roo.log('hook children rendered');
38360 this.on('childrenrendered', function() {
38361 Roo.log('children rendered');
38367 initial : function()
38369 this.selectedBrick = [];
38371 this.currentSize = this.el.getBox(true);
38373 Roo.EventManager.onWindowResize(this.resize, this);
38375 if(!this.isAutoInitial){
38383 //this.layout.defer(500,this);
38387 resize : function()
38389 var cs = this.el.getBox(true);
38392 this.currentSize.width == cs.width &&
38393 this.currentSize.x == cs.x &&
38394 this.currentSize.height == cs.height &&
38395 this.currentSize.y == cs.y
38397 Roo.log("no change in with or X or Y");
38401 this.currentSize = cs;
38407 layout : function()
38409 this._resetLayout();
38411 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38413 this.layoutItems( isInstant );
38415 this._isLayoutInited = true;
38417 this.fireEvent('layout', this);
38421 _resetLayout : function()
38423 if(this.isHorizontal){
38424 this.horizontalMeasureColumns();
38428 this.verticalMeasureColumns();
38432 verticalMeasureColumns : function()
38434 this.getContainerWidth();
38436 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38437 // this.colWidth = Math.floor(this.containerWidth * 0.8);
38441 var boxWidth = this.boxWidth + this.padWidth;
38443 if(this.containerWidth < this.boxWidth){
38444 boxWidth = this.containerWidth
38447 var containerWidth = this.containerWidth;
38449 var cols = Math.floor(containerWidth / boxWidth);
38451 this.cols = Math.max( cols, 1 );
38453 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38455 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38457 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38459 this.colWidth = boxWidth + avail - this.padWidth;
38461 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38462 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
38465 horizontalMeasureColumns : function()
38467 this.getContainerWidth();
38469 var boxWidth = this.boxWidth;
38471 if(this.containerWidth < boxWidth){
38472 boxWidth = this.containerWidth;
38475 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
38477 this.el.setHeight(boxWidth);
38481 getContainerWidth : function()
38483 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38486 layoutItems : function( isInstant )
38488 Roo.log(this.bricks);
38490 var items = Roo.apply([], this.bricks);
38492 if(this.isHorizontal){
38493 this._horizontalLayoutItems( items , isInstant );
38497 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38498 // this._verticalAlternativeLayoutItems( items , isInstant );
38502 this._verticalLayoutItems( items , isInstant );
38506 _verticalLayoutItems : function ( items , isInstant)
38508 if ( !items || !items.length ) {
38513 ['xs', 'xs', 'xs', 'tall'],
38514 ['xs', 'xs', 'tall'],
38515 ['xs', 'xs', 'sm'],
38516 ['xs', 'xs', 'xs'],
38522 ['sm', 'xs', 'xs'],
38526 ['tall', 'xs', 'xs', 'xs'],
38527 ['tall', 'xs', 'xs'],
38539 Roo.each(items, function(item, k){
38541 switch (item.size) {
38542 // these layouts take up a full box,
38553 boxes.push([item]);
38576 var filterPattern = function(box, length)
38584 var pattern = box.slice(0, length);
38588 Roo.each(pattern, function(i){
38589 format.push(i.size);
38592 Roo.each(standard, function(s){
38594 if(String(s) != String(format)){
38603 if(!match && length == 1){
38608 filterPattern(box, length - 1);
38612 queue.push(pattern);
38614 box = box.slice(length, box.length);
38616 filterPattern(box, 4);
38622 Roo.each(boxes, function(box, k){
38628 if(box.length == 1){
38633 filterPattern(box, 4);
38637 this._processVerticalLayoutQueue( queue, isInstant );
38641 // _verticalAlternativeLayoutItems : function( items , isInstant )
38643 // if ( !items || !items.length ) {
38647 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
38651 _horizontalLayoutItems : function ( items , isInstant)
38653 if ( !items || !items.length || items.length < 3) {
38659 var eItems = items.slice(0, 3);
38661 items = items.slice(3, items.length);
38664 ['xs', 'xs', 'xs', 'wide'],
38665 ['xs', 'xs', 'wide'],
38666 ['xs', 'xs', 'sm'],
38667 ['xs', 'xs', 'xs'],
38673 ['sm', 'xs', 'xs'],
38677 ['wide', 'xs', 'xs', 'xs'],
38678 ['wide', 'xs', 'xs'],
38691 Roo.each(items, function(item, k){
38693 switch (item.size) {
38704 boxes.push([item]);
38728 var filterPattern = function(box, length)
38736 var pattern = box.slice(0, length);
38740 Roo.each(pattern, function(i){
38741 format.push(i.size);
38744 Roo.each(standard, function(s){
38746 if(String(s) != String(format)){
38755 if(!match && length == 1){
38760 filterPattern(box, length - 1);
38764 queue.push(pattern);
38766 box = box.slice(length, box.length);
38768 filterPattern(box, 4);
38774 Roo.each(boxes, function(box, k){
38780 if(box.length == 1){
38785 filterPattern(box, 4);
38792 var pos = this.el.getBox(true);
38796 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38798 var hit_end = false;
38800 Roo.each(queue, function(box){
38804 Roo.each(box, function(b){
38806 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38816 Roo.each(box, function(b){
38818 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38821 mx = Math.max(mx, b.x);
38825 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
38829 Roo.each(box, function(b){
38831 b.el.setVisibilityMode(Roo.Element.DISPLAY);
38845 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
38848 /** Sets position of item in DOM
38849 * @param {Element} item
38850 * @param {Number} x - horizontal position
38851 * @param {Number} y - vertical position
38852 * @param {Boolean} isInstant - disables transitions
38854 _processVerticalLayoutQueue : function( queue, isInstant )
38856 var pos = this.el.getBox(true);
38861 for (var i = 0; i < this.cols; i++){
38865 Roo.each(queue, function(box, k){
38867 var col = k % this.cols;
38869 Roo.each(box, function(b,kk){
38871 b.el.position('absolute');
38873 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
38874 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
38876 if(b.size == 'md-left' || b.size == 'md-right'){
38877 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
38878 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
38881 b.el.setWidth(width);
38882 b.el.setHeight(height);
38884 b.el.select('iframe',true).setSize(width,height);
38888 for (var i = 0; i < this.cols; i++){
38890 if(maxY[i] < maxY[col]){
38895 col = Math.min(col, i);
38899 x = pos.x + col * (this.colWidth + this.padWidth);
38903 var positions = [];
38905 switch (box.length){
38907 positions = this.getVerticalOneBoxColPositions(x, y, box);
38910 positions = this.getVerticalTwoBoxColPositions(x, y, box);
38913 positions = this.getVerticalThreeBoxColPositions(x, y, box);
38916 positions = this.getVerticalFourBoxColPositions(x, y, box);
38922 Roo.each(box, function(b,kk){
38924 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
38926 var sz = b.el.getSize();
38928 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
38936 for (var i = 0; i < this.cols; i++){
38937 mY = Math.max(mY, maxY[i]);
38940 this.el.setHeight(mY - pos.y);
38944 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
38946 // var pos = this.el.getBox(true);
38949 // var maxX = pos.right;
38951 // var maxHeight = 0;
38953 // Roo.each(items, function(item, k){
38957 // item.el.position('absolute');
38959 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
38961 // item.el.setWidth(width);
38963 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
38965 // item.el.setHeight(height);
38968 // item.el.setXY([x, y], isInstant ? false : true);
38970 // item.el.setXY([maxX - width, y], isInstant ? false : true);
38973 // y = y + height + this.alternativePadWidth;
38975 // maxHeight = maxHeight + height + this.alternativePadWidth;
38979 // this.el.setHeight(maxHeight);
38983 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
38985 var pos = this.el.getBox(true);
38990 var maxX = pos.right;
38992 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
38994 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
38996 Roo.each(queue, function(box, k){
38998 Roo.each(box, function(b, kk){
39000 b.el.position('absolute');
39002 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39003 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39005 if(b.size == 'md-left' || b.size == 'md-right'){
39006 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39007 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39010 b.el.setWidth(width);
39011 b.el.setHeight(height);
39019 var positions = [];
39021 switch (box.length){
39023 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39026 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39029 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39032 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39038 Roo.each(box, function(b,kk){
39040 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39042 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39050 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39052 Roo.each(eItems, function(b,k){
39054 b.size = (k == 0) ? 'sm' : 'xs';
39055 b.x = (k == 0) ? 2 : 1;
39056 b.y = (k == 0) ? 2 : 1;
39058 b.el.position('absolute');
39060 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39062 b.el.setWidth(width);
39064 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39066 b.el.setHeight(height);
39070 var positions = [];
39073 x : maxX - this.unitWidth * 2 - this.gutter,
39078 x : maxX - this.unitWidth,
39079 y : minY + (this.unitWidth + this.gutter) * 2
39083 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39087 Roo.each(eItems, function(b,k){
39089 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39095 getVerticalOneBoxColPositions : function(x, y, box)
39099 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39101 if(box[0].size == 'md-left'){
39105 if(box[0].size == 'md-right'){
39110 x : x + (this.unitWidth + this.gutter) * rand,
39117 getVerticalTwoBoxColPositions : function(x, y, box)
39121 if(box[0].size == 'xs'){
39125 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39129 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39143 x : x + (this.unitWidth + this.gutter) * 2,
39144 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39151 getVerticalThreeBoxColPositions : function(x, y, box)
39155 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39163 x : x + (this.unitWidth + this.gutter) * 1,
39168 x : x + (this.unitWidth + this.gutter) * 2,
39176 if(box[0].size == 'xs' && box[1].size == 'xs'){
39185 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39189 x : x + (this.unitWidth + this.gutter) * 1,
39203 x : x + (this.unitWidth + this.gutter) * 2,
39208 x : x + (this.unitWidth + this.gutter) * 2,
39209 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39216 getVerticalFourBoxColPositions : function(x, y, box)
39220 if(box[0].size == 'xs'){
39229 y : y + (this.unitHeight + this.gutter) * 1
39234 y : y + (this.unitHeight + this.gutter) * 2
39238 x : x + (this.unitWidth + this.gutter) * 1,
39252 x : x + (this.unitWidth + this.gutter) * 2,
39257 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39258 y : y + (this.unitHeight + this.gutter) * 1
39262 x : x + (this.unitWidth + this.gutter) * 2,
39263 y : y + (this.unitWidth + this.gutter) * 2
39270 getHorizontalOneBoxColPositions : function(maxX, minY, box)
39274 if(box[0].size == 'md-left'){
39276 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39283 if(box[0].size == 'md-right'){
39285 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39286 y : minY + (this.unitWidth + this.gutter) * 1
39292 var rand = Math.floor(Math.random() * (4 - box[0].y));
39295 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39296 y : minY + (this.unitWidth + this.gutter) * rand
39303 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39307 if(box[0].size == 'xs'){
39310 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39315 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39316 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39324 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39329 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39330 y : minY + (this.unitWidth + this.gutter) * 2
39337 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39341 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39344 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39349 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39350 y : minY + (this.unitWidth + this.gutter) * 1
39354 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39355 y : minY + (this.unitWidth + this.gutter) * 2
39362 if(box[0].size == 'xs' && box[1].size == 'xs'){
39365 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39370 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39375 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39376 y : minY + (this.unitWidth + this.gutter) * 1
39384 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39389 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39390 y : minY + (this.unitWidth + this.gutter) * 2
39394 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39395 y : minY + (this.unitWidth + this.gutter) * 2
39402 getHorizontalFourBoxColPositions : function(maxX, minY, box)
39406 if(box[0].size == 'xs'){
39409 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39414 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39419 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),
39424 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39425 y : minY + (this.unitWidth + this.gutter) * 1
39433 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39438 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39439 y : minY + (this.unitWidth + this.gutter) * 2
39443 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39444 y : minY + (this.unitWidth + this.gutter) * 2
39448 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),
39449 y : minY + (this.unitWidth + this.gutter) * 2
39457 * remove a Masonry Brick
39458 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39460 removeBrick : function(brick_id)
39466 for (var i = 0; i<this.bricks.length; i++) {
39467 if (this.bricks[i].id == brick_id) {
39468 this.bricks.splice(i,1);
39469 this.el.dom.removeChild(Roo.get(brick_id).dom);
39476 * adds a Masonry Brick
39477 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39479 addBrick : function(cfg)
39481 var cn = new Roo.bootstrap.MasonryBrick(cfg);
39482 //this.register(cn);
39483 cn.parentId = this.id;
39484 cn.render(this.el);
39489 * register a Masonry Brick
39490 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39493 register : function(brick)
39495 this.bricks.push(brick);
39496 brick.masonryId = this.id;
39500 * clear all the Masonry Brick
39502 clearAll : function()
39505 //this.getChildContainer().dom.innerHTML = "";
39506 this.el.dom.innerHTML = '';
39509 getSelected : function()
39511 if (!this.selectedBrick) {
39515 return this.selectedBrick;
39519 Roo.apply(Roo.bootstrap.LayoutMasonry, {
39523 * register a Masonry Layout
39524 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
39527 register : function(layout)
39529 this.groups[layout.id] = layout;
39532 * fetch a Masonry Layout based on the masonry layout ID
39533 * @param {string} the masonry layout to add
39534 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
39537 get: function(layout_id) {
39538 if (typeof(this.groups[layout_id]) == 'undefined') {
39541 return this.groups[layout_id] ;
39553 * http://masonry.desandro.com
39555 * The idea is to render all the bricks based on vertical width...
39557 * The original code extends 'outlayer' - we might need to use that....
39563 * @class Roo.bootstrap.LayoutMasonryAuto
39564 * @extends Roo.bootstrap.Component
39565 * Bootstrap Layout Masonry class
39568 * Create a new Element
39569 * @param {Object} config The config object
39572 Roo.bootstrap.LayoutMasonryAuto = function(config){
39573 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
39576 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
39579 * @cfg {Boolean} isFitWidth - resize the width..
39581 isFitWidth : false, // options..
39583 * @cfg {Boolean} isOriginLeft = left align?
39585 isOriginLeft : true,
39587 * @cfg {Boolean} isOriginTop = top align?
39589 isOriginTop : false,
39591 * @cfg {Boolean} isLayoutInstant = no animation?
39593 isLayoutInstant : false, // needed?
39595 * @cfg {Boolean} isResizingContainer = not sure if this is used..
39597 isResizingContainer : true,
39599 * @cfg {Number} columnWidth width of the columns
39605 * @cfg {Number} maxCols maximum number of columns
39610 * @cfg {Number} padHeight padding below box..
39616 * @cfg {Boolean} isAutoInitial defalut true
39619 isAutoInitial : true,
39625 initialColumnWidth : 0,
39626 currentSize : null,
39628 colYs : null, // array.
39635 bricks: null, //CompositeElement
39636 cols : 0, // array?
39637 // element : null, // wrapped now this.el
39638 _isLayoutInited : null,
39641 getAutoCreate : function(){
39645 cls: 'blog-masonary-wrapper ' + this.cls,
39647 cls : 'mas-boxes masonary'
39654 getChildContainer: function( )
39656 if (this.boxesEl) {
39657 return this.boxesEl;
39660 this.boxesEl = this.el.select('.mas-boxes').first();
39662 return this.boxesEl;
39666 initEvents : function()
39670 if(this.isAutoInitial){
39671 Roo.log('hook children rendered');
39672 this.on('childrenrendered', function() {
39673 Roo.log('children rendered');
39680 initial : function()
39682 this.reloadItems();
39684 this.currentSize = this.el.getBox(true);
39686 /// was window resize... - let's see if this works..
39687 Roo.EventManager.onWindowResize(this.resize, this);
39689 if(!this.isAutoInitial){
39694 this.layout.defer(500,this);
39697 reloadItems: function()
39699 this.bricks = this.el.select('.masonry-brick', true);
39701 this.bricks.each(function(b) {
39702 //Roo.log(b.getSize());
39703 if (!b.attr('originalwidth')) {
39704 b.attr('originalwidth', b.getSize().width);
39709 Roo.log(this.bricks.elements.length);
39712 resize : function()
39715 var cs = this.el.getBox(true);
39717 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
39718 Roo.log("no change in with or X");
39721 this.currentSize = cs;
39725 layout : function()
39728 this._resetLayout();
39729 //this._manageStamps();
39731 // don't animate first layout
39732 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
39733 this.layoutItems( isInstant );
39735 // flag for initalized
39736 this._isLayoutInited = true;
39739 layoutItems : function( isInstant )
39741 //var items = this._getItemsForLayout( this.items );
39742 // original code supports filtering layout items.. we just ignore it..
39744 this._layoutItems( this.bricks , isInstant );
39746 this._postLayout();
39748 _layoutItems : function ( items , isInstant)
39750 //this.fireEvent( 'layout', this, items );
39753 if ( !items || !items.elements.length ) {
39754 // no items, emit event with empty array
39759 items.each(function(item) {
39760 Roo.log("layout item");
39762 // get x/y object from method
39763 var position = this._getItemLayoutPosition( item );
39765 position.item = item;
39766 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
39767 queue.push( position );
39770 this._processLayoutQueue( queue );
39772 /** Sets position of item in DOM
39773 * @param {Element} item
39774 * @param {Number} x - horizontal position
39775 * @param {Number} y - vertical position
39776 * @param {Boolean} isInstant - disables transitions
39778 _processLayoutQueue : function( queue )
39780 for ( var i=0, len = queue.length; i < len; i++ ) {
39781 var obj = queue[i];
39782 obj.item.position('absolute');
39783 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
39789 * Any logic you want to do after each layout,
39790 * i.e. size the container
39792 _postLayout : function()
39794 this.resizeContainer();
39797 resizeContainer : function()
39799 if ( !this.isResizingContainer ) {
39802 var size = this._getContainerSize();
39804 this.el.setSize(size.width,size.height);
39805 this.boxesEl.setSize(size.width,size.height);
39811 _resetLayout : function()
39813 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
39814 this.colWidth = this.el.getWidth();
39815 //this.gutter = this.el.getWidth();
39817 this.measureColumns();
39823 this.colYs.push( 0 );
39829 measureColumns : function()
39831 this.getContainerWidth();
39832 // if columnWidth is 0, default to outerWidth of first item
39833 if ( !this.columnWidth ) {
39834 var firstItem = this.bricks.first();
39835 Roo.log(firstItem);
39836 this.columnWidth = this.containerWidth;
39837 if (firstItem && firstItem.attr('originalwidth') ) {
39838 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
39840 // columnWidth fall back to item of first element
39841 Roo.log("set column width?");
39842 this.initialColumnWidth = this.columnWidth ;
39844 // if first elem has no width, default to size of container
39849 if (this.initialColumnWidth) {
39850 this.columnWidth = this.initialColumnWidth;
39855 // column width is fixed at the top - however if container width get's smaller we should
39858 // this bit calcs how man columns..
39860 var columnWidth = this.columnWidth += this.gutter;
39862 // calculate columns
39863 var containerWidth = this.containerWidth + this.gutter;
39865 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
39866 // fix rounding errors, typically with gutters
39867 var excess = columnWidth - containerWidth % columnWidth;
39870 // if overshoot is less than a pixel, round up, otherwise floor it
39871 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
39872 cols = Math[ mathMethod ]( cols );
39873 this.cols = Math.max( cols, 1 );
39874 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
39876 // padding positioning..
39877 var totalColWidth = this.cols * this.columnWidth;
39878 var padavail = this.containerWidth - totalColWidth;
39879 // so for 2 columns - we need 3 'pads'
39881 var padNeeded = (1+this.cols) * this.padWidth;
39883 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
39885 this.columnWidth += padExtra
39886 //this.padWidth = Math.floor(padavail / ( this.cols));
39888 // adjust colum width so that padding is fixed??
39890 // we have 3 columns ... total = width * 3
39891 // we have X left over... that should be used by
39893 //if (this.expandC) {
39901 getContainerWidth : function()
39903 /* // container is parent if fit width
39904 var container = this.isFitWidth ? this.element.parentNode : this.element;
39905 // check that this.size and size are there
39906 // IE8 triggers resize on body size change, so they might not be
39908 var size = getSize( container ); //FIXME
39909 this.containerWidth = size && size.innerWidth; //FIXME
39912 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39916 _getItemLayoutPosition : function( item ) // what is item?
39918 // we resize the item to our columnWidth..
39920 item.setWidth(this.columnWidth);
39921 item.autoBoxAdjust = false;
39923 var sz = item.getSize();
39925 // how many columns does this brick span
39926 var remainder = this.containerWidth % this.columnWidth;
39928 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
39929 // round if off by 1 pixel, otherwise use ceil
39930 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
39931 colSpan = Math.min( colSpan, this.cols );
39933 // normally this should be '1' as we dont' currently allow multi width columns..
39935 var colGroup = this._getColGroup( colSpan );
39936 // get the minimum Y value from the columns
39937 var minimumY = Math.min.apply( Math, colGroup );
39938 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
39940 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
39942 // position the brick
39944 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
39945 y: this.currentSize.y + minimumY + this.padHeight
39949 // apply setHeight to necessary columns
39950 var setHeight = minimumY + sz.height + this.padHeight;
39951 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
39953 var setSpan = this.cols + 1 - colGroup.length;
39954 for ( var i = 0; i < setSpan; i++ ) {
39955 this.colYs[ shortColIndex + i ] = setHeight ;
39962 * @param {Number} colSpan - number of columns the element spans
39963 * @returns {Array} colGroup
39965 _getColGroup : function( colSpan )
39967 if ( colSpan < 2 ) {
39968 // if brick spans only one column, use all the column Ys
39973 // how many different places could this brick fit horizontally
39974 var groupCount = this.cols + 1 - colSpan;
39975 // for each group potential horizontal position
39976 for ( var i = 0; i < groupCount; i++ ) {
39977 // make an array of colY values for that one group
39978 var groupColYs = this.colYs.slice( i, i + colSpan );
39979 // and get the max value of the array
39980 colGroup[i] = Math.max.apply( Math, groupColYs );
39985 _manageStamp : function( stamp )
39987 var stampSize = stamp.getSize();
39988 var offset = stamp.getBox();
39989 // get the columns that this stamp affects
39990 var firstX = this.isOriginLeft ? offset.x : offset.right;
39991 var lastX = firstX + stampSize.width;
39992 var firstCol = Math.floor( firstX / this.columnWidth );
39993 firstCol = Math.max( 0, firstCol );
39995 var lastCol = Math.floor( lastX / this.columnWidth );
39996 // lastCol should not go over if multiple of columnWidth #425
39997 lastCol -= lastX % this.columnWidth ? 0 : 1;
39998 lastCol = Math.min( this.cols - 1, lastCol );
40000 // set colYs to bottom of the stamp
40001 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40004 for ( var i = firstCol; i <= lastCol; i++ ) {
40005 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40010 _getContainerSize : function()
40012 this.maxY = Math.max.apply( Math, this.colYs );
40017 if ( this.isFitWidth ) {
40018 size.width = this._getContainerFitWidth();
40024 _getContainerFitWidth : function()
40026 var unusedCols = 0;
40027 // count unused columns
40030 if ( this.colYs[i] !== 0 ) {
40035 // fit container to columns that have been used
40036 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40039 needsResizeLayout : function()
40041 var previousWidth = this.containerWidth;
40042 this.getContainerWidth();
40043 return previousWidth !== this.containerWidth;
40058 * @class Roo.bootstrap.MasonryBrick
40059 * @extends Roo.bootstrap.Component
40060 * Bootstrap MasonryBrick class
40063 * Create a new MasonryBrick
40064 * @param {Object} config The config object
40067 Roo.bootstrap.MasonryBrick = function(config){
40069 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40071 Roo.bootstrap.MasonryBrick.register(this);
40077 * When a MasonryBrick is clcik
40078 * @param {Roo.bootstrap.MasonryBrick} this
40079 * @param {Roo.EventObject} e
40085 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40088 * @cfg {String} title
40092 * @cfg {String} html
40096 * @cfg {String} bgimage
40100 * @cfg {String} videourl
40104 * @cfg {String} cls
40108 * @cfg {String} href
40112 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40117 * @cfg {String} placetitle (center|bottom)
40122 * @cfg {Boolean} isFitContainer defalut true
40124 isFitContainer : true,
40127 * @cfg {Boolean} preventDefault defalut false
40129 preventDefault : false,
40132 * @cfg {Boolean} inverse defalut false
40134 maskInverse : false,
40136 getAutoCreate : function()
40138 if(!this.isFitContainer){
40139 return this.getSplitAutoCreate();
40142 var cls = 'masonry-brick masonry-brick-full';
40144 if(this.href.length){
40145 cls += ' masonry-brick-link';
40148 if(this.bgimage.length){
40149 cls += ' masonry-brick-image';
40152 if(this.maskInverse){
40153 cls += ' mask-inverse';
40156 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40157 cls += ' enable-mask';
40161 cls += ' masonry-' + this.size + '-brick';
40164 if(this.placetitle.length){
40166 switch (this.placetitle) {
40168 cls += ' masonry-center-title';
40171 cls += ' masonry-bottom-title';
40178 if(!this.html.length && !this.bgimage.length){
40179 cls += ' masonry-center-title';
40182 if(!this.html.length && this.bgimage.length){
40183 cls += ' masonry-bottom-title';
40188 cls += ' ' + this.cls;
40192 tag: (this.href.length) ? 'a' : 'div',
40197 cls: 'masonry-brick-mask'
40201 cls: 'masonry-brick-paragraph',
40207 if(this.href.length){
40208 cfg.href = this.href;
40211 var cn = cfg.cn[1].cn;
40213 if(this.title.length){
40216 cls: 'masonry-brick-title',
40221 if(this.html.length){
40224 cls: 'masonry-brick-text',
40229 if (!this.title.length && !this.html.length) {
40230 cfg.cn[1].cls += ' hide';
40233 if(this.bgimage.length){
40236 cls: 'masonry-brick-image-view',
40241 if(this.videourl.length){
40242 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40243 // youtube support only?
40246 cls: 'masonry-brick-image-view',
40249 allowfullscreen : true
40257 getSplitAutoCreate : function()
40259 var cls = 'masonry-brick masonry-brick-split';
40261 if(this.href.length){
40262 cls += ' masonry-brick-link';
40265 if(this.bgimage.length){
40266 cls += ' masonry-brick-image';
40270 cls += ' masonry-' + this.size + '-brick';
40273 switch (this.placetitle) {
40275 cls += ' masonry-center-title';
40278 cls += ' masonry-bottom-title';
40281 if(!this.bgimage.length){
40282 cls += ' masonry-center-title';
40285 if(this.bgimage.length){
40286 cls += ' masonry-bottom-title';
40292 cls += ' ' + this.cls;
40296 tag: (this.href.length) ? 'a' : 'div',
40301 cls: 'masonry-brick-split-head',
40305 cls: 'masonry-brick-paragraph',
40312 cls: 'masonry-brick-split-body',
40318 if(this.href.length){
40319 cfg.href = this.href;
40322 if(this.title.length){
40323 cfg.cn[0].cn[0].cn.push({
40325 cls: 'masonry-brick-title',
40330 if(this.html.length){
40331 cfg.cn[1].cn.push({
40333 cls: 'masonry-brick-text',
40338 if(this.bgimage.length){
40339 cfg.cn[0].cn.push({
40341 cls: 'masonry-brick-image-view',
40346 if(this.videourl.length){
40347 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40348 // youtube support only?
40349 cfg.cn[0].cn.cn.push({
40351 cls: 'masonry-brick-image-view',
40354 allowfullscreen : true
40361 initEvents: function()
40363 switch (this.size) {
40396 this.el.on('touchstart', this.onTouchStart, this);
40397 this.el.on('touchmove', this.onTouchMove, this);
40398 this.el.on('touchend', this.onTouchEnd, this);
40399 this.el.on('contextmenu', this.onContextMenu, this);
40401 this.el.on('mouseenter' ,this.enter, this);
40402 this.el.on('mouseleave', this.leave, this);
40403 this.el.on('click', this.onClick, this);
40406 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40407 this.parent().bricks.push(this);
40412 onClick: function(e, el)
40414 var time = this.endTimer - this.startTimer;
40415 // Roo.log(e.preventDefault());
40418 e.preventDefault();
40423 if(!this.preventDefault){
40427 e.preventDefault();
40429 if (this.activeClass != '') {
40430 this.selectBrick();
40433 this.fireEvent('click', this, e);
40436 enter: function(e, el)
40438 e.preventDefault();
40440 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40444 if(this.bgimage.length && this.html.length){
40445 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40449 leave: function(e, el)
40451 e.preventDefault();
40453 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40457 if(this.bgimage.length && this.html.length){
40458 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40462 onTouchStart: function(e, el)
40464 // e.preventDefault();
40466 this.touchmoved = false;
40468 if(!this.isFitContainer){
40472 if(!this.bgimage.length || !this.html.length){
40476 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40478 this.timer = new Date().getTime();
40482 onTouchMove: function(e, el)
40484 this.touchmoved = true;
40487 onContextMenu : function(e,el)
40489 e.preventDefault();
40490 e.stopPropagation();
40494 onTouchEnd: function(e, el)
40496 // e.preventDefault();
40498 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
40505 if(!this.bgimage.length || !this.html.length){
40507 if(this.href.length){
40508 window.location.href = this.href;
40514 if(!this.isFitContainer){
40518 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40520 window.location.href = this.href;
40523 //selection on single brick only
40524 selectBrick : function() {
40526 if (!this.parentId) {
40530 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
40531 var index = m.selectedBrick.indexOf(this.id);
40534 m.selectedBrick.splice(index,1);
40535 this.el.removeClass(this.activeClass);
40539 for(var i = 0; i < m.selectedBrick.length; i++) {
40540 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
40541 b.el.removeClass(b.activeClass);
40544 m.selectedBrick = [];
40546 m.selectedBrick.push(this.id);
40547 this.el.addClass(this.activeClass);
40551 isSelected : function(){
40552 return this.el.hasClass(this.activeClass);
40557 Roo.apply(Roo.bootstrap.MasonryBrick, {
40560 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
40562 * register a Masonry Brick
40563 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40566 register : function(brick)
40568 //this.groups[brick.id] = brick;
40569 this.groups.add(brick.id, brick);
40572 * fetch a masonry brick based on the masonry brick ID
40573 * @param {string} the masonry brick to add
40574 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
40577 get: function(brick_id)
40579 // if (typeof(this.groups[brick_id]) == 'undefined') {
40582 // return this.groups[brick_id] ;
40584 if(this.groups.key(brick_id)) {
40585 return this.groups.key(brick_id);
40603 * @class Roo.bootstrap.Brick
40604 * @extends Roo.bootstrap.Component
40605 * Bootstrap Brick class
40608 * Create a new Brick
40609 * @param {Object} config The config object
40612 Roo.bootstrap.Brick = function(config){
40613 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
40619 * When a Brick is click
40620 * @param {Roo.bootstrap.Brick} this
40621 * @param {Roo.EventObject} e
40627 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
40630 * @cfg {String} title
40634 * @cfg {String} html
40638 * @cfg {String} bgimage
40642 * @cfg {String} cls
40646 * @cfg {String} href
40650 * @cfg {String} video
40654 * @cfg {Boolean} square
40658 getAutoCreate : function()
40660 var cls = 'roo-brick';
40662 if(this.href.length){
40663 cls += ' roo-brick-link';
40666 if(this.bgimage.length){
40667 cls += ' roo-brick-image';
40670 if(!this.html.length && !this.bgimage.length){
40671 cls += ' roo-brick-center-title';
40674 if(!this.html.length && this.bgimage.length){
40675 cls += ' roo-brick-bottom-title';
40679 cls += ' ' + this.cls;
40683 tag: (this.href.length) ? 'a' : 'div',
40688 cls: 'roo-brick-paragraph',
40694 if(this.href.length){
40695 cfg.href = this.href;
40698 var cn = cfg.cn[0].cn;
40700 if(this.title.length){
40703 cls: 'roo-brick-title',
40708 if(this.html.length){
40711 cls: 'roo-brick-text',
40718 if(this.bgimage.length){
40721 cls: 'roo-brick-image-view',
40729 initEvents: function()
40731 if(this.title.length || this.html.length){
40732 this.el.on('mouseenter' ,this.enter, this);
40733 this.el.on('mouseleave', this.leave, this);
40736 Roo.EventManager.onWindowResize(this.resize, this);
40738 if(this.bgimage.length){
40739 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
40740 this.imageEl.on('load', this.onImageLoad, this);
40747 onImageLoad : function()
40752 resize : function()
40754 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
40756 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
40758 if(this.bgimage.length){
40759 var image = this.el.select('.roo-brick-image-view', true).first();
40761 image.setWidth(paragraph.getWidth());
40764 image.setHeight(paragraph.getWidth());
40767 this.el.setHeight(image.getHeight());
40768 paragraph.setHeight(image.getHeight());
40774 enter: function(e, el)
40776 e.preventDefault();
40778 if(this.bgimage.length){
40779 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
40780 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
40784 leave: function(e, el)
40786 e.preventDefault();
40788 if(this.bgimage.length){
40789 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
40790 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
40805 * @class Roo.bootstrap.form.NumberField
40806 * @extends Roo.bootstrap.form.Input
40807 * Bootstrap NumberField class
40813 * Create a new NumberField
40814 * @param {Object} config The config object
40817 Roo.bootstrap.form.NumberField = function(config){
40818 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
40821 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
40824 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40826 allowDecimals : true,
40828 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40830 decimalSeparator : ".",
40832 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40834 decimalPrecision : 2,
40836 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40838 allowNegative : true,
40841 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
40845 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40847 minValue : Number.NEGATIVE_INFINITY,
40849 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40851 maxValue : Number.MAX_VALUE,
40853 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40855 minText : "The minimum value for this field is {0}",
40857 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40859 maxText : "The maximum value for this field is {0}",
40861 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
40862 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40864 nanText : "{0} is not a valid number",
40866 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40868 thousandsDelimiter : false,
40870 * @cfg {String} valueAlign alignment of value
40872 valueAlign : "left",
40874 getAutoCreate : function()
40876 var hiddenInput = {
40880 cls: 'hidden-number-input'
40884 hiddenInput.name = this.name;
40889 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
40891 this.name = hiddenInput.name;
40893 if(cfg.cn.length > 0) {
40894 cfg.cn.push(hiddenInput);
40901 initEvents : function()
40903 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
40905 var allowed = "0123456789";
40907 if(this.allowDecimals){
40908 allowed += this.decimalSeparator;
40911 if(this.allowNegative){
40915 if(this.thousandsDelimiter) {
40919 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40921 var keyPress = function(e){
40923 var k = e.getKey();
40925 var c = e.getCharCode();
40928 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40929 allowed.indexOf(String.fromCharCode(c)) === -1
40935 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40939 if(allowed.indexOf(String.fromCharCode(c)) === -1){
40944 this.el.on("keypress", keyPress, this);
40947 validateValue : function(value)
40950 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
40954 var num = this.parseValue(value);
40957 this.markInvalid(String.format(this.nanText, value));
40961 if(num < this.minValue){
40962 this.markInvalid(String.format(this.minText, this.minValue));
40966 if(num > this.maxValue){
40967 this.markInvalid(String.format(this.maxText, this.maxValue));
40974 getValue : function()
40976 var v = this.hiddenEl().getValue();
40978 return this.fixPrecision(this.parseValue(v));
40981 parseValue : function(value)
40983 if(this.thousandsDelimiter) {
40985 r = new RegExp(",", "g");
40986 value = value.replace(r, "");
40989 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40990 return isNaN(value) ? '' : value;
40993 fixPrecision : function(value)
40995 if(this.thousandsDelimiter) {
40997 r = new RegExp(",", "g");
40998 value = value.replace(r, "");
41001 var nan = isNaN(value);
41003 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41004 return nan ? '' : value;
41006 return parseFloat(value).toFixed(this.decimalPrecision);
41009 setValue : function(v)
41011 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41017 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41019 this.inputEl().dom.value = (v == '') ? '' :
41020 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41022 if(!this.allowZero && v === '0') {
41023 this.hiddenEl().dom.value = '';
41024 this.inputEl().dom.value = '';
41031 decimalPrecisionFcn : function(v)
41033 return Math.floor(v);
41036 beforeBlur : function()
41038 var v = this.parseValue(this.getRawValue());
41040 if(v || v === 0 || v === ''){
41045 hiddenEl : function()
41047 return this.el.select('input.hidden-number-input',true).first();
41059 * @class Roo.bootstrap.DocumentSlider
41060 * @extends Roo.bootstrap.Component
41061 * Bootstrap DocumentSlider class
41064 * Create a new DocumentViewer
41065 * @param {Object} config The config object
41068 Roo.bootstrap.DocumentSlider = function(config){
41069 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41076 * Fire after initEvent
41077 * @param {Roo.bootstrap.DocumentSlider} this
41082 * Fire after update
41083 * @param {Roo.bootstrap.DocumentSlider} this
41089 * @param {Roo.bootstrap.DocumentSlider} this
41095 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41101 getAutoCreate : function()
41105 cls : 'roo-document-slider',
41109 cls : 'roo-document-slider-header',
41113 cls : 'roo-document-slider-header-title'
41119 cls : 'roo-document-slider-body',
41123 cls : 'roo-document-slider-prev',
41127 cls : 'fa fa-chevron-left'
41133 cls : 'roo-document-slider-thumb',
41137 cls : 'roo-document-slider-image'
41143 cls : 'roo-document-slider-next',
41147 cls : 'fa fa-chevron-right'
41159 initEvents : function()
41161 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41162 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41164 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41165 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41167 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41168 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41170 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41171 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41173 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41174 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41176 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41177 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41179 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41180 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41182 this.thumbEl.on('click', this.onClick, this);
41184 this.prevIndicator.on('click', this.prev, this);
41186 this.nextIndicator.on('click', this.next, this);
41190 initial : function()
41192 if(this.files.length){
41193 this.indicator = 1;
41197 this.fireEvent('initial', this);
41200 update : function()
41202 this.imageEl.attr('src', this.files[this.indicator - 1]);
41204 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41206 this.prevIndicator.show();
41208 if(this.indicator == 1){
41209 this.prevIndicator.hide();
41212 this.nextIndicator.show();
41214 if(this.indicator == this.files.length){
41215 this.nextIndicator.hide();
41218 this.thumbEl.scrollTo('top');
41220 this.fireEvent('update', this);
41223 onClick : function(e)
41225 e.preventDefault();
41227 this.fireEvent('click', this);
41232 e.preventDefault();
41234 this.indicator = Math.max(1, this.indicator - 1);
41241 e.preventDefault();
41243 this.indicator = Math.min(this.files.length, this.indicator + 1);
41257 * @class Roo.bootstrap.form.RadioSet
41258 * @extends Roo.bootstrap.form.Input
41259 * @children Roo.bootstrap.form.Radio
41260 * Bootstrap RadioSet class
41261 * @cfg {String} indicatorpos (left|right) default left
41262 * @cfg {Boolean} inline (true|false) inline the element (default true)
41263 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41265 * Create a new RadioSet
41266 * @param {Object} config The config object
41269 Roo.bootstrap.form.RadioSet = function(config){
41271 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41275 Roo.bootstrap.form.RadioSet.register(this);
41280 * Fires when the element is checked or unchecked.
41281 * @param {Roo.bootstrap.form.RadioSet} this This radio
41282 * @param {Roo.bootstrap.form.Radio} item The checked item
41287 * Fires when the element is click.
41288 * @param {Roo.bootstrap.form.RadioSet} this This radio set
41289 * @param {Roo.bootstrap.form.Radio} item The checked item
41290 * @param {Roo.EventObject} e The event object
41297 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
41305 indicatorpos : 'left',
41307 getAutoCreate : function()
41311 cls : 'roo-radio-set-label',
41315 html : this.fieldLabel
41319 if (Roo.bootstrap.version == 3) {
41322 if(this.indicatorpos == 'left'){
41325 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41326 tooltip : 'This field is required'
41331 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41332 tooltip : 'This field is required'
41338 cls : 'roo-radio-set-items'
41341 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41343 if (align === 'left' && this.fieldLabel.length) {
41346 cls : "roo-radio-set-right",
41352 if(this.labelWidth > 12){
41353 label.style = "width: " + this.labelWidth + 'px';
41356 if(this.labelWidth < 13 && this.labelmd == 0){
41357 this.labelmd = this.labelWidth;
41360 if(this.labellg > 0){
41361 label.cls += ' col-lg-' + this.labellg;
41362 items.cls += ' col-lg-' + (12 - this.labellg);
41365 if(this.labelmd > 0){
41366 label.cls += ' col-md-' + this.labelmd;
41367 items.cls += ' col-md-' + (12 - this.labelmd);
41370 if(this.labelsm > 0){
41371 label.cls += ' col-sm-' + this.labelsm;
41372 items.cls += ' col-sm-' + (12 - this.labelsm);
41375 if(this.labelxs > 0){
41376 label.cls += ' col-xs-' + this.labelxs;
41377 items.cls += ' col-xs-' + (12 - this.labelxs);
41383 cls : 'roo-radio-set',
41387 cls : 'roo-radio-set-input',
41390 value : this.value ? this.value : ''
41397 if(this.weight.length){
41398 cfg.cls += ' roo-radio-' + this.weight;
41402 cfg.cls += ' roo-radio-set-inline';
41406 ['xs','sm','md','lg'].map(function(size){
41407 if (settings[size]) {
41408 cfg.cls += ' col-' + size + '-' + settings[size];
41416 initEvents : function()
41418 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41419 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41421 if(!this.fieldLabel.length){
41422 this.labelEl.hide();
41425 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41426 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41428 this.indicator = this.indicatorEl();
41430 if(this.indicator){
41431 this.indicator.addClass('invisible');
41434 this.originalValue = this.getValue();
41438 inputEl: function ()
41440 return this.el.select('.roo-radio-set-input', true).first();
41443 getChildContainer : function()
41445 return this.itemsEl;
41448 register : function(item)
41450 this.radioes.push(item);
41454 validate : function()
41456 if(this.getVisibilityEl().hasClass('hidden')){
41462 Roo.each(this.radioes, function(i){
41471 if(this.allowBlank) {
41475 if(this.disabled || valid){
41480 this.markInvalid();
41485 markValid : function()
41487 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41488 this.indicatorEl().removeClass('visible');
41489 this.indicatorEl().addClass('invisible');
41493 if (Roo.bootstrap.version == 3) {
41494 this.el.removeClass([this.invalidClass, this.validClass]);
41495 this.el.addClass(this.validClass);
41497 this.el.removeClass(['is-invalid','is-valid']);
41498 this.el.addClass(['is-valid']);
41500 this.fireEvent('valid', this);
41503 markInvalid : function(msg)
41505 if(this.allowBlank || this.disabled){
41509 if(this.labelEl.isVisible(true) && this.indicatorEl()){
41510 this.indicatorEl().removeClass('invisible');
41511 this.indicatorEl().addClass('visible');
41513 if (Roo.bootstrap.version == 3) {
41514 this.el.removeClass([this.invalidClass, this.validClass]);
41515 this.el.addClass(this.invalidClass);
41517 this.el.removeClass(['is-invalid','is-valid']);
41518 this.el.addClass(['is-invalid']);
41521 this.fireEvent('invalid', this, msg);
41525 setValue : function(v, suppressEvent)
41527 if(this.value === v){
41534 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
41537 Roo.each(this.radioes, function(i){
41539 i.el.removeClass('checked');
41542 Roo.each(this.radioes, function(i){
41544 if(i.value === v || i.value.toString() === v.toString()){
41546 i.el.addClass('checked');
41548 if(suppressEvent !== true){
41549 this.fireEvent('check', this, i);
41560 clearInvalid : function(){
41562 if(!this.el || this.preventMark){
41566 this.el.removeClass([this.invalidClass]);
41568 this.fireEvent('valid', this);
41573 Roo.apply(Roo.bootstrap.form.RadioSet, {
41577 register : function(set)
41579 this.groups[set.name] = set;
41582 get: function(name)
41584 if (typeof(this.groups[name]) == 'undefined') {
41588 return this.groups[name] ;
41594 * Ext JS Library 1.1.1
41595 * Copyright(c) 2006-2007, Ext JS, LLC.
41597 * Originally Released Under LGPL - original licence link has changed is not relivant.
41600 * <script type="text/javascript">
41605 * @class Roo.bootstrap.SplitBar
41606 * @extends Roo.util.Observable
41607 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
41611 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
41612 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
41613 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
41614 split.minSize = 100;
41615 split.maxSize = 600;
41616 split.animate = true;
41617 split.on('moved', splitterMoved);
41620 * Create a new SplitBar
41621 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
41622 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
41623 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41624 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
41625 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
41626 position of the SplitBar).
41628 Roo.bootstrap.SplitBar = function(cfg){
41633 // dragElement : elm
41634 // resizingElement: el,
41636 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
41637 // placement : Roo.bootstrap.SplitBar.LEFT ,
41638 // existingProxy ???
41641 this.el = Roo.get(cfg.dragElement, true);
41642 this.el.dom.unselectable = "on";
41644 this.resizingEl = Roo.get(cfg.resizingElement, true);
41648 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
41649 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
41652 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
41655 * The minimum size of the resizing element. (Defaults to 0)
41661 * The maximum size of the resizing element. (Defaults to 2000)
41664 this.maxSize = 2000;
41667 * Whether to animate the transition to the new size
41670 this.animate = false;
41673 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
41676 this.useShim = false;
41681 if(!cfg.existingProxy){
41683 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
41685 this.proxy = Roo.get(cfg.existingProxy).dom;
41688 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
41691 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
41694 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
41697 this.dragSpecs = {};
41700 * @private The adapter to use to positon and resize elements
41702 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41703 this.adapter.init(this);
41705 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41707 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
41708 this.el.addClass("roo-splitbar-h");
41711 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
41712 this.el.addClass("roo-splitbar-v");
41718 * Fires when the splitter is moved (alias for {@link #event-moved})
41719 * @param {Roo.bootstrap.SplitBar} this
41720 * @param {Number} newSize the new width or height
41725 * Fires when the splitter is moved
41726 * @param {Roo.bootstrap.SplitBar} this
41727 * @param {Number} newSize the new width or height
41731 * @event beforeresize
41732 * Fires before the splitter is dragged
41733 * @param {Roo.bootstrap.SplitBar} this
41735 "beforeresize" : true,
41737 "beforeapply" : true
41740 Roo.util.Observable.call(this);
41743 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
41744 onStartProxyDrag : function(x, y){
41745 this.fireEvent("beforeresize", this);
41747 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
41749 o.enableDisplayMode("block");
41750 // all splitbars share the same overlay
41751 Roo.bootstrap.SplitBar.prototype.overlay = o;
41753 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
41754 this.overlay.show();
41755 Roo.get(this.proxy).setDisplayed("block");
41756 var size = this.adapter.getElementSize(this);
41757 this.activeMinSize = this.getMinimumSize();;
41758 this.activeMaxSize = this.getMaximumSize();;
41759 var c1 = size - this.activeMinSize;
41760 var c2 = Math.max(this.activeMaxSize - size, 0);
41761 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41762 this.dd.resetConstraints();
41763 this.dd.setXConstraint(
41764 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
41765 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
41767 this.dd.setYConstraint(0, 0);
41769 this.dd.resetConstraints();
41770 this.dd.setXConstraint(0, 0);
41771 this.dd.setYConstraint(
41772 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
41773 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
41776 this.dragSpecs.startSize = size;
41777 this.dragSpecs.startPoint = [x, y];
41778 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
41782 * @private Called after the drag operation by the DDProxy
41784 onEndProxyDrag : function(e){
41785 Roo.get(this.proxy).setDisplayed(false);
41786 var endPoint = Roo.lib.Event.getXY(e);
41788 this.overlay.hide();
41791 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41792 newSize = this.dragSpecs.startSize +
41793 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
41794 endPoint[0] - this.dragSpecs.startPoint[0] :
41795 this.dragSpecs.startPoint[0] - endPoint[0]
41798 newSize = this.dragSpecs.startSize +
41799 (this.placement == Roo.bootstrap.SplitBar.TOP ?
41800 endPoint[1] - this.dragSpecs.startPoint[1] :
41801 this.dragSpecs.startPoint[1] - endPoint[1]
41804 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
41805 if(newSize != this.dragSpecs.startSize){
41806 if(this.fireEvent('beforeapply', this, newSize) !== false){
41807 this.adapter.setElementSize(this, newSize);
41808 this.fireEvent("moved", this, newSize);
41809 this.fireEvent("resize", this, newSize);
41815 * Get the adapter this SplitBar uses
41816 * @return The adapter object
41818 getAdapter : function(){
41819 return this.adapter;
41823 * Set the adapter this SplitBar uses
41824 * @param {Object} adapter A SplitBar adapter object
41826 setAdapter : function(adapter){
41827 this.adapter = adapter;
41828 this.adapter.init(this);
41832 * Gets the minimum size for the resizing element
41833 * @return {Number} The minimum size
41835 getMinimumSize : function(){
41836 return this.minSize;
41840 * Sets the minimum size for the resizing element
41841 * @param {Number} minSize The minimum size
41843 setMinimumSize : function(minSize){
41844 this.minSize = minSize;
41848 * Gets the maximum size for the resizing element
41849 * @return {Number} The maximum size
41851 getMaximumSize : function(){
41852 return this.maxSize;
41856 * Sets the maximum size for the resizing element
41857 * @param {Number} maxSize The maximum size
41859 setMaximumSize : function(maxSize){
41860 this.maxSize = maxSize;
41864 * Sets the initialize size for the resizing element
41865 * @param {Number} size The initial size
41867 setCurrentSize : function(size){
41868 var oldAnimate = this.animate;
41869 this.animate = false;
41870 this.adapter.setElementSize(this, size);
41871 this.animate = oldAnimate;
41875 * Destroy this splitbar.
41876 * @param {Boolean} removeEl True to remove the element
41878 destroy : function(removeEl){
41880 this.shim.remove();
41883 this.proxy.parentNode.removeChild(this.proxy);
41891 * @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.
41893 Roo.bootstrap.SplitBar.createProxy = function(dir){
41894 var proxy = new Roo.Element(document.createElement("div"));
41895 proxy.unselectable();
41896 var cls = 'roo-splitbar-proxy';
41897 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
41898 document.body.appendChild(proxy.dom);
41903 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
41904 * Default Adapter. It assumes the splitter and resizing element are not positioned
41905 * elements and only gets/sets the width of the element. Generally used for table based layouts.
41907 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
41910 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
41911 // do nothing for now
41912 init : function(s){
41916 * Called before drag operations to get the current size of the resizing element.
41917 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41919 getElementSize : function(s){
41920 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41921 return s.resizingEl.getWidth();
41923 return s.resizingEl.getHeight();
41928 * Called after drag operations to set the size of the resizing element.
41929 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
41930 * @param {Number} newSize The new size to set
41931 * @param {Function} onComplete A function to be invoked when resizing is complete
41933 setElementSize : function(s, newSize, onComplete){
41934 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
41936 s.resizingEl.setWidth(newSize);
41938 onComplete(s, newSize);
41941 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
41946 s.resizingEl.setHeight(newSize);
41948 onComplete(s, newSize);
41951 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
41958 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
41959 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
41960 * Adapter that moves the splitter element to align with the resized sizing element.
41961 * Used with an absolute positioned SplitBar.
41962 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
41963 * document.body, make sure you assign an id to the body element.
41965 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
41966 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
41967 this.container = Roo.get(container);
41970 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
41971 init : function(s){
41972 this.basic.init(s);
41975 getElementSize : function(s){
41976 return this.basic.getElementSize(s);
41979 setElementSize : function(s, newSize, onComplete){
41980 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
41983 moveSplitter : function(s){
41984 var yes = Roo.bootstrap.SplitBar;
41985 switch(s.placement){
41987 s.el.setX(s.resizingEl.getRight());
41990 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
41993 s.el.setY(s.resizingEl.getBottom());
41996 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42003 * Orientation constant - Create a vertical SplitBar
42007 Roo.bootstrap.SplitBar.VERTICAL = 1;
42010 * Orientation constant - Create a horizontal SplitBar
42014 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42017 * Placement constant - The resizing element is to the left of the splitter element
42021 Roo.bootstrap.SplitBar.LEFT = 1;
42024 * Placement constant - The resizing element is to the right of the splitter element
42028 Roo.bootstrap.SplitBar.RIGHT = 2;
42031 * Placement constant - The resizing element is positioned above the splitter element
42035 Roo.bootstrap.SplitBar.TOP = 3;
42038 * Placement constant - The resizing element is positioned under splitter element
42042 Roo.bootstrap.SplitBar.BOTTOM = 4;
42045 * Ext JS Library 1.1.1
42046 * Copyright(c) 2006-2007, Ext JS, LLC.
42048 * Originally Released Under LGPL - original licence link has changed is not relivant.
42051 * <script type="text/javascript">
42055 * @class Roo.bootstrap.layout.Manager
42056 * @extends Roo.bootstrap.Component
42058 * Base class for layout managers.
42060 Roo.bootstrap.layout.Manager = function(config)
42062 this.monitorWindowResize = true; // do this before we apply configuration.
42064 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42070 /** false to disable window resize monitoring @type Boolean */
42076 * Fires when a layout is performed.
42077 * @param {Roo.LayoutManager} this
42081 * @event regionresized
42082 * Fires when the user resizes a region.
42083 * @param {Roo.LayoutRegion} region The resized region
42084 * @param {Number} newSize The new size (width for east/west, height for north/south)
42086 "regionresized" : true,
42088 * @event regioncollapsed
42089 * Fires when a region is collapsed.
42090 * @param {Roo.LayoutRegion} region The collapsed region
42092 "regioncollapsed" : true,
42094 * @event regionexpanded
42095 * Fires when a region is expanded.
42096 * @param {Roo.LayoutRegion} region The expanded region
42098 "regionexpanded" : true
42100 this.updating = false;
42103 this.el = Roo.get(config.el);
42109 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42114 monitorWindowResize : true,
42120 onRender : function(ct, position)
42123 this.el = Roo.get(ct);
42126 //this.fireEvent('render',this);
42130 initEvents: function()
42134 // ie scrollbar fix
42135 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42136 document.body.scroll = "no";
42137 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42138 this.el.position('relative');
42140 this.id = this.el.id;
42141 this.el.addClass("roo-layout-container");
42142 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42143 if(this.el.dom != document.body ) {
42144 this.el.on('resize', this.layout,this);
42145 this.el.on('show', this.layout,this);
42151 * Returns true if this layout is currently being updated
42152 * @return {Boolean}
42154 isUpdating : function(){
42155 return this.updating;
42159 * Suspend the LayoutManager from doing auto-layouts while
42160 * making multiple add or remove calls
42162 beginUpdate : function(){
42163 this.updating = true;
42167 * Restore auto-layouts and optionally disable the manager from performing a layout
42168 * @param {Boolean} noLayout true to disable a layout update
42170 endUpdate : function(noLayout){
42171 this.updating = false;
42177 layout: function(){
42181 onRegionResized : function(region, newSize){
42182 this.fireEvent("regionresized", region, newSize);
42186 onRegionCollapsed : function(region){
42187 this.fireEvent("regioncollapsed", region);
42190 onRegionExpanded : function(region){
42191 this.fireEvent("regionexpanded", region);
42195 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42196 * performs box-model adjustments.
42197 * @return {Object} The size as an object {width: (the width), height: (the height)}
42199 getViewSize : function()
42202 if(this.el.dom != document.body){
42203 size = this.el.getSize();
42205 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42207 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42208 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42213 * Returns the Element this layout is bound to.
42214 * @return {Roo.Element}
42216 getEl : function(){
42221 * Returns the specified region.
42222 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42223 * @return {Roo.LayoutRegion}
42225 getRegion : function(target){
42226 return this.regions[target.toLowerCase()];
42229 onWindowResize : function(){
42230 if(this.monitorWindowResize){
42237 * Ext JS Library 1.1.1
42238 * Copyright(c) 2006-2007, Ext JS, LLC.
42240 * Originally Released Under LGPL - original licence link has changed is not relivant.
42243 * <script type="text/javascript">
42246 * @class Roo.bootstrap.layout.Border
42247 * @extends Roo.bootstrap.layout.Manager
42248 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42249 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42250 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42251 * please see: examples/bootstrap/nested.html<br><br>
42253 <b>The container the layout is rendered into can be either the body element or any other element.
42254 If it is not the body element, the container needs to either be an absolute positioned element,
42255 or you will need to add "position:relative" to the css of the container. You will also need to specify
42256 the container size if it is not the body element.</b>
42259 * Create a new Border
42260 * @param {Object} config Configuration options
42262 Roo.bootstrap.layout.Border = function(config){
42263 config = config || {};
42264 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42268 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42269 if(config[region]){
42270 config[region].region = region;
42271 this.addRegion(config[region]);
42277 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
42279 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42282 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42285 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42288 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42291 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42294 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42300 parent : false, // this might point to a 'nest' or a ???
42303 * Creates and adds a new region if it doesn't already exist.
42304 * @param {String} target The target region key (north, south, east, west or center).
42305 * @param {Object} config The regions config object
42306 * @return {BorderLayoutRegion} The new region
42308 addRegion : function(config)
42310 if(!this.regions[config.region]){
42311 var r = this.factory(config);
42312 this.bindRegion(r);
42314 return this.regions[config.region];
42318 bindRegion : function(r){
42319 this.regions[r.config.region] = r;
42321 r.on("visibilitychange", this.layout, this);
42322 r.on("paneladded", this.layout, this);
42323 r.on("panelremoved", this.layout, this);
42324 r.on("invalidated", this.layout, this);
42325 r.on("resized", this.onRegionResized, this);
42326 r.on("collapsed", this.onRegionCollapsed, this);
42327 r.on("expanded", this.onRegionExpanded, this);
42331 * Performs a layout update.
42333 layout : function()
42335 if(this.updating) {
42339 // render all the rebions if they have not been done alreayd?
42340 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42341 if(this.regions[region] && !this.regions[region].bodyEl){
42342 this.regions[region].onRender(this.el)
42346 var size = this.getViewSize();
42347 var w = size.width;
42348 var h = size.height;
42353 //var x = 0, y = 0;
42355 var rs = this.regions;
42356 var north = rs["north"];
42357 var south = rs["south"];
42358 var west = rs["west"];
42359 var east = rs["east"];
42360 var center = rs["center"];
42361 //if(this.hideOnLayout){ // not supported anymore
42362 //c.el.setStyle("display", "none");
42364 if(north && north.isVisible()){
42365 var b = north.getBox();
42366 var m = north.getMargins();
42367 b.width = w - (m.left+m.right);
42370 centerY = b.height + b.y + m.bottom;
42371 centerH -= centerY;
42372 north.updateBox(this.safeBox(b));
42374 if(south && south.isVisible()){
42375 var b = south.getBox();
42376 var m = south.getMargins();
42377 b.width = w - (m.left+m.right);
42379 var totalHeight = (b.height + m.top + m.bottom);
42380 b.y = h - totalHeight + m.top;
42381 centerH -= totalHeight;
42382 south.updateBox(this.safeBox(b));
42384 if(west && west.isVisible()){
42385 var b = west.getBox();
42386 var m = west.getMargins();
42387 b.height = centerH - (m.top+m.bottom);
42389 b.y = centerY + m.top;
42390 var totalWidth = (b.width + m.left + m.right);
42391 centerX += totalWidth;
42392 centerW -= totalWidth;
42393 west.updateBox(this.safeBox(b));
42395 if(east && east.isVisible()){
42396 var b = east.getBox();
42397 var m = east.getMargins();
42398 b.height = centerH - (m.top+m.bottom);
42399 var totalWidth = (b.width + m.left + m.right);
42400 b.x = w - totalWidth + m.left;
42401 b.y = centerY + m.top;
42402 centerW -= totalWidth;
42403 east.updateBox(this.safeBox(b));
42406 var m = center.getMargins();
42408 x: centerX + m.left,
42409 y: centerY + m.top,
42410 width: centerW - (m.left+m.right),
42411 height: centerH - (m.top+m.bottom)
42413 //if(this.hideOnLayout){
42414 //center.el.setStyle("display", "block");
42416 center.updateBox(this.safeBox(centerBox));
42419 this.fireEvent("layout", this);
42423 safeBox : function(box){
42424 box.width = Math.max(0, box.width);
42425 box.height = Math.max(0, box.height);
42430 * Adds a ContentPanel (or subclass) to this layout.
42431 * @param {String} target The target region key (north, south, east, west or center).
42432 * @param {Roo.ContentPanel} panel The panel to add
42433 * @return {Roo.ContentPanel} The added panel
42435 add : function(target, panel){
42437 target = target.toLowerCase();
42438 return this.regions[target].add(panel);
42442 * Remove a ContentPanel (or subclass) to this layout.
42443 * @param {String} target The target region key (north, south, east, west or center).
42444 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42445 * @return {Roo.ContentPanel} The removed panel
42447 remove : function(target, panel){
42448 target = target.toLowerCase();
42449 return this.regions[target].remove(panel);
42453 * Searches all regions for a panel with the specified id
42454 * @param {String} panelId
42455 * @return {Roo.ContentPanel} The panel or null if it wasn't found
42457 findPanel : function(panelId){
42458 var rs = this.regions;
42459 for(var target in rs){
42460 if(typeof rs[target] != "function"){
42461 var p = rs[target].getPanel(panelId);
42471 * Searches all regions for a panel with the specified id and activates (shows) it.
42472 * @param {String/ContentPanel} panelId The panels id or the panel itself
42473 * @return {Roo.ContentPanel} The shown panel or null
42475 showPanel : function(panelId) {
42476 var rs = this.regions;
42477 for(var target in rs){
42478 var r = rs[target];
42479 if(typeof r != "function"){
42480 if(r.hasPanel(panelId)){
42481 return r.showPanel(panelId);
42489 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42490 * @param {Roo.state.Provider} provider (optional) An alternate state provider
42493 restoreState : function(provider){
42495 provider = Roo.state.Manager;
42497 var sm = new Roo.LayoutStateManager();
42498 sm.init(this, provider);
42504 * Adds a xtype elements to the layout.
42508 xtype : 'ContentPanel',
42515 xtype : 'NestedLayoutPanel',
42521 items : [ ... list of content panels or nested layout panels.. ]
42525 * @param {Object} cfg Xtype definition of item to add.
42527 addxtype : function(cfg)
42529 // basically accepts a pannel...
42530 // can accept a layout region..!?!?
42531 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
42534 // theory? children can only be panels??
42536 //if (!cfg.xtype.match(/Panel$/)) {
42541 if (typeof(cfg.region) == 'undefined') {
42542 Roo.log("Failed to add Panel, region was not set");
42546 var region = cfg.region;
42552 xitems = cfg.items;
42557 if ( region == 'center') {
42558 Roo.log("Center: " + cfg.title);
42564 case 'Content': // ContentPanel (el, cfg)
42565 case 'Scroll': // ContentPanel (el, cfg)
42567 cfg.autoCreate = cfg.autoCreate || true;
42568 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42570 // var el = this.el.createChild();
42571 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42574 this.add(region, ret);
42578 case 'TreePanel': // our new panel!
42579 cfg.el = this.el.createChild();
42580 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42581 this.add(region, ret);
42586 // create a new Layout (which is a Border Layout...
42588 var clayout = cfg.layout;
42589 clayout.el = this.el.createChild();
42590 clayout.items = clayout.items || [];
42594 // replace this exitems with the clayout ones..
42595 xitems = clayout.items;
42597 // force background off if it's in center...
42598 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42599 cfg.background = false;
42601 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
42604 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42605 //console.log('adding nested layout panel ' + cfg.toSource());
42606 this.add(region, ret);
42607 nb = {}; /// find first...
42612 // needs grid and region
42614 //var el = this.getRegion(region).el.createChild();
42616 *var el = this.el.createChild();
42617 // create the grid first...
42618 cfg.grid.container = el;
42619 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
42622 if (region == 'center' && this.active ) {
42623 cfg.background = false;
42626 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
42628 this.add(region, ret);
42630 if (cfg.background) {
42631 // render grid on panel activation (if panel background)
42632 ret.on('activate', function(gp) {
42633 if (!gp.grid.rendered) {
42634 // gp.grid.render(el);
42638 // cfg.grid.render(el);
42644 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
42645 // it was the old xcomponent building that caused this before.
42646 // espeically if border is the top element in the tree.
42656 if (typeof(Roo[cfg.xtype]) != 'undefined') {
42658 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42659 this.add(region, ret);
42663 throw "Can not add '" + cfg.xtype + "' to Border";
42669 this.beginUpdate();
42673 Roo.each(xitems, function(i) {
42674 region = nb && i.region ? i.region : false;
42676 var add = ret.addxtype(i);
42679 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
42680 if (!i.background) {
42681 abn[region] = nb[region] ;
42688 // make the last non-background panel active..
42689 //if (nb) { Roo.log(abn); }
42692 for(var r in abn) {
42693 region = this.getRegion(r);
42695 // tried using nb[r], but it does not work..
42697 region.showPanel(abn[r]);
42708 factory : function(cfg)
42711 var validRegions = Roo.bootstrap.layout.Border.regions;
42713 var target = cfg.region;
42716 var r = Roo.bootstrap.layout;
42720 return new r.North(cfg);
42722 return new r.South(cfg);
42724 return new r.East(cfg);
42726 return new r.West(cfg);
42728 return new r.Center(cfg);
42730 throw 'Layout region "'+target+'" not supported.';
42737 * Ext JS Library 1.1.1
42738 * Copyright(c) 2006-2007, Ext JS, LLC.
42740 * Originally Released Under LGPL - original licence link has changed is not relivant.
42743 * <script type="text/javascript">
42747 * @class Roo.bootstrap.layout.Basic
42748 * @extends Roo.util.Observable
42749 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42750 * and does not have a titlebar, tabs or any other features. All it does is size and position
42751 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42752 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
42753 * @cfg {string} region the region that it inhabits..
42754 * @cfg {bool} skipConfig skip config?
42758 Roo.bootstrap.layout.Basic = function(config){
42760 this.mgr = config.mgr;
42762 this.position = config.region;
42764 var skipConfig = config.skipConfig;
42768 * @scope Roo.BasicLayoutRegion
42772 * @event beforeremove
42773 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42774 * @param {Roo.LayoutRegion} this
42775 * @param {Roo.ContentPanel} panel The panel
42776 * @param {Object} e The cancel event object
42778 "beforeremove" : true,
42780 * @event invalidated
42781 * Fires when the layout for this region is changed.
42782 * @param {Roo.LayoutRegion} this
42784 "invalidated" : true,
42786 * @event visibilitychange
42787 * Fires when this region is shown or hidden
42788 * @param {Roo.LayoutRegion} this
42789 * @param {Boolean} visibility true or false
42791 "visibilitychange" : true,
42793 * @event paneladded
42794 * Fires when a panel is added.
42795 * @param {Roo.LayoutRegion} this
42796 * @param {Roo.ContentPanel} panel The panel
42798 "paneladded" : true,
42800 * @event panelremoved
42801 * Fires when a panel is removed.
42802 * @param {Roo.LayoutRegion} this
42803 * @param {Roo.ContentPanel} panel The panel
42805 "panelremoved" : true,
42807 * @event beforecollapse
42808 * Fires when this region before collapse.
42809 * @param {Roo.LayoutRegion} this
42811 "beforecollapse" : true,
42814 * Fires when this region is collapsed.
42815 * @param {Roo.LayoutRegion} this
42817 "collapsed" : true,
42820 * Fires when this region is expanded.
42821 * @param {Roo.LayoutRegion} this
42826 * Fires when this region is slid into view.
42827 * @param {Roo.LayoutRegion} this
42829 "slideshow" : true,
42832 * Fires when this region slides out of view.
42833 * @param {Roo.LayoutRegion} this
42835 "slidehide" : true,
42837 * @event panelactivated
42838 * Fires when a panel is activated.
42839 * @param {Roo.LayoutRegion} this
42840 * @param {Roo.ContentPanel} panel The activated panel
42842 "panelactivated" : true,
42845 * Fires when the user resizes this region.
42846 * @param {Roo.LayoutRegion} this
42847 * @param {Number} newSize The new size (width for east/west, height for north/south)
42851 /** A collection of panels in this region. @type Roo.util.MixedCollection */
42852 this.panels = new Roo.util.MixedCollection();
42853 this.panels.getKey = this.getPanelId.createDelegate(this);
42855 this.activePanel = null;
42856 // ensure listeners are added...
42858 if (config.listeners || config.events) {
42859 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
42860 listeners : config.listeners || {},
42861 events : config.events || {}
42865 if(skipConfig !== true){
42866 this.applyConfig(config);
42870 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
42872 getPanelId : function(p){
42876 applyConfig : function(config){
42877 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
42878 this.config = config;
42883 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
42884 * the width, for horizontal (north, south) the height.
42885 * @param {Number} newSize The new width or height
42887 resizeTo : function(newSize){
42888 var el = this.el ? this.el :
42889 (this.activePanel ? this.activePanel.getEl() : null);
42891 switch(this.position){
42894 el.setWidth(newSize);
42895 this.fireEvent("resized", this, newSize);
42899 el.setHeight(newSize);
42900 this.fireEvent("resized", this, newSize);
42906 getBox : function(){
42907 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
42910 getMargins : function(){
42911 return this.margins;
42914 updateBox : function(box){
42916 var el = this.activePanel.getEl();
42917 el.dom.style.left = box.x + "px";
42918 el.dom.style.top = box.y + "px";
42919 this.activePanel.setSize(box.width, box.height);
42923 * Returns the container element for this region.
42924 * @return {Roo.Element}
42926 getEl : function(){
42927 return this.activePanel;
42931 * Returns true if this region is currently visible.
42932 * @return {Boolean}
42934 isVisible : function(){
42935 return this.activePanel ? true : false;
42938 setActivePanel : function(panel){
42939 panel = this.getPanel(panel);
42940 if(this.activePanel && this.activePanel != panel){
42941 this.activePanel.setActiveState(false);
42942 this.activePanel.getEl().setLeftTop(-10000,-10000);
42944 this.activePanel = panel;
42945 panel.setActiveState(true);
42947 panel.setSize(this.box.width, this.box.height);
42949 this.fireEvent("panelactivated", this, panel);
42950 this.fireEvent("invalidated");
42954 * Show the specified panel.
42955 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
42956 * @return {Roo.ContentPanel} The shown panel or null
42958 showPanel : function(panel){
42959 panel = this.getPanel(panel);
42961 this.setActivePanel(panel);
42967 * Get the active panel for this region.
42968 * @return {Roo.ContentPanel} The active panel or null
42970 getActivePanel : function(){
42971 return this.activePanel;
42975 * Add the passed ContentPanel(s)
42976 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42977 * @return {Roo.ContentPanel} The panel added (if only one was added)
42979 add : function(panel){
42980 if(arguments.length > 1){
42981 for(var i = 0, len = arguments.length; i < len; i++) {
42982 this.add(arguments[i]);
42986 if(this.hasPanel(panel)){
42987 this.showPanel(panel);
42990 var el = panel.getEl();
42991 if(el.dom.parentNode != this.mgr.el.dom){
42992 this.mgr.el.dom.appendChild(el.dom);
42994 if(panel.setRegion){
42995 panel.setRegion(this);
42997 this.panels.add(panel);
42998 el.setStyle("position", "absolute");
42999 if(!panel.background){
43000 this.setActivePanel(panel);
43001 if(this.config.initialSize && this.panels.getCount()==1){
43002 this.resizeTo(this.config.initialSize);
43005 this.fireEvent("paneladded", this, panel);
43010 * Returns true if the panel is in this region.
43011 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43012 * @return {Boolean}
43014 hasPanel : function(panel){
43015 if(typeof panel == "object"){ // must be panel obj
43016 panel = panel.getId();
43018 return this.getPanel(panel) ? true : false;
43022 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43023 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43024 * @param {Boolean} preservePanel Overrides the config preservePanel option
43025 * @return {Roo.ContentPanel} The panel that was removed
43027 remove : function(panel, preservePanel){
43028 panel = this.getPanel(panel);
43033 this.fireEvent("beforeremove", this, panel, e);
43034 if(e.cancel === true){
43037 var panelId = panel.getId();
43038 this.panels.removeKey(panelId);
43043 * Returns the panel specified or null if it's not in this region.
43044 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43045 * @return {Roo.ContentPanel}
43047 getPanel : function(id){
43048 if(typeof id == "object"){ // must be panel obj
43051 return this.panels.get(id);
43055 * Returns this regions position (north/south/east/west/center).
43058 getPosition: function(){
43059 return this.position;
43063 * Ext JS Library 1.1.1
43064 * Copyright(c) 2006-2007, Ext JS, LLC.
43066 * Originally Released Under LGPL - original licence link has changed is not relivant.
43069 * <script type="text/javascript">
43073 * @class Roo.bootstrap.layout.Region
43074 * @extends Roo.bootstrap.layout.Basic
43075 * This class represents a region in a layout manager.
43077 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43078 * @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})
43079 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43080 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43081 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43082 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43083 * @cfg {String} title The title for the region (overrides panel titles)
43084 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43085 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43086 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43087 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43088 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43089 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43090 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43091 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43092 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43093 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43095 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43096 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43097 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43098 * @cfg {Number} width For East/West panels
43099 * @cfg {Number} height For North/South panels
43100 * @cfg {Boolean} split To show the splitter
43101 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43103 * @cfg {string} cls Extra CSS classes to add to region
43105 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43106 * @cfg {string} region the region that it inhabits..
43109 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43110 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43112 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43113 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43114 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43116 Roo.bootstrap.layout.Region = function(config)
43118 this.applyConfig(config);
43120 var mgr = config.mgr;
43121 var pos = config.region;
43122 config.skipConfig = true;
43123 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43126 this.onRender(mgr.el);
43129 this.visible = true;
43130 this.collapsed = false;
43131 this.unrendered_panels = [];
43134 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43136 position: '', // set by wrapper (eg. north/south etc..)
43137 unrendered_panels : null, // unrendered panels.
43139 tabPosition : false,
43141 mgr: false, // points to 'Border'
43144 createBody : function(){
43145 /** This region's body element
43146 * @type Roo.Element */
43147 this.bodyEl = this.el.createChild({
43149 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43153 onRender: function(ctr, pos)
43155 var dh = Roo.DomHelper;
43156 /** This region's container element
43157 * @type Roo.Element */
43158 this.el = dh.append(ctr.dom, {
43160 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43162 /** This region's title element
43163 * @type Roo.Element */
43165 this.titleEl = dh.append(this.el.dom, {
43167 unselectable: "on",
43168 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43170 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43171 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43175 this.titleEl.enableDisplayMode();
43176 /** This region's title text element
43177 * @type HTMLElement */
43178 this.titleTextEl = this.titleEl.dom.firstChild;
43179 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43181 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43182 this.closeBtn.enableDisplayMode();
43183 this.closeBtn.on("click", this.closeClicked, this);
43184 this.closeBtn.hide();
43186 this.createBody(this.config);
43187 if(this.config.hideWhenEmpty){
43189 this.on("paneladded", this.validateVisibility, this);
43190 this.on("panelremoved", this.validateVisibility, this);
43192 if(this.autoScroll){
43193 this.bodyEl.setStyle("overflow", "auto");
43195 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43197 //if(c.titlebar !== false){
43198 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43199 this.titleEl.hide();
43201 this.titleEl.show();
43202 if(this.config.title){
43203 this.titleTextEl.innerHTML = this.config.title;
43207 if(this.config.collapsed){
43208 this.collapse(true);
43210 if(this.config.hidden){
43214 if (this.unrendered_panels && this.unrendered_panels.length) {
43215 for (var i =0;i< this.unrendered_panels.length; i++) {
43216 this.add(this.unrendered_panels[i]);
43218 this.unrendered_panels = null;
43224 applyConfig : function(c)
43227 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43228 var dh = Roo.DomHelper;
43229 if(c.titlebar !== false){
43230 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43231 this.collapseBtn.on("click", this.collapse, this);
43232 this.collapseBtn.enableDisplayMode();
43234 if(c.showPin === true || this.showPin){
43235 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43236 this.stickBtn.enableDisplayMode();
43237 this.stickBtn.on("click", this.expand, this);
43238 this.stickBtn.hide();
43243 /** This region's collapsed element
43244 * @type Roo.Element */
43247 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43248 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43251 if(c.floatable !== false){
43252 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43253 this.collapsedEl.on("click", this.collapseClick, this);
43256 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43257 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43258 id: "message", unselectable: "on", style:{"float":"left"}});
43259 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43261 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43262 this.expandBtn.on("click", this.expand, this);
43266 if(this.collapseBtn){
43267 this.collapseBtn.setVisible(c.collapsible == true);
43270 this.cmargins = c.cmargins || this.cmargins ||
43271 (this.position == "west" || this.position == "east" ?
43272 {top: 0, left: 2, right:2, bottom: 0} :
43273 {top: 2, left: 0, right:0, bottom: 2});
43275 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43278 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43280 this.autoScroll = c.autoScroll || false;
43285 this.duration = c.duration || .30;
43286 this.slideDuration = c.slideDuration || .45;
43291 * Returns true if this region is currently visible.
43292 * @return {Boolean}
43294 isVisible : function(){
43295 return this.visible;
43299 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43300 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
43302 //setCollapsedTitle : function(title){
43303 // title = title || " ";
43304 // if(this.collapsedTitleTextEl){
43305 // this.collapsedTitleTextEl.innerHTML = title;
43309 getBox : function(){
43311 // if(!this.collapsed){
43312 b = this.el.getBox(false, true);
43314 // b = this.collapsedEl.getBox(false, true);
43319 getMargins : function(){
43320 return this.margins;
43321 //return this.collapsed ? this.cmargins : this.margins;
43324 highlight : function(){
43325 this.el.addClass("x-layout-panel-dragover");
43328 unhighlight : function(){
43329 this.el.removeClass("x-layout-panel-dragover");
43332 updateBox : function(box)
43334 if (!this.bodyEl) {
43335 return; // not rendered yet..
43339 if(!this.collapsed){
43340 this.el.dom.style.left = box.x + "px";
43341 this.el.dom.style.top = box.y + "px";
43342 this.updateBody(box.width, box.height);
43344 this.collapsedEl.dom.style.left = box.x + "px";
43345 this.collapsedEl.dom.style.top = box.y + "px";
43346 this.collapsedEl.setSize(box.width, box.height);
43349 this.tabs.autoSizeTabs();
43353 updateBody : function(w, h)
43356 this.el.setWidth(w);
43357 w -= this.el.getBorderWidth("rl");
43358 if(this.config.adjustments){
43359 w += this.config.adjustments[0];
43362 if(h !== null && h > 0){
43363 this.el.setHeight(h);
43364 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43365 h -= this.el.getBorderWidth("tb");
43366 if(this.config.adjustments){
43367 h += this.config.adjustments[1];
43369 this.bodyEl.setHeight(h);
43371 h = this.tabs.syncHeight(h);
43374 if(this.panelSize){
43375 w = w !== null ? w : this.panelSize.width;
43376 h = h !== null ? h : this.panelSize.height;
43378 if(this.activePanel){
43379 var el = this.activePanel.getEl();
43380 w = w !== null ? w : el.getWidth();
43381 h = h !== null ? h : el.getHeight();
43382 this.panelSize = {width: w, height: h};
43383 this.activePanel.setSize(w, h);
43385 if(Roo.isIE && this.tabs){
43386 this.tabs.el.repaint();
43391 * Returns the container element for this region.
43392 * @return {Roo.Element}
43394 getEl : function(){
43399 * Hides this region.
43402 //if(!this.collapsed){
43403 this.el.dom.style.left = "-2000px";
43406 // this.collapsedEl.dom.style.left = "-2000px";
43407 // this.collapsedEl.hide();
43409 this.visible = false;
43410 this.fireEvent("visibilitychange", this, false);
43414 * Shows this region if it was previously hidden.
43417 //if(!this.collapsed){
43420 // this.collapsedEl.show();
43422 this.visible = true;
43423 this.fireEvent("visibilitychange", this, true);
43426 closeClicked : function(){
43427 if(this.activePanel){
43428 this.remove(this.activePanel);
43432 collapseClick : function(e){
43434 e.stopPropagation();
43437 e.stopPropagation();
43443 * Collapses this region.
43444 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43447 collapse : function(skipAnim, skipCheck = false){
43448 if(this.collapsed) {
43452 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43454 this.collapsed = true;
43456 this.split.el.hide();
43458 if(this.config.animate && skipAnim !== true){
43459 this.fireEvent("invalidated", this);
43460 this.animateCollapse();
43462 this.el.setLocation(-20000,-20000);
43464 this.collapsedEl.show();
43465 this.fireEvent("collapsed", this);
43466 this.fireEvent("invalidated", this);
43472 animateCollapse : function(){
43477 * Expands this region if it was previously collapsed.
43478 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43479 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43482 expand : function(e, skipAnim){
43484 e.stopPropagation();
43486 if(!this.collapsed || this.el.hasActiveFx()) {
43490 this.afterSlideIn();
43493 this.collapsed = false;
43494 if(this.config.animate && skipAnim !== true){
43495 this.animateExpand();
43499 this.split.el.show();
43501 this.collapsedEl.setLocation(-2000,-2000);
43502 this.collapsedEl.hide();
43503 this.fireEvent("invalidated", this);
43504 this.fireEvent("expanded", this);
43508 animateExpand : function(){
43512 initTabs : function()
43514 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
43516 var ts = new Roo.bootstrap.panel.Tabs({
43517 el: this.bodyEl.dom,
43519 tabPosition: this.tabPosition ? this.tabPosition : 'top',
43520 disableTooltips: this.config.disableTabTips,
43521 toolbar : this.config.toolbar
43524 if(this.config.hideTabs){
43525 ts.stripWrap.setDisplayed(false);
43528 ts.resizeTabs = this.config.resizeTabs === true;
43529 ts.minTabWidth = this.config.minTabWidth || 40;
43530 ts.maxTabWidth = this.config.maxTabWidth || 250;
43531 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43532 ts.monitorResize = false;
43533 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
43534 ts.bodyEl.addClass('roo-layout-tabs-body');
43535 this.panels.each(this.initPanelAsTab, this);
43538 initPanelAsTab : function(panel){
43539 var ti = this.tabs.addTab(
43543 this.config.closeOnTab && panel.isClosable(),
43546 if(panel.tabTip !== undefined){
43547 ti.setTooltip(panel.tabTip);
43549 ti.on("activate", function(){
43550 this.setActivePanel(panel);
43553 if(this.config.closeOnTab){
43554 ti.on("beforeclose", function(t, e){
43556 this.remove(panel);
43560 panel.tabItem = ti;
43565 updatePanelTitle : function(panel, title)
43567 if(this.activePanel == panel){
43568 this.updateTitle(title);
43571 var ti = this.tabs.getTab(panel.getEl().id);
43573 if(panel.tabTip !== undefined){
43574 ti.setTooltip(panel.tabTip);
43579 updateTitle : function(title){
43580 if(this.titleTextEl && !this.config.title){
43581 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
43585 setActivePanel : function(panel)
43587 panel = this.getPanel(panel);
43588 if(this.activePanel && this.activePanel != panel){
43589 if(this.activePanel.setActiveState(false) === false){
43593 this.activePanel = panel;
43594 panel.setActiveState(true);
43595 if(this.panelSize){
43596 panel.setSize(this.panelSize.width, this.panelSize.height);
43599 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43601 this.updateTitle(panel.getTitle());
43603 this.fireEvent("invalidated", this);
43605 this.fireEvent("panelactivated", this, panel);
43609 * Shows the specified panel.
43610 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43611 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43613 showPanel : function(panel)
43615 panel = this.getPanel(panel);
43618 var tab = this.tabs.getTab(panel.getEl().id);
43619 if(tab.isHidden()){
43620 this.tabs.unhideTab(tab.id);
43624 this.setActivePanel(panel);
43631 * Get the active panel for this region.
43632 * @return {Roo.ContentPanel} The active panel or null
43634 getActivePanel : function(){
43635 return this.activePanel;
43638 validateVisibility : function(){
43639 if(this.panels.getCount() < 1){
43640 this.updateTitle(" ");
43641 this.closeBtn.hide();
43644 if(!this.isVisible()){
43651 * Adds the passed ContentPanel(s) to this region.
43652 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43653 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43655 add : function(panel)
43657 if(arguments.length > 1){
43658 for(var i = 0, len = arguments.length; i < len; i++) {
43659 this.add(arguments[i]);
43664 // if we have not been rendered yet, then we can not really do much of this..
43665 if (!this.bodyEl) {
43666 this.unrendered_panels.push(panel);
43673 if(this.hasPanel(panel)){
43674 this.showPanel(panel);
43677 panel.setRegion(this);
43678 this.panels.add(panel);
43679 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43680 // sinle panel - no tab...?? would it not be better to render it with the tabs,
43681 // and hide them... ???
43682 this.bodyEl.dom.appendChild(panel.getEl().dom);
43683 if(panel.background !== true){
43684 this.setActivePanel(panel);
43686 this.fireEvent("paneladded", this, panel);
43693 this.initPanelAsTab(panel);
43697 if(panel.background !== true){
43698 this.tabs.activate(panel.getEl().id);
43700 this.fireEvent("paneladded", this, panel);
43705 * Hides the tab for the specified panel.
43706 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43708 hidePanel : function(panel){
43709 if(this.tabs && (panel = this.getPanel(panel))){
43710 this.tabs.hideTab(panel.getEl().id);
43715 * Unhides the tab for a previously hidden panel.
43716 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43718 unhidePanel : function(panel){
43719 if(this.tabs && (panel = this.getPanel(panel))){
43720 this.tabs.unhideTab(panel.getEl().id);
43724 clearPanels : function(){
43725 while(this.panels.getCount() > 0){
43726 this.remove(this.panels.first());
43731 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43732 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43733 * @param {Boolean} preservePanel Overrides the config preservePanel option
43734 * @return {Roo.ContentPanel} The panel that was removed
43736 remove : function(panel, preservePanel)
43738 panel = this.getPanel(panel);
43743 this.fireEvent("beforeremove", this, panel, e);
43744 if(e.cancel === true){
43747 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43748 var panelId = panel.getId();
43749 this.panels.removeKey(panelId);
43751 document.body.appendChild(panel.getEl().dom);
43754 this.tabs.removeTab(panel.getEl().id);
43755 }else if (!preservePanel){
43756 this.bodyEl.dom.removeChild(panel.getEl().dom);
43758 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43759 var p = this.panels.first();
43760 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43761 tempEl.appendChild(p.getEl().dom);
43762 this.bodyEl.update("");
43763 this.bodyEl.dom.appendChild(p.getEl().dom);
43765 this.updateTitle(p.getTitle());
43767 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43768 this.setActivePanel(p);
43770 panel.setRegion(null);
43771 if(this.activePanel == panel){
43772 this.activePanel = null;
43774 if(this.config.autoDestroy !== false && preservePanel !== true){
43775 try{panel.destroy();}catch(e){}
43777 this.fireEvent("panelremoved", this, panel);
43782 * Returns the TabPanel component used by this region
43783 * @return {Roo.TabPanel}
43785 getTabs : function(){
43789 createTool : function(parentEl, className){
43790 var btn = Roo.DomHelper.append(parentEl, {
43792 cls: "x-layout-tools-button",
43795 cls: "roo-layout-tools-button-inner " + className,
43799 btn.addClassOnOver("roo-layout-tools-button-over");
43804 * Ext JS Library 1.1.1
43805 * Copyright(c) 2006-2007, Ext JS, LLC.
43807 * Originally Released Under LGPL - original licence link has changed is not relivant.
43810 * <script type="text/javascript">
43816 * @class Roo.SplitLayoutRegion
43817 * @extends Roo.LayoutRegion
43818 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43820 Roo.bootstrap.layout.Split = function(config){
43821 this.cursor = config.cursor;
43822 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
43825 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
43827 splitTip : "Drag to resize.",
43828 collapsibleSplitTip : "Drag to resize. Double click to hide.",
43829 useSplitTips : false,
43831 applyConfig : function(config){
43832 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
43835 onRender : function(ctr,pos) {
43837 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
43838 if(!this.config.split){
43843 var splitEl = Roo.DomHelper.append(ctr.dom, {
43845 id: this.el.id + "-split",
43846 cls: "roo-layout-split roo-layout-split-"+this.position,
43849 /** The SplitBar for this region
43850 * @type Roo.SplitBar */
43851 // does not exist yet...
43852 Roo.log([this.position, this.orientation]);
43854 this.split = new Roo.bootstrap.SplitBar({
43855 dragElement : splitEl,
43856 resizingElement: this.el,
43857 orientation : this.orientation
43860 this.split.on("moved", this.onSplitMove, this);
43861 this.split.useShim = this.config.useShim === true;
43862 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43863 if(this.useSplitTips){
43864 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43866 //if(config.collapsible){
43867 // this.split.el.on("dblclick", this.collapse, this);
43870 if(typeof this.config.minSize != "undefined"){
43871 this.split.minSize = this.config.minSize;
43873 if(typeof this.config.maxSize != "undefined"){
43874 this.split.maxSize = this.config.maxSize;
43876 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
43877 this.hideSplitter();
43882 getHMaxSize : function(){
43883 var cmax = this.config.maxSize || 10000;
43884 var center = this.mgr.getRegion("center");
43885 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43888 getVMaxSize : function(){
43889 var cmax = this.config.maxSize || 10000;
43890 var center = this.mgr.getRegion("center");
43891 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43894 onSplitMove : function(split, newSize){
43895 this.fireEvent("resized", this, newSize);
43899 * Returns the {@link Roo.SplitBar} for this region.
43900 * @return {Roo.SplitBar}
43902 getSplitBar : function(){
43907 this.hideSplitter();
43908 Roo.bootstrap.layout.Split.superclass.hide.call(this);
43911 hideSplitter : function(){
43913 this.split.el.setLocation(-2000,-2000);
43914 this.split.el.hide();
43920 this.split.el.show();
43922 Roo.bootstrap.layout.Split.superclass.show.call(this);
43925 beforeSlide: function(){
43926 if(Roo.isGecko){// firefox overflow auto bug workaround
43927 this.bodyEl.clip();
43929 this.tabs.bodyEl.clip();
43931 if(this.activePanel){
43932 this.activePanel.getEl().clip();
43934 if(this.activePanel.beforeSlide){
43935 this.activePanel.beforeSlide();
43941 afterSlide : function(){
43942 if(Roo.isGecko){// firefox overflow auto bug workaround
43943 this.bodyEl.unclip();
43945 this.tabs.bodyEl.unclip();
43947 if(this.activePanel){
43948 this.activePanel.getEl().unclip();
43949 if(this.activePanel.afterSlide){
43950 this.activePanel.afterSlide();
43956 initAutoHide : function(){
43957 if(this.autoHide !== false){
43958 if(!this.autoHideHd){
43959 var st = new Roo.util.DelayedTask(this.slideIn, this);
43960 this.autoHideHd = {
43961 "mouseout": function(e){
43962 if(!e.within(this.el, true)){
43966 "mouseover" : function(e){
43972 this.el.on(this.autoHideHd);
43976 clearAutoHide : function(){
43977 if(this.autoHide !== false){
43978 this.el.un("mouseout", this.autoHideHd.mouseout);
43979 this.el.un("mouseover", this.autoHideHd.mouseover);
43983 clearMonitor : function(){
43984 Roo.get(document).un("click", this.slideInIf, this);
43987 // these names are backwards but not changed for compat
43988 slideOut : function(){
43989 if(this.isSlid || this.el.hasActiveFx()){
43992 this.isSlid = true;
43993 if(this.collapseBtn){
43994 this.collapseBtn.hide();
43996 this.closeBtnState = this.closeBtn.getStyle('display');
43997 this.closeBtn.hide();
43999 this.stickBtn.show();
44002 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44003 this.beforeSlide();
44004 this.el.setStyle("z-index", 10001);
44005 this.el.slideIn(this.getSlideAnchor(), {
44006 callback: function(){
44008 this.initAutoHide();
44009 Roo.get(document).on("click", this.slideInIf, this);
44010 this.fireEvent("slideshow", this);
44017 afterSlideIn : function(){
44018 this.clearAutoHide();
44019 this.isSlid = false;
44020 this.clearMonitor();
44021 this.el.setStyle("z-index", "");
44022 if(this.collapseBtn){
44023 this.collapseBtn.show();
44025 this.closeBtn.setStyle('display', this.closeBtnState);
44027 this.stickBtn.hide();
44029 this.fireEvent("slidehide", this);
44032 slideIn : function(cb){
44033 if(!this.isSlid || this.el.hasActiveFx()){
44037 this.isSlid = false;
44038 this.beforeSlide();
44039 this.el.slideOut(this.getSlideAnchor(), {
44040 callback: function(){
44041 this.el.setLeftTop(-10000, -10000);
44043 this.afterSlideIn();
44051 slideInIf : function(e){
44052 if(!e.within(this.el)){
44057 animateCollapse : function(){
44058 this.beforeSlide();
44059 this.el.setStyle("z-index", 20000);
44060 var anchor = this.getSlideAnchor();
44061 this.el.slideOut(anchor, {
44062 callback : function(){
44063 this.el.setStyle("z-index", "");
44064 this.collapsedEl.slideIn(anchor, {duration:.3});
44066 this.el.setLocation(-10000,-10000);
44068 this.fireEvent("collapsed", this);
44075 animateExpand : function(){
44076 this.beforeSlide();
44077 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44078 this.el.setStyle("z-index", 20000);
44079 this.collapsedEl.hide({
44082 this.el.slideIn(this.getSlideAnchor(), {
44083 callback : function(){
44084 this.el.setStyle("z-index", "");
44087 this.split.el.show();
44089 this.fireEvent("invalidated", this);
44090 this.fireEvent("expanded", this);
44118 getAnchor : function(){
44119 return this.anchors[this.position];
44122 getCollapseAnchor : function(){
44123 return this.canchors[this.position];
44126 getSlideAnchor : function(){
44127 return this.sanchors[this.position];
44130 getAlignAdj : function(){
44131 var cm = this.cmargins;
44132 switch(this.position){
44148 getExpandAdj : function(){
44149 var c = this.collapsedEl, cm = this.cmargins;
44150 switch(this.position){
44152 return [-(cm.right+c.getWidth()+cm.left), 0];
44155 return [cm.right+c.getWidth()+cm.left, 0];
44158 return [0, -(cm.top+cm.bottom+c.getHeight())];
44161 return [0, cm.top+cm.bottom+c.getHeight()];
44167 * Ext JS Library 1.1.1
44168 * Copyright(c) 2006-2007, Ext JS, LLC.
44170 * Originally Released Under LGPL - original licence link has changed is not relivant.
44173 * <script type="text/javascript">
44176 * These classes are private internal classes
44178 Roo.bootstrap.layout.Center = function(config){
44179 config.region = "center";
44180 Roo.bootstrap.layout.Region.call(this, config);
44181 this.visible = true;
44182 this.minWidth = config.minWidth || 20;
44183 this.minHeight = config.minHeight || 20;
44186 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44188 // center panel can't be hidden
44192 // center panel can't be hidden
44195 getMinWidth: function(){
44196 return this.minWidth;
44199 getMinHeight: function(){
44200 return this.minHeight;
44214 Roo.bootstrap.layout.North = function(config)
44216 config.region = 'north';
44217 config.cursor = 'n-resize';
44219 Roo.bootstrap.layout.Split.call(this, config);
44223 this.split.placement = Roo.bootstrap.SplitBar.TOP;
44224 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44225 this.split.el.addClass("roo-layout-split-v");
44227 //var size = config.initialSize || config.height;
44228 //if(this.el && typeof size != "undefined"){
44229 // this.el.setHeight(size);
44232 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44234 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44237 onRender : function(ctr, pos)
44239 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44240 var size = this.config.initialSize || this.config.height;
44241 if(this.el && typeof size != "undefined"){
44242 this.el.setHeight(size);
44247 getBox : function(){
44248 if(this.collapsed){
44249 return this.collapsedEl.getBox();
44251 var box = this.el.getBox();
44253 box.height += this.split.el.getHeight();
44258 updateBox : function(box){
44259 if(this.split && !this.collapsed){
44260 box.height -= this.split.el.getHeight();
44261 this.split.el.setLeft(box.x);
44262 this.split.el.setTop(box.y+box.height);
44263 this.split.el.setWidth(box.width);
44265 if(this.collapsed){
44266 this.updateBody(box.width, null);
44268 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44276 Roo.bootstrap.layout.South = function(config){
44277 config.region = 'south';
44278 config.cursor = 's-resize';
44279 Roo.bootstrap.layout.Split.call(this, config);
44281 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44282 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44283 this.split.el.addClass("roo-layout-split-v");
44288 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44289 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44291 onRender : function(ctr, pos)
44293 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44294 var size = this.config.initialSize || this.config.height;
44295 if(this.el && typeof size != "undefined"){
44296 this.el.setHeight(size);
44301 getBox : function(){
44302 if(this.collapsed){
44303 return this.collapsedEl.getBox();
44305 var box = this.el.getBox();
44307 var sh = this.split.el.getHeight();
44314 updateBox : function(box){
44315 if(this.split && !this.collapsed){
44316 var sh = this.split.el.getHeight();
44319 this.split.el.setLeft(box.x);
44320 this.split.el.setTop(box.y-sh);
44321 this.split.el.setWidth(box.width);
44323 if(this.collapsed){
44324 this.updateBody(box.width, null);
44326 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44330 Roo.bootstrap.layout.East = function(config){
44331 config.region = "east";
44332 config.cursor = "e-resize";
44333 Roo.bootstrap.layout.Split.call(this, config);
44335 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44336 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44337 this.split.el.addClass("roo-layout-split-h");
44341 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44342 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44344 onRender : function(ctr, pos)
44346 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44347 var size = this.config.initialSize || this.config.width;
44348 if(this.el && typeof size != "undefined"){
44349 this.el.setWidth(size);
44354 getBox : function(){
44355 if(this.collapsed){
44356 return this.collapsedEl.getBox();
44358 var box = this.el.getBox();
44360 var sw = this.split.el.getWidth();
44367 updateBox : function(box){
44368 if(this.split && !this.collapsed){
44369 var sw = this.split.el.getWidth();
44371 this.split.el.setLeft(box.x);
44372 this.split.el.setTop(box.y);
44373 this.split.el.setHeight(box.height);
44376 if(this.collapsed){
44377 this.updateBody(null, box.height);
44379 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44383 Roo.bootstrap.layout.West = function(config){
44384 config.region = "west";
44385 config.cursor = "w-resize";
44387 Roo.bootstrap.layout.Split.call(this, config);
44389 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44390 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44391 this.split.el.addClass("roo-layout-split-h");
44395 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44396 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44398 onRender: function(ctr, pos)
44400 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44401 var size = this.config.initialSize || this.config.width;
44402 if(typeof size != "undefined"){
44403 this.el.setWidth(size);
44407 getBox : function(){
44408 if(this.collapsed){
44409 return this.collapsedEl.getBox();
44411 var box = this.el.getBox();
44412 if (box.width == 0) {
44413 box.width = this.config.width; // kludge?
44416 box.width += this.split.el.getWidth();
44421 updateBox : function(box){
44422 if(this.split && !this.collapsed){
44423 var sw = this.split.el.getWidth();
44425 this.split.el.setLeft(box.x+box.width);
44426 this.split.el.setTop(box.y);
44427 this.split.el.setHeight(box.height);
44429 if(this.collapsed){
44430 this.updateBody(null, box.height);
44432 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44436 * Ext JS Library 1.1.1
44437 * Copyright(c) 2006-2007, Ext JS, LLC.
44439 * Originally Released Under LGPL - original licence link has changed is not relivant.
44442 * <script type="text/javascript">
44445 * @class Roo.bootstrap.paenl.Content
44446 * @extends Roo.util.Observable
44447 * @children Roo.bootstrap.Component
44448 * @parent builder Roo.bootstrap.layout.Border
44449 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44450 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
44451 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
44452 * @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
44453 * @cfg {Boolean} closable True if the panel can be closed/removed
44454 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44455 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44456 * @cfg {Toolbar} toolbar A toolbar for this panel
44457 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44458 * @cfg {String} title The title for this panel
44459 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44460 * @cfg {String} url Calls {@link #setUrl} with this value
44461 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44462 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44463 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44464 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
44465 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
44466 * @cfg {Boolean} badges render the badges
44467 * @cfg {String} cls extra classes to use
44468 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44471 * Create a new ContentPanel.
44472 * @param {String/Object} config A string to set only the title or a config object
44475 Roo.bootstrap.panel.Content = function( config){
44477 this.tpl = config.tpl || false;
44479 var el = config.el;
44480 var content = config.content;
44482 if(config.autoCreate){ // xtype is available if this is called from factory
44485 this.el = Roo.get(el);
44486 if(!this.el && config && config.autoCreate){
44487 if(typeof config.autoCreate == "object"){
44488 if(!config.autoCreate.id){
44489 config.autoCreate.id = config.id||el;
44491 this.el = Roo.DomHelper.append(document.body,
44492 config.autoCreate, true);
44496 cls: (config.cls || '') +
44497 (config.background ? ' bg-' + config.background : '') +
44498 " roo-layout-inactive-content",
44501 if (config.iframe) {
44505 style : 'border: 0px',
44506 src : 'about:blank'
44512 elcfg.html = config.html;
44516 this.el = Roo.DomHelper.append(document.body, elcfg , true);
44517 if (config.iframe) {
44518 this.iframeEl = this.el.select('iframe',true).first();
44523 this.closable = false;
44524 this.loaded = false;
44525 this.active = false;
44528 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
44530 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
44532 this.wrapEl = this.el; //this.el.wrap();
44534 if (config.toolbar.items) {
44535 ti = config.toolbar.items ;
44536 delete config.toolbar.items ;
44540 this.toolbar.render(this.wrapEl, 'before');
44541 for(var i =0;i < ti.length;i++) {
44542 // Roo.log(['add child', items[i]]);
44543 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
44545 this.toolbar.items = nitems;
44546 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
44547 delete config.toolbar;
44551 // xtype created footer. - not sure if will work as we normally have to render first..
44552 if (this.footer && !this.footer.el && this.footer.xtype) {
44553 if (!this.wrapEl) {
44554 this.wrapEl = this.el.wrap();
44557 this.footer.container = this.wrapEl.createChild();
44559 this.footer = Roo.factory(this.footer, Roo);
44564 if(typeof config == "string"){
44565 this.title = config;
44567 Roo.apply(this, config);
44571 this.resizeEl = Roo.get(this.resizeEl, true);
44573 this.resizeEl = this.el;
44575 // handle view.xtype
44583 * Fires when this panel is activated.
44584 * @param {Roo.ContentPanel} this
44588 * @event deactivate
44589 * Fires when this panel is activated.
44590 * @param {Roo.ContentPanel} this
44592 "deactivate" : true,
44596 * Fires when this panel is resized if fitToFrame is true.
44597 * @param {Roo.ContentPanel} this
44598 * @param {Number} width The width after any component adjustments
44599 * @param {Number} height The height after any component adjustments
44605 * Fires when this tab is created
44606 * @param {Roo.ContentPanel} this
44612 * Fires when this content is scrolled
44613 * @param {Roo.ContentPanel} this
44614 * @param {Event} scrollEvent
44625 if(this.autoScroll && !this.iframe){
44626 this.resizeEl.setStyle("overflow", "auto");
44627 this.resizeEl.on('scroll', this.onScroll, this);
44629 // fix randome scrolling
44630 //this.el.on('scroll', function() {
44631 // Roo.log('fix random scolling');
44632 // this.scrollTo('top',0);
44635 content = content || this.content;
44637 this.setContent(content);
44639 if(config && config.url){
44640 this.setUrl(this.url, this.params, this.loadOnce);
44645 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
44647 if (this.view && typeof(this.view.xtype) != 'undefined') {
44648 this.view.el = this.el.appendChild(document.createElement("div"));
44649 this.view = Roo.factory(this.view);
44650 this.view.render && this.view.render(false, '');
44654 this.fireEvent('render', this);
44657 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
44667 /* Resize Element - use this to work out scroll etc. */
44670 setRegion : function(region){
44671 this.region = region;
44672 this.setActiveClass(region && !this.background);
44676 setActiveClass: function(state)
44679 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
44680 this.el.setStyle('position','relative');
44682 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
44683 this.el.setStyle('position', 'absolute');
44688 * Returns the toolbar for this Panel if one was configured.
44689 * @return {Roo.Toolbar}
44691 getToolbar : function(){
44692 return this.toolbar;
44695 setActiveState : function(active)
44697 this.active = active;
44698 this.setActiveClass(active);
44700 if(this.fireEvent("deactivate", this) === false){
44705 this.fireEvent("activate", this);
44709 * Updates this panel's element (not for iframe)
44710 * @param {String} content The new content
44711 * @param {Boolean} loadScripts (optional) true to look for and process scripts
44713 setContent : function(content, loadScripts){
44718 this.el.update(content, loadScripts);
44721 ignoreResize : function(w, h)
44723 //return false; // always resize?
44724 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44727 this.lastSize = {width: w, height: h};
44732 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44733 * @return {Roo.UpdateManager} The UpdateManager
44735 getUpdateManager : function(){
44739 return this.el.getUpdateManager();
44742 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44743 * Does not work with IFRAME contents
44744 * @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:
44747 url: "your-url.php",
44748 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44749 callback: yourFunction,
44750 scope: yourObject, //(optional scope)
44753 text: "Loading...",
44759 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44760 * 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.
44761 * @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}
44762 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44763 * @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.
44764 * @return {Roo.ContentPanel} this
44772 var um = this.el.getUpdateManager();
44773 um.update.apply(um, arguments);
44779 * 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.
44780 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44781 * @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)
44782 * @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)
44783 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
44785 setUrl : function(url, params, loadOnce){
44787 this.iframeEl.dom.src = url;
44791 if(this.refreshDelegate){
44792 this.removeListener("activate", this.refreshDelegate);
44794 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44795 this.on("activate", this.refreshDelegate);
44796 return this.el.getUpdateManager();
44799 _handleRefresh : function(url, params, loadOnce){
44800 if(!loadOnce || !this.loaded){
44801 var updater = this.el.getUpdateManager();
44802 updater.update(url, params, this._setLoaded.createDelegate(this));
44806 _setLoaded : function(){
44807 this.loaded = true;
44811 * Returns this panel's id
44814 getId : function(){
44819 * Returns this panel's element - used by regiosn to add.
44820 * @return {Roo.Element}
44822 getEl : function(){
44823 return this.wrapEl || this.el;
44828 adjustForComponents : function(width, height)
44830 //Roo.log('adjustForComponents ');
44831 if(this.resizeEl != this.el){
44832 width -= this.el.getFrameWidth('lr');
44833 height -= this.el.getFrameWidth('tb');
44836 var te = this.toolbar.getEl();
44837 te.setWidth(width);
44838 height -= te.getHeight();
44841 var te = this.footer.getEl();
44842 te.setWidth(width);
44843 height -= te.getHeight();
44847 if(this.adjustments){
44848 width += this.adjustments[0];
44849 height += this.adjustments[1];
44851 return {"width": width, "height": height};
44854 setSize : function(width, height){
44855 if(this.fitToFrame && !this.ignoreResize(width, height)){
44856 if(this.fitContainer && this.resizeEl != this.el){
44857 this.el.setSize(width, height);
44859 var size = this.adjustForComponents(width, height);
44861 this.iframeEl.setSize(width,height);
44864 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44865 this.fireEvent('resize', this, size.width, size.height);
44872 * Returns this panel's title
44875 getTitle : function(){
44877 if (typeof(this.title) != 'object') {
44882 for (var k in this.title) {
44883 if (!this.title.hasOwnProperty(k)) {
44887 if (k.indexOf('-') >= 0) {
44888 var s = k.split('-');
44889 for (var i = 0; i<s.length; i++) {
44890 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
44893 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
44900 * Set this panel's title
44901 * @param {String} title
44903 setTitle : function(title){
44904 this.title = title;
44906 this.region.updatePanelTitle(this, title);
44911 * Returns true is this panel was configured to be closable
44912 * @return {Boolean}
44914 isClosable : function(){
44915 return this.closable;
44918 beforeSlide : function(){
44920 this.resizeEl.clip();
44923 afterSlide : function(){
44925 this.resizeEl.unclip();
44929 * Force a content refresh from the URL specified in the {@link #setUrl} method.
44930 * Will fail silently if the {@link #setUrl} method has not been called.
44931 * This does not activate the panel, just updates its content.
44933 refresh : function(){
44934 if(this.refreshDelegate){
44935 this.loaded = false;
44936 this.refreshDelegate();
44941 * Destroys this panel
44943 destroy : function(){
44944 this.el.removeAllListeners();
44945 var tempEl = document.createElement("span");
44946 tempEl.appendChild(this.el.dom);
44947 tempEl.innerHTML = "";
44953 * form - if the content panel contains a form - this is a reference to it.
44954 * @type {Roo.form.Form}
44958 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
44959 * This contains a reference to it.
44965 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44975 * @param {Object} cfg Xtype definition of item to add.
44979 getChildContainer: function () {
44980 return this.getEl();
44984 onScroll : function(e)
44986 this.fireEvent('scroll', this, e);
44991 var ret = new Roo.factory(cfg);
44996 if (cfg.xtype.match(/^Form$/)) {
44999 //if (this.footer) {
45000 // el = this.footer.container.insertSibling(false, 'before');
45002 el = this.el.createChild();
45005 this.form = new Roo.form.Form(cfg);
45008 if ( this.form.allItems.length) {
45009 this.form.render(el.dom);
45013 // should only have one of theses..
45014 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45015 // views.. should not be just added - used named prop 'view''
45017 cfg.el = this.el.appendChild(document.createElement("div"));
45020 var ret = new Roo.factory(cfg);
45022 ret.render && ret.render(false, ''); // render blank..
45032 * @class Roo.bootstrap.panel.Grid
45033 * @extends Roo.bootstrap.panel.Content
45035 * Create a new GridPanel.
45036 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45037 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45038 * @param {Object} config A the config object
45044 Roo.bootstrap.panel.Grid = function(config)
45048 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45049 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45051 config.el = this.wrapper;
45052 //this.el = this.wrapper;
45054 if (config.container) {
45055 // ctor'ed from a Border/panel.grid
45058 this.wrapper.setStyle("overflow", "hidden");
45059 this.wrapper.addClass('roo-grid-container');
45064 if(config.toolbar){
45065 var tool_el = this.wrapper.createChild();
45066 this.toolbar = Roo.factory(config.toolbar);
45068 if (config.toolbar.items) {
45069 ti = config.toolbar.items ;
45070 delete config.toolbar.items ;
45074 this.toolbar.render(tool_el);
45075 for(var i =0;i < ti.length;i++) {
45076 // Roo.log(['add child', items[i]]);
45077 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45079 this.toolbar.items = nitems;
45081 delete config.toolbar;
45084 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45085 config.grid.scrollBody = true;;
45086 config.grid.monitorWindowResize = false; // turn off autosizing
45087 config.grid.autoHeight = false;
45088 config.grid.autoWidth = false;
45090 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45092 if (config.background) {
45093 // render grid on panel activation (if panel background)
45094 this.on('activate', function(gp) {
45095 if (!gp.grid.rendered) {
45096 gp.grid.render(this.wrapper);
45097 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45102 this.grid.render(this.wrapper);
45103 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45106 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45107 // ??? needed ??? config.el = this.wrapper;
45112 // xtype created footer. - not sure if will work as we normally have to render first..
45113 if (this.footer && !this.footer.el && this.footer.xtype) {
45115 var ctr = this.grid.getView().getFooterPanel(true);
45116 this.footer.dataSource = this.grid.dataSource;
45117 this.footer = Roo.factory(this.footer, Roo);
45118 this.footer.render(ctr);
45128 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45131 getId : function(){
45132 return this.grid.id;
45136 * Returns the grid for this panel
45137 * @return {Roo.bootstrap.Table}
45139 getGrid : function(){
45143 setSize : function(width, height)
45146 //if(!this.ignoreResize(width, height)){
45147 var grid = this.grid;
45148 var size = this.adjustForComponents(width, height);
45149 // tfoot is not a footer?
45152 var gridel = grid.getGridEl();
45153 gridel.setSize(size.width, size.height);
45155 var tbd = grid.getGridEl().select('tbody', true).first();
45156 var thd = grid.getGridEl().select('thead',true).first();
45157 var tbf= grid.getGridEl().select('tfoot', true).first();
45160 size.height -= tbf.getHeight();
45163 size.height -= thd.getHeight();
45166 tbd.setSize(size.width, size.height );
45167 // this is for the account management tab -seems to work there.
45168 var thd = grid.getGridEl().select('thead',true).first();
45170 // tbd.setSize(size.width, size.height - thd.getHeight());
45180 beforeSlide : function(){
45181 this.grid.getView().scroller.clip();
45184 afterSlide : function(){
45185 this.grid.getView().scroller.unclip();
45188 destroy : function(){
45189 this.grid.destroy();
45191 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45196 * @class Roo.bootstrap.panel.Nest
45197 * @extends Roo.bootstrap.panel.Content
45199 * Create a new Panel, that can contain a layout.Border.
45202 * @param {String/Object} config A string to set only the title or a config object
45204 Roo.bootstrap.panel.Nest = function(config)
45206 // construct with only one argument..
45207 /* FIXME - implement nicer consturctors
45208 if (layout.layout) {
45210 layout = config.layout;
45211 delete config.layout;
45213 if (layout.xtype && !layout.getEl) {
45214 // then layout needs constructing..
45215 layout = Roo.factory(layout, Roo);
45219 config.el = config.layout.getEl();
45221 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45223 config.layout.monitorWindowResize = false; // turn off autosizing
45224 this.layout = config.layout;
45225 this.layout.getEl().addClass("roo-layout-nested-layout");
45226 this.layout.parent = this;
45233 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45235 * @cfg {Roo.BorderLayout} layout The layout for this panel
45239 setSize : function(width, height){
45240 if(!this.ignoreResize(width, height)){
45241 var size = this.adjustForComponents(width, height);
45242 var el = this.layout.getEl();
45243 if (size.height < 1) {
45244 el.setWidth(size.width);
45246 el.setSize(size.width, size.height);
45248 var touch = el.dom.offsetWidth;
45249 this.layout.layout();
45250 // ie requires a double layout on the first pass
45251 if(Roo.isIE && !this.initialized){
45252 this.initialized = true;
45253 this.layout.layout();
45258 // activate all subpanels if not currently active..
45260 setActiveState : function(active){
45261 this.active = active;
45262 this.setActiveClass(active);
45265 this.fireEvent("deactivate", this);
45269 this.fireEvent("activate", this);
45270 // not sure if this should happen before or after..
45271 if (!this.layout) {
45272 return; // should not happen..
45275 for (var r in this.layout.regions) {
45276 reg = this.layout.getRegion(r);
45277 if (reg.getActivePanel()) {
45278 //reg.showPanel(reg.getActivePanel()); // force it to activate..
45279 reg.setActivePanel(reg.getActivePanel());
45282 if (!reg.panels.length) {
45285 reg.showPanel(reg.getPanel(0));
45294 * Returns the nested BorderLayout for this panel
45295 * @return {Roo.BorderLayout}
45297 getLayout : function(){
45298 return this.layout;
45302 * Adds a xtype elements to the layout of the nested panel
45306 xtype : 'ContentPanel',
45313 xtype : 'NestedLayoutPanel',
45319 items : [ ... list of content panels or nested layout panels.. ]
45323 * @param {Object} cfg Xtype definition of item to add.
45325 addxtype : function(cfg) {
45326 return this.layout.addxtype(cfg);
45331 * Ext JS Library 1.1.1
45332 * Copyright(c) 2006-2007, Ext JS, LLC.
45334 * Originally Released Under LGPL - original licence link has changed is not relivant.
45337 * <script type="text/javascript">
45340 * @class Roo.TabPanel
45341 * @extends Roo.util.Observable
45342 * A lightweight tab container.
45346 // basic tabs 1, built from existing content
45347 var tabs = new Roo.TabPanel("tabs1");
45348 tabs.addTab("script", "View Script");
45349 tabs.addTab("markup", "View Markup");
45350 tabs.activate("script");
45352 // more advanced tabs, built from javascript
45353 var jtabs = new Roo.TabPanel("jtabs");
45354 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45356 // set up the UpdateManager
45357 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45358 var updater = tab2.getUpdateManager();
45359 updater.setDefaultUrl("ajax1.htm");
45360 tab2.on('activate', updater.refresh, updater, true);
45362 // Use setUrl for Ajax loading
45363 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45364 tab3.setUrl("ajax2.htm", null, true);
45367 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45370 jtabs.activate("jtabs-1");
45373 * Create a new TabPanel.
45374 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45375 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45377 Roo.bootstrap.panel.Tabs = function(config){
45379 * The container element for this TabPanel.
45380 * @type Roo.Element
45382 this.el = Roo.get(config.el);
45385 if(typeof config == "boolean"){
45386 this.tabPosition = config ? "bottom" : "top";
45388 Roo.apply(this, config);
45392 if(this.tabPosition == "bottom"){
45393 // if tabs are at the bottom = create the body first.
45394 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45395 this.el.addClass("roo-tabs-bottom");
45397 // next create the tabs holders
45399 if (this.tabPosition == "west"){
45401 var reg = this.region; // fake it..
45403 if (!reg.mgr.parent) {
45406 reg = reg.mgr.parent.region;
45408 Roo.log("got nest?");
45410 if (reg.mgr.getRegion('west')) {
45411 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45412 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45413 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45414 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45415 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45423 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45424 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45425 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45426 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45431 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45434 // finally - if tabs are at the top, then create the body last..
45435 if(this.tabPosition != "bottom"){
45436 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45437 * @type Roo.Element
45439 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45440 this.el.addClass("roo-tabs-top");
45444 this.bodyEl.setStyle("position", "relative");
45446 this.active = null;
45447 this.activateDelegate = this.activate.createDelegate(this);
45452 * Fires when the active tab changes
45453 * @param {Roo.TabPanel} this
45454 * @param {Roo.TabPanelItem} activePanel The new active tab
45458 * @event beforetabchange
45459 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45460 * @param {Roo.TabPanel} this
45461 * @param {Object} e Set cancel to true on this object to cancel the tab change
45462 * @param {Roo.TabPanelItem} tab The tab being changed to
45464 "beforetabchange" : true
45467 Roo.EventManager.onWindowResize(this.onResize, this);
45468 this.cpad = this.el.getPadding("lr");
45469 this.hiddenCount = 0;
45472 // toolbar on the tabbar support...
45473 if (this.toolbar) {
45474 alert("no toolbar support yet");
45475 this.toolbar = false;
45477 var tcfg = this.toolbar;
45478 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
45479 this.toolbar = new Roo.Toolbar(tcfg);
45480 if (Roo.isSafari) {
45481 var tbl = tcfg.container.child('table', true);
45482 tbl.setAttribute('width', '100%');
45490 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
45493 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
45495 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
45497 tabPosition : "top",
45499 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
45501 currentTabWidth : 0,
45503 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
45507 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
45511 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
45513 preferredTabWidth : 175,
45515 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
45517 resizeTabs : false,
45519 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
45521 monitorResize : true,
45523 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
45525 toolbar : false, // set by caller..
45527 region : false, /// set by caller
45529 disableTooltips : true, // not used yet...
45532 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
45533 * @param {String} id The id of the div to use <b>or create</b>
45534 * @param {String} text The text for the tab
45535 * @param {String} content (optional) Content to put in the TabPanelItem body
45536 * @param {Boolean} closable (optional) True to create a close icon on the tab
45537 * @return {Roo.TabPanelItem} The created TabPanelItem
45539 addTab : function(id, text, content, closable, tpl)
45541 var item = new Roo.bootstrap.panel.TabItem({
45545 closable : closable,
45548 this.addTabItem(item);
45550 item.setContent(content);
45556 * Returns the {@link Roo.TabPanelItem} with the specified id/index
45557 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
45558 * @return {Roo.TabPanelItem}
45560 getTab : function(id){
45561 return this.items[id];
45565 * Hides the {@link Roo.TabPanelItem} with the specified id/index
45566 * @param {String/Number} id The id or index of the TabPanelItem to hide.
45568 hideTab : function(id){
45569 var t = this.items[id];
45572 this.hiddenCount++;
45573 this.autoSizeTabs();
45578 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
45579 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
45581 unhideTab : function(id){
45582 var t = this.items[id];
45584 t.setHidden(false);
45585 this.hiddenCount--;
45586 this.autoSizeTabs();
45591 * Adds an existing {@link Roo.TabPanelItem}.
45592 * @param {Roo.TabPanelItem} item The TabPanelItem to add
45594 addTabItem : function(item)
45596 this.items[item.id] = item;
45597 this.items.push(item);
45598 this.autoSizeTabs();
45599 // if(this.resizeTabs){
45600 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
45601 // this.autoSizeTabs();
45603 // item.autoSize();
45608 * Removes a {@link Roo.TabPanelItem}.
45609 * @param {String/Number} id The id or index of the TabPanelItem to remove.
45611 removeTab : function(id){
45612 var items = this.items;
45613 var tab = items[id];
45614 if(!tab) { return; }
45615 var index = items.indexOf(tab);
45616 if(this.active == tab && items.length > 1){
45617 var newTab = this.getNextAvailable(index);
45622 this.stripEl.dom.removeChild(tab.pnode.dom);
45623 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
45624 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
45626 items.splice(index, 1);
45627 delete this.items[tab.id];
45628 tab.fireEvent("close", tab);
45629 tab.purgeListeners();
45630 this.autoSizeTabs();
45633 getNextAvailable : function(start){
45634 var items = this.items;
45636 // look for a next tab that will slide over to
45637 // replace the one being removed
45638 while(index < items.length){
45639 var item = items[++index];
45640 if(item && !item.isHidden()){
45644 // if one isn't found select the previous tab (on the left)
45647 var item = items[--index];
45648 if(item && !item.isHidden()){
45656 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
45657 * @param {String/Number} id The id or index of the TabPanelItem to disable.
45659 disableTab : function(id){
45660 var tab = this.items[id];
45661 if(tab && this.active != tab){
45667 * Enables a {@link Roo.TabPanelItem} that is disabled.
45668 * @param {String/Number} id The id or index of the TabPanelItem to enable.
45670 enableTab : function(id){
45671 var tab = this.items[id];
45676 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
45677 * @param {String/Number} id The id or index of the TabPanelItem to activate.
45678 * @return {Roo.TabPanelItem} The TabPanelItem.
45680 activate : function(id)
45682 //Roo.log('activite:' + id);
45684 var tab = this.items[id];
45688 if(tab == this.active || tab.disabled){
45692 this.fireEvent("beforetabchange", this, e, tab);
45693 if(e.cancel !== true && !tab.disabled){
45695 this.active.hide();
45697 this.active = this.items[id];
45698 this.active.show();
45699 this.fireEvent("tabchange", this, this.active);
45705 * Gets the active {@link Roo.TabPanelItem}.
45706 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
45708 getActiveTab : function(){
45709 return this.active;
45713 * Updates the tab body element to fit the height of the container element
45714 * for overflow scrolling
45715 * @param {Number} targetHeight (optional) Override the starting height from the elements height
45717 syncHeight : function(targetHeight){
45718 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
45719 var bm = this.bodyEl.getMargins();
45720 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
45721 this.bodyEl.setHeight(newHeight);
45725 onResize : function(){
45726 if(this.monitorResize){
45727 this.autoSizeTabs();
45732 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
45734 beginUpdate : function(){
45735 this.updating = true;
45739 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
45741 endUpdate : function(){
45742 this.updating = false;
45743 this.autoSizeTabs();
45747 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
45749 autoSizeTabs : function()
45751 var count = this.items.length;
45752 var vcount = count - this.hiddenCount;
45755 this.stripEl.hide();
45757 this.stripEl.show();
45760 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
45765 var w = Math.max(this.el.getWidth() - this.cpad, 10);
45766 var availWidth = Math.floor(w / vcount);
45767 var b = this.stripBody;
45768 if(b.getWidth() > w){
45769 var tabs = this.items;
45770 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
45771 if(availWidth < this.minTabWidth){
45772 /*if(!this.sleft){ // incomplete scrolling code
45773 this.createScrollButtons();
45776 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
45779 if(this.currentTabWidth < this.preferredTabWidth){
45780 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
45786 * Returns the number of tabs in this TabPanel.
45789 getCount : function(){
45790 return this.items.length;
45794 * Resizes all the tabs to the passed width
45795 * @param {Number} The new width
45797 setTabWidth : function(width){
45798 this.currentTabWidth = width;
45799 for(var i = 0, len = this.items.length; i < len; i++) {
45800 if(!this.items[i].isHidden()) {
45801 this.items[i].setWidth(width);
45807 * Destroys this TabPanel
45808 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
45810 destroy : function(removeEl){
45811 Roo.EventManager.removeResizeListener(this.onResize, this);
45812 for(var i = 0, len = this.items.length; i < len; i++){
45813 this.items[i].purgeListeners();
45815 if(removeEl === true){
45816 this.el.update("");
45821 createStrip : function(container)
45823 var strip = document.createElement("nav");
45824 strip.className = Roo.bootstrap.version == 4 ?
45825 "navbar-light bg-light" :
45826 "navbar navbar-default"; //"x-tabs-wrap";
45827 container.appendChild(strip);
45831 createStripList : function(strip)
45833 // div wrapper for retard IE
45834 // returns the "tr" element.
45835 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
45836 //'<div class="x-tabs-strip-wrap">'+
45837 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
45838 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
45839 return strip.firstChild; //.firstChild.firstChild.firstChild;
45841 createBody : function(container)
45843 var body = document.createElement("div");
45844 Roo.id(body, "tab-body");
45845 //Roo.fly(body).addClass("x-tabs-body");
45846 Roo.fly(body).addClass("tab-content");
45847 container.appendChild(body);
45850 createItemBody :function(bodyEl, id){
45851 var body = Roo.getDom(id);
45853 body = document.createElement("div");
45856 //Roo.fly(body).addClass("x-tabs-item-body");
45857 Roo.fly(body).addClass("tab-pane");
45858 bodyEl.insertBefore(body, bodyEl.firstChild);
45862 createStripElements : function(stripEl, text, closable, tpl)
45864 var td = document.createElement("li"); // was td..
45865 td.className = 'nav-item';
45867 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
45870 stripEl.appendChild(td);
45872 td.className = "x-tabs-closable";
45873 if(!this.closeTpl){
45874 this.closeTpl = new Roo.Template(
45875 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45876 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
45877 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
45880 var el = this.closeTpl.overwrite(td, {"text": text});
45881 var close = el.getElementsByTagName("div")[0];
45882 var inner = el.getElementsByTagName("em")[0];
45883 return {"el": el, "close": close, "inner": inner};
45886 // not sure what this is..
45887 // if(!this.tabTpl){
45888 //this.tabTpl = new Roo.Template(
45889 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
45890 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
45892 // this.tabTpl = new Roo.Template(
45893 // '<a href="#">' +
45894 // '<span unselectable="on"' +
45895 // (this.disableTooltips ? '' : ' title="{text}"') +
45896 // ' >{text}</span></a>'
45902 var template = tpl || this.tabTpl || false;
45905 template = new Roo.Template(
45906 Roo.bootstrap.version == 4 ?
45908 '<a class="nav-link" href="#" unselectable="on"' +
45909 (this.disableTooltips ? '' : ' title="{text}"') +
45912 '<a class="nav-link" href="#">' +
45913 '<span unselectable="on"' +
45914 (this.disableTooltips ? '' : ' title="{text}"') +
45915 ' >{text}</span></a>'
45920 switch (typeof(template)) {
45924 template = new Roo.Template(template);
45930 var el = template.overwrite(td, {"text": text});
45932 var inner = el.getElementsByTagName("span")[0];
45934 return {"el": el, "inner": inner};
45942 * @class Roo.TabPanelItem
45943 * @extends Roo.util.Observable
45944 * Represents an individual item (tab plus body) in a TabPanel.
45945 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
45946 * @param {String} id The id of this TabPanelItem
45947 * @param {String} text The text for the tab of this TabPanelItem
45948 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
45950 Roo.bootstrap.panel.TabItem = function(config){
45952 * The {@link Roo.TabPanel} this TabPanelItem belongs to
45953 * @type Roo.TabPanel
45955 this.tabPanel = config.panel;
45957 * The id for this TabPanelItem
45960 this.id = config.id;
45962 this.disabled = false;
45964 this.text = config.text;
45966 this.loaded = false;
45967 this.closable = config.closable;
45970 * The body element for this TabPanelItem.
45971 * @type Roo.Element
45973 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
45974 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
45975 this.bodyEl.setStyle("display", "block");
45976 this.bodyEl.setStyle("zoom", "1");
45977 //this.hideAction();
45979 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
45981 this.el = Roo.get(els.el);
45982 this.inner = Roo.get(els.inner, true);
45983 this.textEl = Roo.bootstrap.version == 4 ?
45984 this.el : Roo.get(this.el.dom.firstChild, true);
45986 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
45987 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
45990 // this.el.on("mousedown", this.onTabMouseDown, this);
45991 this.el.on("click", this.onTabClick, this);
45993 if(config.closable){
45994 var c = Roo.get(els.close, true);
45995 c.dom.title = this.closeText;
45996 c.addClassOnOver("close-over");
45997 c.on("click", this.closeClick, this);
46003 * Fires when this tab becomes the active tab.
46004 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46005 * @param {Roo.TabPanelItem} this
46009 * @event beforeclose
46010 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46011 * @param {Roo.TabPanelItem} this
46012 * @param {Object} e Set cancel to true on this object to cancel the close.
46014 "beforeclose": true,
46017 * Fires when this tab is closed.
46018 * @param {Roo.TabPanelItem} this
46022 * @event deactivate
46023 * Fires when this tab is no longer the active tab.
46024 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46025 * @param {Roo.TabPanelItem} this
46027 "deactivate" : true
46029 this.hidden = false;
46031 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46034 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46036 purgeListeners : function(){
46037 Roo.util.Observable.prototype.purgeListeners.call(this);
46038 this.el.removeAllListeners();
46041 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46044 this.status_node.addClass("active");
46047 this.tabPanel.stripWrap.repaint();
46049 this.fireEvent("activate", this.tabPanel, this);
46053 * Returns true if this tab is the active tab.
46054 * @return {Boolean}
46056 isActive : function(){
46057 return this.tabPanel.getActiveTab() == this;
46061 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46064 this.status_node.removeClass("active");
46066 this.fireEvent("deactivate", this.tabPanel, this);
46069 hideAction : function(){
46070 this.bodyEl.hide();
46071 this.bodyEl.setStyle("position", "absolute");
46072 this.bodyEl.setLeft("-20000px");
46073 this.bodyEl.setTop("-20000px");
46076 showAction : function(){
46077 this.bodyEl.setStyle("position", "relative");
46078 this.bodyEl.setTop("");
46079 this.bodyEl.setLeft("");
46080 this.bodyEl.show();
46084 * Set the tooltip for the tab.
46085 * @param {String} tooltip The tab's tooltip
46087 setTooltip : function(text){
46088 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46089 this.textEl.dom.qtip = text;
46090 this.textEl.dom.removeAttribute('title');
46092 this.textEl.dom.title = text;
46096 onTabClick : function(e){
46097 e.preventDefault();
46098 this.tabPanel.activate(this.id);
46101 onTabMouseDown : function(e){
46102 e.preventDefault();
46103 this.tabPanel.activate(this.id);
46106 getWidth : function(){
46107 return this.inner.getWidth();
46110 setWidth : function(width){
46111 var iwidth = width - this.linode.getPadding("lr");
46112 this.inner.setWidth(iwidth);
46113 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46114 this.linode.setWidth(width);
46118 * Show or hide the tab
46119 * @param {Boolean} hidden True to hide or false to show.
46121 setHidden : function(hidden){
46122 this.hidden = hidden;
46123 this.linode.setStyle("display", hidden ? "none" : "");
46127 * Returns true if this tab is "hidden"
46128 * @return {Boolean}
46130 isHidden : function(){
46131 return this.hidden;
46135 * Returns the text for this tab
46138 getText : function(){
46142 autoSize : function(){
46143 //this.el.beginMeasure();
46144 this.textEl.setWidth(1);
46146 * #2804 [new] Tabs in Roojs
46147 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46149 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46150 //this.el.endMeasure();
46154 * Sets the text for the tab (Note: this also sets the tooltip text)
46155 * @param {String} text The tab's text and tooltip
46157 setText : function(text){
46159 this.textEl.update(text);
46160 this.setTooltip(text);
46161 //if(!this.tabPanel.resizeTabs){
46162 // this.autoSize();
46166 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46168 activate : function(){
46169 this.tabPanel.activate(this.id);
46173 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46175 disable : function(){
46176 if(this.tabPanel.active != this){
46177 this.disabled = true;
46178 this.status_node.addClass("disabled");
46183 * Enables this TabPanelItem if it was previously disabled.
46185 enable : function(){
46186 this.disabled = false;
46187 this.status_node.removeClass("disabled");
46191 * Sets the content for this TabPanelItem.
46192 * @param {String} content The content
46193 * @param {Boolean} loadScripts true to look for and load scripts
46195 setContent : function(content, loadScripts){
46196 this.bodyEl.update(content, loadScripts);
46200 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46201 * @return {Roo.UpdateManager} The UpdateManager
46203 getUpdateManager : function(){
46204 return this.bodyEl.getUpdateManager();
46208 * Set a URL to be used to load the content for this TabPanelItem.
46209 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46210 * @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)
46211 * @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)
46212 * @return {Roo.UpdateManager} The UpdateManager
46214 setUrl : function(url, params, loadOnce){
46215 if(this.refreshDelegate){
46216 this.un('activate', this.refreshDelegate);
46218 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46219 this.on("activate", this.refreshDelegate);
46220 return this.bodyEl.getUpdateManager();
46224 _handleRefresh : function(url, params, loadOnce){
46225 if(!loadOnce || !this.loaded){
46226 var updater = this.bodyEl.getUpdateManager();
46227 updater.update(url, params, this._setLoaded.createDelegate(this));
46232 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
46233 * Will fail silently if the setUrl method has not been called.
46234 * This does not activate the panel, just updates its content.
46236 refresh : function(){
46237 if(this.refreshDelegate){
46238 this.loaded = false;
46239 this.refreshDelegate();
46244 _setLoaded : function(){
46245 this.loaded = true;
46249 closeClick : function(e){
46252 this.fireEvent("beforeclose", this, o);
46253 if(o.cancel !== true){
46254 this.tabPanel.removeTab(this.id);
46258 * The text displayed in the tooltip for the close icon.
46261 closeText : "Close this tab"
46264 * This script refer to:
46265 * Title: International Telephone Input
46266 * Author: Jack O'Connor
46267 * Code version: v12.1.12
46268 * Availability: https://github.com/jackocnr/intl-tel-input.git
46271 Roo.bootstrap.form.PhoneInputData = function() {
46274 "Afghanistan (افغانستان)",
46279 "Albania (Shqipëri)",
46284 "Algeria (الجزائر)",
46309 "Antigua and Barbuda",
46319 "Armenia (Հայաստան)",
46335 "Austria (Österreich)",
46340 "Azerbaijan (Azərbaycan)",
46350 "Bahrain (البحرين)",
46355 "Bangladesh (বাংলাদেশ)",
46365 "Belarus (Беларусь)",
46370 "Belgium (België)",
46400 "Bosnia and Herzegovina (Босна и Херцеговина)",
46415 "British Indian Ocean Territory",
46420 "British Virgin Islands",
46430 "Bulgaria (България)",
46440 "Burundi (Uburundi)",
46445 "Cambodia (កម្ពុជា)",
46450 "Cameroon (Cameroun)",
46459 ["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"]
46462 "Cape Verde (Kabu Verdi)",
46467 "Caribbean Netherlands",
46478 "Central African Republic (République centrafricaine)",
46498 "Christmas Island",
46504 "Cocos (Keeling) Islands",
46515 "Comoros (جزر القمر)",
46520 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
46525 "Congo (Republic) (Congo-Brazzaville)",
46545 "Croatia (Hrvatska)",
46566 "Czech Republic (Česká republika)",
46571 "Denmark (Danmark)",
46586 "Dominican Republic (República Dominicana)",
46590 ["809", "829", "849"]
46608 "Equatorial Guinea (Guinea Ecuatorial)",
46628 "Falkland Islands (Islas Malvinas)",
46633 "Faroe Islands (Føroyar)",
46654 "French Guiana (Guyane française)",
46659 "French Polynesia (Polynésie française)",
46674 "Georgia (საქართველო)",
46679 "Germany (Deutschland)",
46699 "Greenland (Kalaallit Nunaat)",
46736 "Guinea-Bissau (Guiné Bissau)",
46761 "Hungary (Magyarország)",
46766 "Iceland (Ísland)",
46786 "Iraq (العراق)",
46802 "Israel (ישראל)",
46829 "Jordan (الأردن)",
46834 "Kazakhstan (Казахстан)",
46855 "Kuwait (الكويت)",
46860 "Kyrgyzstan (Кыргызстан)",
46870 "Latvia (Latvija)",
46875 "Lebanon (لبنان)",
46890 "Libya (ليبيا)",
46900 "Lithuania (Lietuva)",
46915 "Macedonia (FYROM) (Македонија)",
46920 "Madagascar (Madagasikara)",
46950 "Marshall Islands",
46960 "Mauritania (موريتانيا)",
46965 "Mauritius (Moris)",
46986 "Moldova (Republica Moldova)",
46996 "Mongolia (Монгол)",
47001 "Montenegro (Crna Gora)",
47011 "Morocco (المغرب)",
47017 "Mozambique (Moçambique)",
47022 "Myanmar (Burma) (မြန်မာ)",
47027 "Namibia (Namibië)",
47042 "Netherlands (Nederland)",
47047 "New Caledonia (Nouvelle-Calédonie)",
47082 "North Korea (조선 민주주의 인민 공화국)",
47087 "Northern Mariana Islands",
47103 "Pakistan (پاکستان)",
47113 "Palestine (فلسطين)",
47123 "Papua New Guinea",
47165 "Réunion (La Réunion)",
47171 "Romania (România)",
47187 "Saint Barthélemy",
47198 "Saint Kitts and Nevis",
47208 "Saint Martin (Saint-Martin (partie française))",
47214 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47219 "Saint Vincent and the Grenadines",
47234 "São Tomé and Príncipe (São Tomé e Príncipe)",
47239 "Saudi Arabia (المملكة العربية السعودية)",
47244 "Senegal (Sénégal)",
47274 "Slovakia (Slovensko)",
47279 "Slovenia (Slovenija)",
47289 "Somalia (Soomaaliya)",
47299 "South Korea (대한민국)",
47304 "South Sudan (جنوب السودان)",
47314 "Sri Lanka (ශ්රී ලංකාව)",
47319 "Sudan (السودان)",
47329 "Svalbard and Jan Mayen",
47340 "Sweden (Sverige)",
47345 "Switzerland (Schweiz)",
47350 "Syria (سوريا)",
47395 "Trinidad and Tobago",
47400 "Tunisia (تونس)",
47405 "Turkey (Türkiye)",
47415 "Turks and Caicos Islands",
47425 "U.S. Virgin Islands",
47435 "Ukraine (Україна)",
47440 "United Arab Emirates (الإمارات العربية المتحدة)",
47462 "Uzbekistan (Oʻzbekiston)",
47472 "Vatican City (Città del Vaticano)",
47483 "Vietnam (Việt Nam)",
47488 "Wallis and Futuna (Wallis-et-Futuna)",
47493 "Western Sahara (الصحراء الغربية)",
47499 "Yemen (اليمن)",
47523 * This script refer to:
47524 * Title: International Telephone Input
47525 * Author: Jack O'Connor
47526 * Code version: v12.1.12
47527 * Availability: https://github.com/jackocnr/intl-tel-input.git
47531 * @class Roo.bootstrap.form.PhoneInput
47532 * @extends Roo.bootstrap.form.TriggerField
47533 * An input with International dial-code selection
47535 * @cfg {String} defaultDialCode default '+852'
47536 * @cfg {Array} preferedCountries default []
47539 * Create a new PhoneInput.
47540 * @param {Object} config Configuration options
47543 Roo.bootstrap.form.PhoneInput = function(config) {
47544 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
47547 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
47549 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
47551 listWidth: undefined,
47553 selectedClass: 'active',
47555 invalidClass : "has-warning",
47557 validClass: 'has-success',
47559 allowed: '0123456789',
47564 * @cfg {String} defaultDialCode The default dial code when initializing the input
47566 defaultDialCode: '+852',
47569 * @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
47571 preferedCountries: false,
47573 getAutoCreate : function()
47575 var data = Roo.bootstrap.form.PhoneInputData();
47576 var align = this.labelAlign || this.parentLabelAlign();
47579 this.allCountries = [];
47580 this.dialCodeMapping = [];
47582 for (var i = 0; i < data.length; i++) {
47584 this.allCountries[i] = {
47588 priority: c[3] || 0,
47589 areaCodes: c[4] || null
47591 this.dialCodeMapping[c[2]] = {
47594 priority: c[3] || 0,
47595 areaCodes: c[4] || null
47607 // type: 'number', -- do not use number - we get the flaky up/down arrows.
47608 maxlength: this.max_length,
47609 cls : 'form-control tel-input',
47610 autocomplete: 'new-password'
47613 var hiddenInput = {
47616 cls: 'hidden-tel-input'
47620 hiddenInput.name = this.name;
47623 if (this.disabled) {
47624 input.disabled = true;
47627 var flag_container = {
47644 cls: this.hasFeedback ? 'has-feedback' : '',
47650 cls: 'dial-code-holder',
47657 cls: 'roo-select2-container input-group',
47664 if (this.fieldLabel.length) {
47667 tooltip: 'This field is required'
47673 cls: 'control-label',
47679 html: this.fieldLabel
47682 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
47688 if(this.indicatorpos == 'right') {
47689 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
47696 if(align == 'left') {
47704 if(this.labelWidth > 12){
47705 label.style = "width: " + this.labelWidth + 'px';
47707 if(this.labelWidth < 13 && this.labelmd == 0){
47708 this.labelmd = this.labelWidth;
47710 if(this.labellg > 0){
47711 label.cls += ' col-lg-' + this.labellg;
47712 input.cls += ' col-lg-' + (12 - this.labellg);
47714 if(this.labelmd > 0){
47715 label.cls += ' col-md-' + this.labelmd;
47716 container.cls += ' col-md-' + (12 - this.labelmd);
47718 if(this.labelsm > 0){
47719 label.cls += ' col-sm-' + this.labelsm;
47720 container.cls += ' col-sm-' + (12 - this.labelsm);
47722 if(this.labelxs > 0){
47723 label.cls += ' col-xs-' + this.labelxs;
47724 container.cls += ' col-xs-' + (12 - this.labelxs);
47734 var settings = this;
47736 ['xs','sm','md','lg'].map(function(size){
47737 if (settings[size]) {
47738 cfg.cls += ' col-' + size + '-' + settings[size];
47742 this.store = new Roo.data.Store({
47743 proxy : new Roo.data.MemoryProxy({}),
47744 reader : new Roo.data.JsonReader({
47755 'name' : 'dialCode',
47759 'name' : 'priority',
47763 'name' : 'areaCodes',
47770 if(!this.preferedCountries) {
47771 this.preferedCountries = [
47778 var p = this.preferedCountries.reverse();
47781 for (var i = 0; i < p.length; i++) {
47782 for (var j = 0; j < this.allCountries.length; j++) {
47783 if(this.allCountries[j].iso2 == p[i]) {
47784 var t = this.allCountries[j];
47785 this.allCountries.splice(j,1);
47786 this.allCountries.unshift(t);
47792 this.store.proxy.data = {
47794 data: this.allCountries
47800 initEvents : function()
47803 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
47805 this.indicator = this.indicatorEl();
47806 this.flag = this.flagEl();
47807 this.dialCodeHolder = this.dialCodeHolderEl();
47809 this.trigger = this.el.select('div.flag-box',true).first();
47810 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
47815 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
47816 _this.list.setWidth(lw);
47819 this.list.on('mouseover', this.onViewOver, this);
47820 this.list.on('mousemove', this.onViewMove, this);
47821 this.inputEl().on("keyup", this.onKeyUp, this);
47822 this.inputEl().on("keypress", this.onKeyPress, this);
47824 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
47826 this.view = new Roo.View(this.list, this.tpl, {
47827 singleSelect:true, store: this.store, selectedClass: this.selectedClass
47830 this.view.on('click', this.onViewClick, this);
47831 this.setValue(this.defaultDialCode);
47834 onTriggerClick : function(e)
47836 Roo.log('trigger click');
47841 if(this.isExpanded()){
47843 this.hasFocus = false;
47845 this.store.load({});
47846 this.hasFocus = true;
47851 isExpanded : function()
47853 return this.list.isVisible();
47856 collapse : function()
47858 if(!this.isExpanded()){
47862 Roo.get(document).un('mousedown', this.collapseIf, this);
47863 Roo.get(document).un('mousewheel', this.collapseIf, this);
47864 this.fireEvent('collapse', this);
47868 expand : function()
47872 if(this.isExpanded() || !this.hasFocus){
47876 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
47877 this.list.setWidth(lw);
47880 this.restrictHeight();
47882 Roo.get(document).on('mousedown', this.collapseIf, this);
47883 Roo.get(document).on('mousewheel', this.collapseIf, this);
47885 this.fireEvent('expand', this);
47888 restrictHeight : function()
47890 this.list.alignTo(this.inputEl(), this.listAlign);
47891 this.list.alignTo(this.inputEl(), this.listAlign);
47894 onViewOver : function(e, t)
47896 if(this.inKeyMode){
47899 var item = this.view.findItemFromChild(t);
47902 var index = this.view.indexOf(item);
47903 this.select(index, false);
47908 onViewClick : function(view, doFocus, el, e)
47910 var index = this.view.getSelectedIndexes()[0];
47912 var r = this.store.getAt(index);
47915 this.onSelect(r, index);
47917 if(doFocus !== false && !this.blockFocus){
47918 this.inputEl().focus();
47922 onViewMove : function(e, t)
47924 this.inKeyMode = false;
47927 select : function(index, scrollIntoView)
47929 this.selectedIndex = index;
47930 this.view.select(index);
47931 if(scrollIntoView !== false){
47932 var el = this.view.getNode(index);
47934 this.list.scrollChildIntoView(el, false);
47939 createList : function()
47941 this.list = Roo.get(document.body).createChild({
47943 cls: 'typeahead typeahead-long dropdown-menu tel-list',
47944 style: 'display:none'
47947 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
47950 collapseIf : function(e)
47952 var in_combo = e.within(this.el);
47953 var in_list = e.within(this.list);
47954 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
47956 if (in_combo || in_list || is_list) {
47962 onSelect : function(record, index)
47964 if(this.fireEvent('beforeselect', this, record, index) !== false){
47966 this.setFlagClass(record.data.iso2);
47967 this.setDialCode(record.data.dialCode);
47968 this.hasFocus = false;
47970 this.fireEvent('select', this, record, index);
47974 flagEl : function()
47976 var flag = this.el.select('div.flag',true).first();
47983 dialCodeHolderEl : function()
47985 var d = this.el.select('input.dial-code-holder',true).first();
47992 setDialCode : function(v)
47994 this.dialCodeHolder.dom.value = '+'+v;
47997 setFlagClass : function(n)
47999 this.flag.dom.className = 'flag '+n;
48002 getValue : function()
48004 var v = this.inputEl().getValue();
48005 if(this.dialCodeHolder) {
48006 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48011 setValue : function(v)
48013 var d = this.getDialCode(v);
48015 //invalid dial code
48016 if(v.length == 0 || !d || d.length == 0) {
48018 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48019 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48025 this.setFlagClass(this.dialCodeMapping[d].iso2);
48026 this.setDialCode(d);
48027 this.inputEl().dom.value = v.replace('+'+d,'');
48028 this.hiddenEl().dom.value = this.getValue();
48033 getDialCode : function(v)
48037 if (v.length == 0) {
48038 return this.dialCodeHolder.dom.value;
48042 if (v.charAt(0) != "+") {
48045 var numericChars = "";
48046 for (var i = 1; i < v.length; i++) {
48047 var c = v.charAt(i);
48050 if (this.dialCodeMapping[numericChars]) {
48051 dialCode = v.substr(1, i);
48053 if (numericChars.length == 4) {
48063 this.setValue(this.defaultDialCode);
48067 hiddenEl : function()
48069 return this.el.select('input.hidden-tel-input',true).first();
48072 // after setting val
48073 onKeyUp : function(e){
48074 this.setValue(this.getValue());
48077 onKeyPress : function(e){
48078 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48085 * @class Roo.bootstrap.form.MoneyField
48086 * @extends Roo.bootstrap.form.ComboBox
48087 * Bootstrap MoneyField class
48090 * Create a new MoneyField.
48091 * @param {Object} config Configuration options
48094 Roo.bootstrap.form.MoneyField = function(config) {
48096 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48100 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48103 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48105 allowDecimals : true,
48107 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48109 decimalSeparator : ".",
48111 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48113 decimalPrecision : 0,
48115 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48117 allowNegative : true,
48119 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48123 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48125 minValue : Number.NEGATIVE_INFINITY,
48127 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48129 maxValue : Number.MAX_VALUE,
48131 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48133 minText : "The minimum value for this field is {0}",
48135 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48137 maxText : "The maximum value for this field is {0}",
48139 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48140 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48142 nanText : "{0} is not a valid number",
48144 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48148 * @cfg {String} defaults currency of the MoneyField
48149 * value should be in lkey
48151 defaultCurrency : false,
48153 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48155 thousandsDelimiter : false,
48157 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48166 * @cfg {Roo.data.Store} store Store to lookup currency??
48170 getAutoCreate : function()
48172 var align = this.labelAlign || this.parentLabelAlign();
48184 cls : 'form-control roo-money-amount-input',
48185 autocomplete: 'new-password'
48188 var hiddenInput = {
48192 cls: 'hidden-number-input'
48195 if(this.max_length) {
48196 input.maxlength = this.max_length;
48200 hiddenInput.name = this.name;
48203 if (this.disabled) {
48204 input.disabled = true;
48207 var clg = 12 - this.inputlg;
48208 var cmd = 12 - this.inputmd;
48209 var csm = 12 - this.inputsm;
48210 var cxs = 12 - this.inputxs;
48214 cls : 'row roo-money-field',
48218 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48222 cls: 'roo-select2-container input-group',
48226 cls : 'form-control roo-money-currency-input',
48227 autocomplete: 'new-password',
48229 name : this.currencyName
48233 cls : 'input-group-addon',
48247 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48251 cls: this.hasFeedback ? 'has-feedback' : '',
48262 if (this.fieldLabel.length) {
48265 tooltip: 'This field is required'
48271 cls: 'control-label',
48277 html: this.fieldLabel
48280 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48286 if(this.indicatorpos == 'right') {
48287 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48294 if(align == 'left') {
48302 if(this.labelWidth > 12){
48303 label.style = "width: " + this.labelWidth + 'px';
48305 if(this.labelWidth < 13 && this.labelmd == 0){
48306 this.labelmd = this.labelWidth;
48308 if(this.labellg > 0){
48309 label.cls += ' col-lg-' + this.labellg;
48310 input.cls += ' col-lg-' + (12 - this.labellg);
48312 if(this.labelmd > 0){
48313 label.cls += ' col-md-' + this.labelmd;
48314 container.cls += ' col-md-' + (12 - this.labelmd);
48316 if(this.labelsm > 0){
48317 label.cls += ' col-sm-' + this.labelsm;
48318 container.cls += ' col-sm-' + (12 - this.labelsm);
48320 if(this.labelxs > 0){
48321 label.cls += ' col-xs-' + this.labelxs;
48322 container.cls += ' col-xs-' + (12 - this.labelxs);
48333 var settings = this;
48335 ['xs','sm','md','lg'].map(function(size){
48336 if (settings[size]) {
48337 cfg.cls += ' col-' + size + '-' + settings[size];
48344 initEvents : function()
48346 this.indicator = this.indicatorEl();
48348 this.initCurrencyEvent();
48350 this.initNumberEvent();
48353 initCurrencyEvent : function()
48356 throw "can not find store for combo";
48359 this.store = Roo.factory(this.store, Roo.data);
48360 this.store.parent = this;
48364 this.triggerEl = this.el.select('.input-group-addon', true).first();
48366 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48371 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48372 _this.list.setWidth(lw);
48375 this.list.on('mouseover', this.onViewOver, this);
48376 this.list.on('mousemove', this.onViewMove, this);
48377 this.list.on('scroll', this.onViewScroll, this);
48380 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48383 this.view = new Roo.View(this.list, this.tpl, {
48384 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48387 this.view.on('click', this.onViewClick, this);
48389 this.store.on('beforeload', this.onBeforeLoad, this);
48390 this.store.on('load', this.onLoad, this);
48391 this.store.on('loadexception', this.onLoadException, this);
48393 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48394 "up" : function(e){
48395 this.inKeyMode = true;
48399 "down" : function(e){
48400 if(!this.isExpanded()){
48401 this.onTriggerClick();
48403 this.inKeyMode = true;
48408 "enter" : function(e){
48411 if(this.fireEvent("specialkey", this, e)){
48412 this.onViewClick(false);
48418 "esc" : function(e){
48422 "tab" : function(e){
48425 if(this.fireEvent("specialkey", this, e)){
48426 this.onViewClick(false);
48434 doRelay : function(foo, bar, hname){
48435 if(hname == 'down' || this.scope.isExpanded()){
48436 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48444 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48448 initNumberEvent : function(e)
48450 this.inputEl().on("keydown" , this.fireKey, this);
48451 this.inputEl().on("focus", this.onFocus, this);
48452 this.inputEl().on("blur", this.onBlur, this);
48454 this.inputEl().relayEvent('keyup', this);
48456 if(this.indicator){
48457 this.indicator.addClass('invisible');
48460 this.originalValue = this.getValue();
48462 if(this.validationEvent == 'keyup'){
48463 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48464 this.inputEl().on('keyup', this.filterValidation, this);
48466 else if(this.validationEvent !== false){
48467 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48470 if(this.selectOnFocus){
48471 this.on("focus", this.preFocus, this);
48474 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
48475 this.inputEl().on("keypress", this.filterKeys, this);
48477 this.inputEl().relayEvent('keypress', this);
48480 var allowed = "0123456789";
48482 if(this.allowDecimals){
48483 allowed += this.decimalSeparator;
48486 if(this.allowNegative){
48490 if(this.thousandsDelimiter) {
48494 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
48496 var keyPress = function(e){
48498 var k = e.getKey();
48500 var c = e.getCharCode();
48503 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
48504 allowed.indexOf(String.fromCharCode(c)) === -1
48510 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
48514 if(allowed.indexOf(String.fromCharCode(c)) === -1){
48519 this.inputEl().on("keypress", keyPress, this);
48523 onTriggerClick : function(e)
48530 this.loadNext = false;
48532 if(this.isExpanded()){
48537 this.hasFocus = true;
48539 if(this.triggerAction == 'all') {
48540 this.doQuery(this.allQuery, true);
48544 this.doQuery(this.getRawValue());
48547 getCurrency : function()
48549 var v = this.currencyEl().getValue();
48554 restrictHeight : function()
48556 this.list.alignTo(this.currencyEl(), this.listAlign);
48557 this.list.alignTo(this.currencyEl(), this.listAlign);
48560 onViewClick : function(view, doFocus, el, e)
48562 var index = this.view.getSelectedIndexes()[0];
48564 var r = this.store.getAt(index);
48567 this.onSelect(r, index);
48571 onSelect : function(record, index){
48573 if(this.fireEvent('beforeselect', this, record, index) !== false){
48575 this.setFromCurrencyData(index > -1 ? record.data : false);
48579 this.fireEvent('select', this, record, index);
48583 setFromCurrencyData : function(o)
48587 this.lastCurrency = o;
48589 if (this.currencyField) {
48590 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
48592 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
48595 this.lastSelectionText = currency;
48597 //setting default currency
48598 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
48599 this.setCurrency(this.defaultCurrency);
48603 this.setCurrency(currency);
48606 setFromData : function(o)
48610 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
48612 this.setFromCurrencyData(c);
48617 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
48619 Roo.log('no value set for '+ (this.name ? this.name : this.id));
48622 this.setValue(value);
48626 setCurrency : function(v)
48628 this.currencyValue = v;
48631 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
48636 setValue : function(v)
48638 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
48644 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48646 this.inputEl().dom.value = (v == '') ? '' :
48647 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
48649 if(!this.allowZero && v === '0') {
48650 this.hiddenEl().dom.value = '';
48651 this.inputEl().dom.value = '';
48658 getRawValue : function()
48660 var v = this.inputEl().getValue();
48665 getValue : function()
48667 return this.fixPrecision(this.parseValue(this.getRawValue()));
48670 parseValue : function(value)
48672 if(this.thousandsDelimiter) {
48674 r = new RegExp(",", "g");
48675 value = value.replace(r, "");
48678 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
48679 return isNaN(value) ? '' : value;
48683 fixPrecision : function(value)
48685 if(this.thousandsDelimiter) {
48687 r = new RegExp(",", "g");
48688 value = value.replace(r, "");
48691 var nan = isNaN(value);
48693 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
48694 return nan ? '' : value;
48696 return parseFloat(value).toFixed(this.decimalPrecision);
48699 decimalPrecisionFcn : function(v)
48701 return Math.floor(v);
48704 validateValue : function(value)
48706 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
48710 var num = this.parseValue(value);
48713 this.markInvalid(String.format(this.nanText, value));
48717 if(num < this.minValue){
48718 this.markInvalid(String.format(this.minText, this.minValue));
48722 if(num > this.maxValue){
48723 this.markInvalid(String.format(this.maxText, this.maxValue));
48730 validate : function()
48732 if(this.disabled || this.allowBlank){
48737 var currency = this.getCurrency();
48739 if(this.validateValue(this.getRawValue()) && currency.length){
48744 this.markInvalid();
48748 getName: function()
48753 beforeBlur : function()
48759 var v = this.parseValue(this.getRawValue());
48766 onBlur : function()
48770 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
48771 //this.el.removeClass(this.focusClass);
48774 this.hasFocus = false;
48776 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
48780 var v = this.getValue();
48782 if(String(v) !== String(this.startValue)){
48783 this.fireEvent('change', this, v, this.startValue);
48786 this.fireEvent("blur", this);
48789 inputEl : function()
48791 return this.el.select('.roo-money-amount-input', true).first();
48794 currencyEl : function()
48796 return this.el.select('.roo-money-currency-input', true).first();
48799 hiddenEl : function()
48801 return this.el.select('input.hidden-number-input',true).first();
48805 * @class Roo.bootstrap.BezierSignature
48806 * @extends Roo.bootstrap.Component
48807 * Bootstrap BezierSignature class
48808 * This script refer to:
48809 * Title: Signature Pad
48811 * Availability: https://github.com/szimek/signature_pad
48814 * Create a new BezierSignature
48815 * @param {Object} config The config object
48818 Roo.bootstrap.BezierSignature = function(config){
48819 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
48825 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
48832 mouse_btn_down: true,
48835 * @cfg {int} canvas height
48837 canvas_height: '200px',
48840 * @cfg {float|function} Radius of a single dot.
48845 * @cfg {float} Minimum width of a line. Defaults to 0.5.
48850 * @cfg {float} Maximum width of a line. Defaults to 2.5.
48855 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
48860 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
48865 * @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.
48867 bg_color: 'rgba(0, 0, 0, 0)',
48870 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
48872 dot_color: 'black',
48875 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
48877 velocity_filter_weight: 0.7,
48880 * @cfg {function} Callback when stroke begin.
48885 * @cfg {function} Callback when stroke end.
48889 getAutoCreate : function()
48891 var cls = 'roo-signature column';
48894 cls += ' ' + this.cls;
48904 for(var i = 0; i < col_sizes.length; i++) {
48905 if(this[col_sizes[i]]) {
48906 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
48916 cls: 'roo-signature-body',
48920 cls: 'roo-signature-body-canvas',
48921 height: this.canvas_height,
48922 width: this.canvas_width
48929 style: 'display: none'
48937 initEvents: function()
48939 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
48941 var canvas = this.canvasEl();
48943 // mouse && touch event swapping...
48944 canvas.dom.style.touchAction = 'none';
48945 canvas.dom.style.msTouchAction = 'none';
48947 this.mouse_btn_down = false;
48948 canvas.on('mousedown', this._handleMouseDown, this);
48949 canvas.on('mousemove', this._handleMouseMove, this);
48950 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
48952 if (window.PointerEvent) {
48953 canvas.on('pointerdown', this._handleMouseDown, this);
48954 canvas.on('pointermove', this._handleMouseMove, this);
48955 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
48958 if ('ontouchstart' in window) {
48959 canvas.on('touchstart', this._handleTouchStart, this);
48960 canvas.on('touchmove', this._handleTouchMove, this);
48961 canvas.on('touchend', this._handleTouchEnd, this);
48964 Roo.EventManager.onWindowResize(this.resize, this, true);
48966 // file input event
48967 this.fileEl().on('change', this.uploadImage, this);
48974 resize: function(){
48976 var canvas = this.canvasEl().dom;
48977 var ctx = this.canvasElCtx();
48978 var img_data = false;
48980 if(canvas.width > 0) {
48981 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
48983 // setting canvas width will clean img data
48986 var style = window.getComputedStyle ?
48987 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
48989 var padding_left = parseInt(style.paddingLeft) || 0;
48990 var padding_right = parseInt(style.paddingRight) || 0;
48992 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
48995 ctx.putImageData(img_data, 0, 0);
48999 _handleMouseDown: function(e)
49001 if (e.browserEvent.which === 1) {
49002 this.mouse_btn_down = true;
49003 this.strokeBegin(e);
49007 _handleMouseMove: function (e)
49009 if (this.mouse_btn_down) {
49010 this.strokeMoveUpdate(e);
49014 _handleMouseUp: function (e)
49016 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49017 this.mouse_btn_down = false;
49022 _handleTouchStart: function (e) {
49024 e.preventDefault();
49025 if (e.browserEvent.targetTouches.length === 1) {
49026 // var touch = e.browserEvent.changedTouches[0];
49027 // this.strokeBegin(touch);
49029 this.strokeBegin(e); // assume e catching the correct xy...
49033 _handleTouchMove: function (e) {
49034 e.preventDefault();
49035 // var touch = event.targetTouches[0];
49036 // _this._strokeMoveUpdate(touch);
49037 this.strokeMoveUpdate(e);
49040 _handleTouchEnd: function (e) {
49041 var wasCanvasTouched = e.target === this.canvasEl().dom;
49042 if (wasCanvasTouched) {
49043 e.preventDefault();
49044 // var touch = event.changedTouches[0];
49045 // _this._strokeEnd(touch);
49050 reset: function () {
49051 this._lastPoints = [];
49052 this._lastVelocity = 0;
49053 this._lastWidth = (this.min_width + this.max_width) / 2;
49054 this.canvasElCtx().fillStyle = this.dot_color;
49057 strokeMoveUpdate: function(e)
49059 this.strokeUpdate(e);
49061 if (this.throttle) {
49062 this.throttleStroke(this.strokeUpdate, this.throttle);
49065 this.strokeUpdate(e);
49069 strokeBegin: function(e)
49071 var newPointGroup = {
49072 color: this.dot_color,
49076 if (typeof this.onBegin === 'function') {
49080 this.curve_data.push(newPointGroup);
49082 this.strokeUpdate(e);
49085 strokeUpdate: function(e)
49087 var rect = this.canvasEl().dom.getBoundingClientRect();
49088 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49089 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49090 var lastPoints = lastPointGroup.points;
49091 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49092 var isLastPointTooClose = lastPoint
49093 ? point.distanceTo(lastPoint) <= this.min_distance
49095 var color = lastPointGroup.color;
49096 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49097 var curve = this.addPoint(point);
49099 this.drawDot({color: color, point: point});
49102 this.drawCurve({color: color, curve: curve});
49112 strokeEnd: function(e)
49114 this.strokeUpdate(e);
49115 if (typeof this.onEnd === 'function') {
49120 addPoint: function (point) {
49121 var _lastPoints = this._lastPoints;
49122 _lastPoints.push(point);
49123 if (_lastPoints.length > 2) {
49124 if (_lastPoints.length === 3) {
49125 _lastPoints.unshift(_lastPoints[0]);
49127 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49128 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49129 _lastPoints.shift();
49135 calculateCurveWidths: function (startPoint, endPoint) {
49136 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49137 (1 - this.velocity_filter_weight) * this._lastVelocity;
49139 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49142 start: this._lastWidth
49145 this._lastVelocity = velocity;
49146 this._lastWidth = newWidth;
49150 drawDot: function (_a) {
49151 var color = _a.color, point = _a.point;
49152 var ctx = this.canvasElCtx();
49153 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49155 this.drawCurveSegment(point.x, point.y, width);
49157 ctx.fillStyle = color;
49161 drawCurve: function (_a) {
49162 var color = _a.color, curve = _a.curve;
49163 var ctx = this.canvasElCtx();
49164 var widthDelta = curve.endWidth - curve.startWidth;
49165 var drawSteps = Math.floor(curve.length()) * 2;
49167 ctx.fillStyle = color;
49168 for (var i = 0; i < drawSteps; i += 1) {
49169 var t = i / drawSteps;
49175 var x = uuu * curve.startPoint.x;
49176 x += 3 * uu * t * curve.control1.x;
49177 x += 3 * u * tt * curve.control2.x;
49178 x += ttt * curve.endPoint.x;
49179 var y = uuu * curve.startPoint.y;
49180 y += 3 * uu * t * curve.control1.y;
49181 y += 3 * u * tt * curve.control2.y;
49182 y += ttt * curve.endPoint.y;
49183 var width = curve.startWidth + ttt * widthDelta;
49184 this.drawCurveSegment(x, y, width);
49190 drawCurveSegment: function (x, y, width) {
49191 var ctx = this.canvasElCtx();
49193 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49194 this.is_empty = false;
49199 var ctx = this.canvasElCtx();
49200 var canvas = this.canvasEl().dom;
49201 ctx.fillStyle = this.bg_color;
49202 ctx.clearRect(0, 0, canvas.width, canvas.height);
49203 ctx.fillRect(0, 0, canvas.width, canvas.height);
49204 this.curve_data = [];
49206 this.is_empty = true;
49211 return this.el.select('input',true).first();
49214 canvasEl: function()
49216 return this.el.select('canvas',true).first();
49219 canvasElCtx: function()
49221 return this.el.select('canvas',true).first().dom.getContext('2d');
49224 getImage: function(type)
49226 if(this.is_empty) {
49231 return this.canvasEl().dom.toDataURL('image/'+type, 1);
49234 drawFromImage: function(img_src)
49236 var img = new Image();
49238 img.onload = function(){
49239 this.canvasElCtx().drawImage(img, 0, 0);
49244 this.is_empty = false;
49247 selectImage: function()
49249 this.fileEl().dom.click();
49252 uploadImage: function(e)
49254 var reader = new FileReader();
49256 reader.onload = function(e){
49257 var img = new Image();
49258 img.onload = function(){
49260 this.canvasElCtx().drawImage(img, 0, 0);
49262 img.src = e.target.result;
49265 reader.readAsDataURL(e.target.files[0]);
49268 // Bezier Point Constructor
49269 Point: (function () {
49270 function Point(x, y, time) {
49273 this.time = time || Date.now();
49275 Point.prototype.distanceTo = function (start) {
49276 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49278 Point.prototype.equals = function (other) {
49279 return this.x === other.x && this.y === other.y && this.time === other.time;
49281 Point.prototype.velocityFrom = function (start) {
49282 return this.time !== start.time
49283 ? this.distanceTo(start) / (this.time - start.time)
49290 // Bezier Constructor
49291 Bezier: (function () {
49292 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49293 this.startPoint = startPoint;
49294 this.control2 = control2;
49295 this.control1 = control1;
49296 this.endPoint = endPoint;
49297 this.startWidth = startWidth;
49298 this.endWidth = endWidth;
49300 Bezier.fromPoints = function (points, widths, scope) {
49301 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49302 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49303 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49305 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49306 var dx1 = s1.x - s2.x;
49307 var dy1 = s1.y - s2.y;
49308 var dx2 = s2.x - s3.x;
49309 var dy2 = s2.y - s3.y;
49310 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49311 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49312 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49313 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49314 var dxm = m1.x - m2.x;
49315 var dym = m1.y - m2.y;
49316 var k = l2 / (l1 + l2);
49317 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49318 var tx = s2.x - cm.x;
49319 var ty = s2.y - cm.y;
49321 c1: new scope.Point(m1.x + tx, m1.y + ty),
49322 c2: new scope.Point(m2.x + tx, m2.y + ty)
49325 Bezier.prototype.length = function () {
49330 for (var i = 0; i <= steps; i += 1) {
49332 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49333 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49335 var xdiff = cx - px;
49336 var ydiff = cy - py;
49337 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49344 Bezier.prototype.point = function (t, start, c1, c2, end) {
49345 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49346 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49347 + (3.0 * c2 * (1.0 - t) * t * t)
49348 + (end * t * t * t);
49353 throttleStroke: function(fn, wait) {
49354 if (wait === void 0) { wait = 250; }
49356 var timeout = null;
49360 var later = function () {
49361 previous = Date.now();
49363 result = fn.apply(storedContext, storedArgs);
49365 storedContext = null;
49369 return function wrapper() {
49371 for (var _i = 0; _i < arguments.length; _i++) {
49372 args[_i] = arguments[_i];
49374 var now = Date.now();
49375 var remaining = wait - (now - previous);
49376 storedContext = this;
49378 if (remaining <= 0 || remaining > wait) {
49380 clearTimeout(timeout);
49384 result = fn.apply(storedContext, storedArgs);
49386 storedContext = null;
49390 else if (!timeout) {
49391 timeout = window.setTimeout(later, remaining);
49401 // old names for form elements
49402 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
49403 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
49404 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
49405 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
49406 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
49407 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
49408 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
49409 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
49410 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
49411 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
49412 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
49413 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
49414 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
49415 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
49416 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
49417 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
49418 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
49419 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
49420 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
49421 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
49422 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
49423 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
49424 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
49425 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
49426 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
49427 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
49429 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
49430 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49432 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
49433 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
49435 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
49436 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49437 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
49438 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator