2 * set the version of bootstrap based on the stylesheet...
6 Roo.bootstrap.version = ( function() {
8 Roo.each(document.styleSheets, function(s) {
9 if ( s.href && s.href.match(/css-bootstrap4/)) {
14 Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
17 })(); Roo.bootstrap.menu = Roo.bootstrap.menu || {};
18 Roo.bootstrap.nav = {};
20 Roo.bootstrap.form = {};Roo.bootstrap.panel = {};Roo.bootstrap.layout = {};/*
22 * Ext JS Library 1.1.1
23 * Copyright(c) 2006-2007, Ext JS, LLC.
25 * Originally Released Under LGPL - original licence link has changed is not relivant.
28 * <script type="text/javascript">
34 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
35 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
36 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
39 * @param {Object} config The config object
41 Roo.Shadow = function(config){
42 Roo.apply(this, config);
43 if(typeof this.mode != "string"){
44 this.mode = this.defaultMode;
46 var o = this.offset, a = {h: 0};
47 var rad = Math.floor(this.offset/2);
48 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
54 a.l -= this.offset + rad;
55 a.t -= this.offset + rad;
66 a.l -= (this.offset - rad);
67 a.t -= this.offset + rad;
69 a.w -= (this.offset - rad)*2;
80 a.l -= (this.offset - rad);
81 a.t -= (this.offset - rad);
83 a.w -= (this.offset + rad + 1);
84 a.h -= (this.offset + rad);
93 Roo.Shadow.prototype = {
96 * The shadow display mode. Supports the following options:<br />
97 * sides: Shadow displays on both sides and bottom only<br />
98 * frame: Shadow displays equally on all four sides<br />
99 * drop: Traditional bottom-right drop shadow (default)
103 * @cfg {String} offset
104 * The number of pixels to offset the shadow from the element (defaults to 4)
112 * Displays the shadow under the target element
113 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
115 show : function(target){
116 target = Roo.get(target);
118 this.el = Roo.Shadow.Pool.pull();
119 if(this.el.dom.nextSibling != target.dom){
120 this.el.insertBefore(target);
123 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
125 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
128 target.getLeft(true),
133 this.el.dom.style.display = "block";
137 * Returns true if the shadow is visible, else false
139 isVisible : function(){
140 return this.el ? true : false;
144 * Direct alignment when values are already available. Show must be called at least once before
145 * calling this method to ensure it is initialized.
146 * @param {Number} left The target element left position
147 * @param {Number} top The target element top position
148 * @param {Number} width The target element width
149 * @param {Number} height The target element height
151 realign : function(l, t, w, h){
155 var a = this.adjusts, d = this.el.dom, s = d.style;
157 s.left = (l+a.l)+"px";
158 s.top = (t+a.t)+"px";
159 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
161 if(s.width != sws || s.height != shs){
165 var cn = d.childNodes;
166 var sww = Math.max(0, (sw-12))+"px";
167 cn[0].childNodes[1].style.width = sww;
168 cn[1].childNodes[1].style.width = sww;
169 cn[2].childNodes[1].style.width = sww;
170 cn[1].style.height = Math.max(0, (sh-12))+"px";
180 this.el.dom.style.display = "none";
181 Roo.Shadow.Pool.push(this.el);
187 * Adjust the z-index of this shadow
188 * @param {Number} zindex The new z-index
190 setZIndex : function(z){
193 this.el.setStyle("z-index", z);
198 // Private utility class that manages the internal Shadow cache
199 Roo.Shadow.Pool = function(){
201 var markup = Roo.isIE ?
202 '<div class="x-ie-shadow"></div>' :
203 '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
208 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
209 sh.autoBoxAdjust = false;
221 * base class for bootstrap elements.
225 Roo.bootstrap = Roo.bootstrap || {};
227 * @class Roo.bootstrap.Component
228 * @extends Roo.Component
230 * @children Roo.bootstrap.Component
231 * Bootstrap Component base class
232 * @cfg {String} cls css class
233 * @cfg {String} style any extra css
234 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
235 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
236 * @cfg {string} dataId cutomer id
237 * @cfg {string} name Specifies name attribute
238 * @cfg {string} tooltip Text for the tooltip
239 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
240 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
243 * Do not use directly - it does not do anything..
244 * @param {Object} config The config object
249 Roo.bootstrap.Component = function(config){
250 Roo.bootstrap.Component.superclass.constructor.call(this, config);
254 * @event childrenrendered
255 * Fires when the children have been rendered..
256 * @param {Roo.bootstrap.Component} this
258 "childrenrendered" : true
267 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
270 allowDomMove : false, // to stop relocations in parent onRender...
280 * Initialize Events for the element
282 initEvents : function() { },
288 can_build_overlaid : true,
290 container_method : false,
297 // returns the parent component..
298 return Roo.ComponentMgr.get(this.parentId)
304 onRender : function(ct, position)
306 // Roo.log("Call onRender: " + this.xtype);
308 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
311 if (this.el.attr('xtype')) {
312 this.el.attr('xtypex', this.el.attr('xtype'));
313 this.el.dom.removeAttribute('xtype');
323 var cfg = Roo.apply({}, this.getAutoCreate());
325 cfg.id = this.id || Roo.id();
327 // fill in the extra attributes
328 if (this.xattr && typeof(this.xattr) =='object') {
329 for (var i in this.xattr) {
330 cfg[i] = this.xattr[i];
335 cfg.dataId = this.dataId;
339 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
342 if (this.style) { // fixme needs to support more complex style data.
343 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
347 cfg.name = this.name;
350 this.el = ct.createChild(cfg, position);
353 this.tooltipEl().attr('tooltip', this.tooltip);
356 if(this.tabIndex !== undefined){
357 this.el.dom.setAttribute('tabIndex', this.tabIndex);
364 * Fetch the element to add children to
365 * @return {Roo.Element} defaults to this.el
367 getChildContainer : function()
371 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
373 return Roo.get(document.body);
377 * Fetch the element to display the tooltip on.
378 * @return {Roo.Element} defaults to this.el
380 tooltipEl : function()
385 addxtype : function(tree,cntr)
389 cn = Roo.factory(tree);
390 //Roo.log(['addxtype', cn]);
392 cn.parentType = this.xtype; //??
393 cn.parentId = this.id;
395 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
396 if (typeof(cn.container_method) == 'string') {
397 cntr = cn.container_method;
401 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
403 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
405 var build_from_html = Roo.XComponent.build_from_html;
407 var is_body = (tree.xtype == 'Body') ;
409 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
411 var self_cntr_el = Roo.get(this[cntr](false));
413 // do not try and build conditional elements
414 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
418 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
419 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
420 return this.addxtypeChild(tree,cntr, is_body);
423 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
426 return this.addxtypeChild(Roo.apply({}, tree),cntr);
429 Roo.log('skipping render');
435 if (!build_from_html) {
439 // this i think handles overlaying multiple children of the same type
440 // with the sam eelement.. - which might be buggy..
442 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
448 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
452 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
459 addxtypeChild : function (tree, cntr, is_body)
461 Roo.debug && Roo.log('addxtypeChild:' + cntr);
463 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
466 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
467 (typeof(tree['flexy:foreach']) != 'undefined');
471 skip_children = false;
472 // render the element if it's not BODY.
475 // if parent was disabled, then do not try and create the children..
476 if(!this[cntr](true)){
481 cn = Roo.factory(tree);
483 cn.parentType = this.xtype; //??
484 cn.parentId = this.id;
486 var build_from_html = Roo.XComponent.build_from_html;
489 // does the container contain child eleemnts with 'xtype' attributes.
490 // that match this xtype..
491 // note - when we render we create these as well..
492 // so we should check to see if body has xtype set.
493 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
495 var self_cntr_el = Roo.get(this[cntr](false));
496 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
498 //Roo.log(Roo.XComponent.build_from_html);
499 //Roo.log("got echild:");
502 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
503 // and are not displayed -this causes this to use up the wrong element when matching.
504 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
507 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
508 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
514 //echild.dom.removeAttribute('xtype');
516 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
517 Roo.debug && Roo.log(self_cntr_el);
518 Roo.debug && Roo.log(echild);
519 Roo.debug && Roo.log(cn);
525 // if object has flexy:if - then it may or may not be rendered.
526 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
527 // skip a flexy if element.
528 Roo.debug && Roo.log('skipping render');
529 Roo.debug && Roo.log(tree);
531 Roo.debug && Roo.log('skipping all children');
532 skip_children = true;
537 // actually if flexy:foreach is found, we really want to create
538 // multiple copies here...
540 //Roo.log(this[cntr]());
541 // some elements do not have render methods.. like the layouts...
543 if(this[cntr](true) === false){
548 cn.render && cn.render(this[cntr](true));
551 // then add the element..
558 if (typeof (tree.menu) != 'undefined') {
559 tree.menu.parentType = cn.xtype;
560 tree.menu.triggerEl = cn.el;
561 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
565 if (!tree.items || !tree.items.length) {
567 //Roo.log(["no children", this]);
572 var items = tree.items;
575 //Roo.log(items.length);
577 if (!skip_children) {
578 for(var i =0;i < items.length;i++) {
579 // Roo.log(['add child', items[i]]);
580 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
586 //Roo.log("fire childrenrendered");
588 cn.fireEvent('childrenrendered', this);
594 * Set the element that will be used to show or hide
596 setVisibilityEl : function(el)
598 this.visibilityEl = el;
602 * Get the element that will be used to show or hide
604 getVisibilityEl : function()
606 if (typeof(this.visibilityEl) == 'object') {
607 return this.visibilityEl;
610 if (typeof(this.visibilityEl) == 'string') {
611 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
618 * Show a component - removes 'hidden' class
622 if(!this.getVisibilityEl()){
626 this.getVisibilityEl().removeClass(['hidden','d-none']);
628 this.fireEvent('show', this);
633 * Hide a component - adds 'hidden' class
637 if(!this.getVisibilityEl()){
641 this.getVisibilityEl().addClass(['hidden','d-none']);
643 this.fireEvent('hide', this);
656 * @class Roo.bootstrap.Element
657 * @extends Roo.bootstrap.Component
658 * @children Roo.bootstrap.Component
659 * Bootstrap Element class (basically a DIV used to make random stuff )
661 * @cfg {String} html contents of the element
662 * @cfg {String} tag tag of the element
663 * @cfg {String} cls class of the element
664 * @cfg {Boolean} preventDefault (true|false) default false
665 * @cfg {Boolean} clickable (true|false) default false
666 * @cfg {String} role default blank - set to button to force cursor pointer
670 * Create a new Element
671 * @param {Object} config The config object
674 Roo.bootstrap.Element = function(config){
675 Roo.bootstrap.Element.superclass.constructor.call(this, config);
681 * When a element is chick
682 * @param {Roo.bootstrap.Element} this
683 * @param {Roo.EventObject} e
691 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
696 preventDefault: false,
701 getAutoCreate : function(){
705 // cls: this.cls, double assign in parent class Component.js :: onRender
708 if (this.role !== false) {
709 cfg.role = this.role;
715 initEvents: function()
717 Roo.bootstrap.Element.superclass.initEvents.call(this);
720 this.el.on('click', this.onClick, this);
726 onClick : function(e)
728 if(this.preventDefault){
732 this.fireEvent('click', this, e); // why was this double click before?
740 getValue : function()
742 return this.el.dom.innerHTML;
745 setValue : function(value)
747 this.el.dom.innerHTML = value;
762 * @class Roo.bootstrap.DropTarget
763 * @extends Roo.bootstrap.Element
764 * Bootstrap DropTarget class
766 * @cfg {string} name dropable name
769 * Create a new Dropable Area
770 * @param {Object} config The config object
773 Roo.bootstrap.DropTarget = function(config){
774 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
780 * When a element is chick
781 * @param {Roo.bootstrap.Element} this
782 * @param {Roo.EventObject} e
788 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
791 getAutoCreate : function(){
796 initEvents: function()
798 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
799 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
802 drop : this.dragDrop.createDelegate(this),
803 enter : this.dragEnter.createDelegate(this),
804 out : this.dragOut.createDelegate(this),
805 over : this.dragOver.createDelegate(this)
809 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
812 dragDrop : function(source,e,data)
814 // user has to decide how to impliment this.
817 //this.fireEvent('drop', this, source, e ,data);
821 dragEnter : function(n, dd, e, data)
823 // probably want to resize the element to match the dropped element..
825 this.originalSize = this.el.getSize();
826 this.el.setSize( n.el.getSize());
827 this.dropZone.DDM.refreshCache(this.name);
828 Roo.log([n, dd, e, data]);
831 dragOut : function(value)
833 // resize back to normal
835 this.el.setSize(this.originalSize);
836 this.dropZone.resetConstraints();
839 dragOver : function()
856 * @class Roo.bootstrap.Body
857 * @extends Roo.bootstrap.Component
858 * @children Roo.bootstrap.Component
859 * @parent none builder
860 * Bootstrap Body class
864 * @param {Object} config The config object
867 Roo.bootstrap.Body = function(config){
869 config = config || {};
871 Roo.bootstrap.Body.superclass.constructor.call(this, config);
872 this.el = Roo.get(config.el ? config.el : document.body );
873 if (this.cls && this.cls.length) {
874 Roo.get(document.body).addClass(this.cls);
878 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
880 is_body : true,// just to make sure it's constructed?
885 onRender : function(ct, position)
887 /* Roo.log("Roo.bootstrap.Body - onRender");
888 if (this.cls && this.cls.length) {
889 Roo.get(document.body).addClass(this.cls);
908 * @class Roo.bootstrap.ButtonGroup
909 * @extends Roo.bootstrap.Component
910 * Bootstrap ButtonGroup class
911 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
913 * @cfg {String} size lg | sm | xs (default empty normal)
914 * @cfg {String} align vertical | justified (default none)
915 * @cfg {String} direction up | down (default down)
916 * @cfg {Boolean} toolbar false | true
917 * @cfg {Boolean} btn true | false
922 * @param {Object} config The config object
925 Roo.bootstrap.ButtonGroup = function(config){
926 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
929 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
937 getAutoCreate : function(){
943 cfg.html = this.html || cfg.html;
954 if (['vertical','justified'].indexOf(this.align)!==-1) {
955 cfg.cls = 'btn-group-' + this.align;
957 if (this.align == 'justified') {
958 console.log(this.items);
962 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
963 cfg.cls += ' btn-group-' + this.size;
966 if (this.direction == 'up') {
967 cfg.cls += ' dropup' ;
973 * Add a button to the group (similar to NavItem API.)
975 addItem : function(cfg)
977 var cn = new Roo.bootstrap.Button(cfg);
979 cn.parentId = this.id;
980 cn.onRender(this.el, null);
994 * @class Roo.bootstrap.Button
995 * @extends Roo.bootstrap.Component
996 * Bootstrap Button class
997 * @cfg {String} html The button content
998 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
999 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1000 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1001 * @cfg {String} size (lg|sm|xs)
1002 * @cfg {String} tag (a|input|submit)
1003 * @cfg {String} href empty or href
1004 * @cfg {Boolean} disabled default false;
1005 * @cfg {Boolean} isClose default false;
1006 * @cfg {String} glyphicon depricated - use fa
1007 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1008 * @cfg {String} badge text for badge
1009 * @cfg {String} theme (default|glow)
1010 * @cfg {Boolean} inverse dark themed version
1011 * @cfg {Boolean} toggle is it a slidy toggle button
1012 * @cfg {Boolean} pressed default null - if the button ahs active state
1013 * @cfg {String} ontext text for on slidy toggle state
1014 * @cfg {String} offtext text for off slidy toggle state
1015 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1016 * @cfg {Boolean} removeClass remove the standard class..
1017 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1018 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1019 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1022 * Create a new button
1023 * @param {Object} config The config object
1027 Roo.bootstrap.Button = function(config){
1028 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1034 * When a button is pressed
1035 * @param {Roo.bootstrap.Button} btn
1036 * @param {Roo.EventObject} e
1041 * When a button is double clicked
1042 * @param {Roo.bootstrap.Button} btn
1043 * @param {Roo.EventObject} e
1048 * After the button has been toggles
1049 * @param {Roo.bootstrap.Button} btn
1050 * @param {Roo.EventObject} e
1051 * @param {boolean} pressed (also available as button.pressed)
1057 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1078 preventDefault: true,
1087 getAutoCreate : function(){
1095 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1096 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1097 this.tag = 'button';
1101 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1103 if (this.toggle == true) {
1106 cls: 'slider-frame roo-button',
1110 'data-on-text':'ON',
1111 'data-off-text':'OFF',
1112 cls: 'slider-button',
1117 // why are we validating the weights?
1118 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1119 cfg.cls += ' ' + this.weight;
1126 cfg.cls += ' close';
1128 cfg["aria-hidden"] = true;
1130 cfg.html = "×";
1136 if (this.theme==='default') {
1137 cfg.cls = 'btn roo-button';
1139 //if (this.parentType != 'Navbar') {
1140 this.weight = this.weight.length ? this.weight : 'default';
1142 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1144 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1145 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1146 cfg.cls += ' btn-' + outline + weight;
1147 if (this.weight == 'default') {
1149 cfg.cls += ' btn-' + this.weight;
1152 } else if (this.theme==='glow') {
1155 cfg.cls = 'btn-glow roo-button';
1157 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1159 cfg.cls += ' ' + this.weight;
1165 this.cls += ' inverse';
1169 if (this.active || this.pressed === true) {
1170 cfg.cls += ' active';
1173 if (this.disabled) {
1174 cfg.disabled = 'disabled';
1178 Roo.log('changing to ul' );
1180 this.glyphicon = 'caret';
1181 if (Roo.bootstrap.version == 4) {
1182 this.fa = 'caret-down';
1187 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1189 //gsRoo.log(this.parentType);
1190 if (this.parentType === 'Navbar' && !this.parent().bar) {
1191 Roo.log('changing to li?');
1200 href : this.href || '#'
1203 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1204 cfg.cls += ' dropdown';
1211 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1213 if (this.glyphicon) {
1214 cfg.html = ' ' + cfg.html;
1219 cls: 'glyphicon glyphicon-' + this.glyphicon
1224 cfg.html = ' ' + cfg.html;
1229 cls: 'fa fas fa-' + this.fa
1239 // cfg.cls='btn roo-button';
1243 var value = cfg.html;
1248 cls: 'glyphicon glyphicon-' + this.glyphicon,
1255 cls: 'fa fas fa-' + this.fa,
1260 var bw = this.badge_weight.length ? this.badge_weight :
1261 (this.weight.length ? this.weight : 'secondary');
1262 bw = bw == 'default' ? 'secondary' : bw;
1268 cls: 'badge badge-' + bw,
1277 cfg.cls += ' dropdown';
1278 cfg.html = typeof(cfg.html) != 'undefined' ?
1279 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1282 if (cfg.tag !== 'a' && this.href !== '') {
1283 throw "Tag must be a to set href.";
1284 } else if (this.href.length > 0) {
1285 cfg.href = this.href;
1288 if(this.removeClass){
1293 cfg.target = this.target;
1298 initEvents: function() {
1299 // Roo.log('init events?');
1300 // Roo.log(this.el.dom);
1303 if (typeof (this.menu) != 'undefined') {
1304 this.menu.parentType = this.xtype;
1305 this.menu.triggerEl = this.el;
1306 this.addxtype(Roo.apply({}, this.menu));
1310 if (this.el.hasClass('roo-button')) {
1311 this.el.on('click', this.onClick, this);
1312 this.el.on('dblclick', this.onDblClick, this);
1314 this.el.select('.roo-button').on('click', this.onClick, this);
1315 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1319 if(this.removeClass){
1320 this.el.on('click', this.onClick, this);
1323 if (this.group === true) {
1324 if (this.pressed === false || this.pressed === true) {
1327 this.pressed = false;
1328 this.setActive(this.pressed);
1333 this.el.enableDisplayMode();
1336 onClick : function(e)
1338 if (this.disabled) {
1342 Roo.log('button on click ');
1343 if(this.href === '' || this.preventDefault){
1352 this.setActive(true);
1353 var pi = this.parent().items;
1354 for (var i = 0;i < pi.length;i++) {
1355 if (this == pi[i]) {
1358 if (pi[i].el.hasClass('roo-button')) {
1359 pi[i].setActive(false);
1362 this.fireEvent('click', this, e);
1366 if (this.pressed === true || this.pressed === false) {
1367 this.toggleActive(e);
1371 this.fireEvent('click', this, e);
1373 onDblClick: function(e)
1375 if (this.disabled) {
1378 if(this.preventDefault){
1381 this.fireEvent('dblclick', this, e);
1384 * Enables this button
1388 this.disabled = false;
1389 this.el.removeClass('disabled');
1390 this.el.dom.removeAttribute("disabled");
1394 * Disable this button
1396 disable : function()
1398 this.disabled = true;
1399 this.el.addClass('disabled');
1400 this.el.attr("disabled", "disabled")
1403 * sets the active state on/off,
1404 * @param {Boolean} state (optional) Force a particular state
1406 setActive : function(v) {
1408 this.el[v ? 'addClass' : 'removeClass']('active');
1412 * toggles the current active state
1414 toggleActive : function(e)
1416 this.setActive(!this.pressed); // this modifies pressed...
1417 this.fireEvent('toggle', this, e, this.pressed);
1420 * get the current active state
1421 * @return {boolean} true if it's active
1423 isActive : function()
1425 return this.el.hasClass('active');
1428 * set the text of the first selected button
1430 setText : function(str)
1432 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1435 * get the text of the first selected button
1437 getText : function()
1439 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1442 setWeight : function(str)
1444 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1447 var outline = this.outline ? 'outline-' : '';
1448 if (str == 'default') {
1449 this.el.addClass('btn-default btn-outline-secondary');
1452 this.el.addClass('btn-' + outline + str);
1457 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1459 Roo.bootstrap.Button.weights = [
1479 * @class Roo.bootstrap.Column
1480 * @extends Roo.bootstrap.Component
1481 * @children Roo.bootstrap.Component
1482 * Bootstrap Column class
1483 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1484 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1485 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1486 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1487 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1488 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1489 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1490 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1493 * @cfg {Boolean} hidden (true|false) hide the element
1494 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1495 * @cfg {String} fa (ban|check|...) font awesome icon
1496 * @cfg {Number} fasize (1|2|....) font awsome size
1498 * @cfg {String} icon (info-sign|check|...) glyphicon name
1500 * @cfg {String} html content of column.
1503 * Create a new Column
1504 * @param {Object} config The config object
1507 Roo.bootstrap.Column = function(config){
1508 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1511 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1529 getAutoCreate : function(){
1530 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1538 var sizes = ['xs','sm','md','lg'];
1539 sizes.map(function(size ,ix){
1540 //Roo.log( size + ':' + settings[size]);
1542 if (settings[size+'off'] !== false) {
1543 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1546 if (settings[size] === false) {
1550 if (!settings[size]) { // 0 = hidden
1551 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1553 for (var i = ix; i > -1; i--) {
1554 cfg.cls += ' d-' + sizes[i] + '-none';
1560 cfg.cls += ' col-' + size + '-' + settings[size] + (
1561 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1567 cfg.cls += ' hidden';
1570 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1571 cfg.cls +=' alert alert-' + this.alert;
1575 if (this.html.length) {
1576 cfg.html = this.html;
1580 if (this.fasize > 1) {
1581 fasize = ' fa-' + this.fasize + 'x';
1583 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1588 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1607 * @class Roo.bootstrap.Container
1608 * @extends Roo.bootstrap.Component
1609 * @children Roo.bootstrap.Component
1611 * Bootstrap Container class
1612 * @cfg {Boolean} jumbotron is it a jumbotron element
1613 * @cfg {String} html content of element
1614 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1615 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1616 * @cfg {String} header content of header (for panel)
1617 * @cfg {String} footer content of footer (for panel)
1618 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1619 * @cfg {String} tag (header|aside|section) type of HTML tag.
1620 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1621 * @cfg {String} fa font awesome icon
1622 * @cfg {String} icon (info-sign|check|...) glyphicon name
1623 * @cfg {Boolean} hidden (true|false) hide the element
1624 * @cfg {Boolean} expandable (true|false) default false
1625 * @cfg {Boolean} expanded (true|false) default true
1626 * @cfg {String} rheader contet on the right of header
1627 * @cfg {Boolean} clickable (true|false) default false
1631 * Create a new Container
1632 * @param {Object} config The config object
1635 Roo.bootstrap.Container = function(config){
1636 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1642 * After the panel has been expand
1644 * @param {Roo.bootstrap.Container} this
1649 * After the panel has been collapsed
1651 * @param {Roo.bootstrap.Container} this
1656 * When a element is chick
1657 * @param {Roo.bootstrap.Container} this
1658 * @param {Roo.EventObject} e
1664 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1682 getChildContainer : function() {
1688 if (this.panel.length) {
1689 return this.el.select('.panel-body',true).first();
1696 getAutoCreate : function(){
1699 tag : this.tag || 'div',
1703 if (this.jumbotron) {
1704 cfg.cls = 'jumbotron';
1709 // - this is applied by the parent..
1711 // cfg.cls = this.cls + '';
1714 if (this.sticky.length) {
1716 var bd = Roo.get(document.body);
1717 if (!bd.hasClass('bootstrap-sticky')) {
1718 bd.addClass('bootstrap-sticky');
1719 Roo.select('html',true).setStyle('height', '100%');
1722 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1726 if (this.well.length) {
1727 switch (this.well) {
1730 cfg.cls +=' well well-' +this.well;
1739 cfg.cls += ' hidden';
1743 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1744 cfg.cls +=' alert alert-' + this.alert;
1749 if (this.panel.length) {
1750 cfg.cls += ' panel panel-' + this.panel;
1752 if (this.header.length) {
1756 if(this.expandable){
1758 cfg.cls = cfg.cls + ' expandable';
1762 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1770 cls : 'panel-title',
1771 html : (this.expandable ? ' ' : '') + this.header
1775 cls: 'panel-header-right',
1781 cls : 'panel-heading',
1782 style : this.expandable ? 'cursor: pointer' : '',
1790 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1795 if (this.footer.length) {
1797 cls : 'panel-footer',
1806 body.html = this.html || cfg.html;
1807 // prefix with the icons..
1809 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1812 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1817 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1818 cfg.cls = 'container';
1824 initEvents: function()
1826 if(this.expandable){
1827 var headerEl = this.headerEl();
1830 headerEl.on('click', this.onToggleClick, this);
1835 this.el.on('click', this.onClick, this);
1840 onToggleClick : function()
1842 var headerEl = this.headerEl();
1858 if(this.fireEvent('expand', this)) {
1860 this.expanded = true;
1862 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1864 this.el.select('.panel-body',true).first().removeClass('hide');
1866 var toggleEl = this.toggleEl();
1872 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1877 collapse : function()
1879 if(this.fireEvent('collapse', this)) {
1881 this.expanded = false;
1883 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1884 this.el.select('.panel-body',true).first().addClass('hide');
1886 var toggleEl = this.toggleEl();
1892 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1896 toggleEl : function()
1898 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1902 return this.el.select('.panel-heading .fa',true).first();
1905 headerEl : function()
1907 if(!this.el || !this.panel.length || !this.header.length){
1911 return this.el.select('.panel-heading',true).first()
1916 if(!this.el || !this.panel.length){
1920 return this.el.select('.panel-body',true).first()
1923 titleEl : function()
1925 if(!this.el || !this.panel.length || !this.header.length){
1929 return this.el.select('.panel-title',true).first();
1932 setTitle : function(v)
1934 var titleEl = this.titleEl();
1940 titleEl.dom.innerHTML = v;
1943 getTitle : function()
1946 var titleEl = this.titleEl();
1952 return titleEl.dom.innerHTML;
1955 setRightTitle : function(v)
1957 var t = this.el.select('.panel-header-right',true).first();
1963 t.dom.innerHTML = v;
1966 onClick : function(e)
1970 this.fireEvent('click', this, e);
1975 * @class Roo.bootstrap.Card
1976 * @extends Roo.bootstrap.Component
1977 * @children Roo.bootstrap.Component
1979 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1982 * possible... may not be implemented..
1983 * @cfg {String} header_image src url of image.
1984 * @cfg {String|Object} header
1985 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1986 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1988 * @cfg {String} title
1989 * @cfg {String} subtitle
1990 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1991 * @cfg {String} footer
1993 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1995 * @cfg {String} margin (0|1|2|3|4|5|auto)
1996 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2003 * @cfg {String} padding (0|1|2|3|4|5)
2004 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2005 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2006 * @cfg {String} padding_left (0|1|2|3|4|5)
2007 * @cfg {String} padding_right (0|1|2|3|4|5)
2008 * @cfg {String} padding_x (0|1|2|3|4|5)
2009 * @cfg {String} padding_y (0|1|2|3|4|5)
2011 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2012 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2017 * @config {Boolean} dragable if this card can be dragged.
2018 * @config {String} drag_group group for drag
2019 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2020 * @config {String} drop_group group for drag
2022 * @config {Boolean} collapsable can the body be collapsed.
2023 * @config {Boolean} collapsed is the body collapsed when rendered...
2024 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2025 * @config {Boolean} rotated is the body rotated when rendered...
2028 * Create a new Container
2029 * @param {Object} config The config object
2032 Roo.bootstrap.Card = function(config){
2033 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2039 * When a element a card is dropped
2040 * @param {Roo.bootstrap.Card} this
2043 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2044 * @param {String} position 'above' or 'below'
2045 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2051 * When a element a card is rotate
2052 * @param {Roo.bootstrap.Card} this
2053 * @param {Roo.Element} n the node being dropped?
2054 * @param {Boolean} rotate status
2059 * When a card element is dragged over ready to drop (return false to block dropable)
2060 * @param {Roo.bootstrap.Card} this
2061 * @param {Object} data from dragdrop
2069 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2074 margin: '', /// may be better in component?
2104 collapsable : false,
2113 childContainer : false,
2114 dropEl : false, /// the dom placeholde element that indicates drop location.
2115 containerEl: false, // body container
2116 bodyEl: false, // card-body
2117 headerContainerEl : false, //
2119 header_imageEl : false,
2122 layoutCls : function()
2126 Roo.log(this.margin_bottom.length);
2127 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2128 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2130 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2131 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2133 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2134 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2138 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2139 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2140 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2144 // more generic support?
2152 // Roo.log("Call onRender: " + this.xtype);
2153 /* We are looking at something like this.
2155 <img src="..." class="card-img-top" alt="...">
2156 <div class="card-body">
2157 <h5 class="card-title">Card title</h5>
2158 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2160 >> this bit is really the body...
2161 <div> << we will ad dthis in hopefully it will not break shit.
2163 ** card text does not actually have any styling...
2165 <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2168 <a href="#" class="card-link">Card link</a>
2171 <div class="card-footer">
2172 <small class="text-muted">Last updated 3 mins ago</small>
2176 getAutoCreate : function(){
2184 if (this.weight.length && this.weight != 'light') {
2185 cfg.cls += ' text-white';
2187 cfg.cls += ' text-dark'; // need as it's nested..
2189 if (this.weight.length) {
2190 cfg.cls += ' bg-' + this.weight;
2193 cfg.cls += ' ' + this.layoutCls();
2196 var hdr_ctr = false;
2197 if (this.header.length) {
2199 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2200 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2208 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2214 if (this.collapsable) {
2217 cls : 'd-block user-select-none',
2221 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2226 hdr.cn.push(hdr_ctr);
2231 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2236 if (this.header_image.length) {
2239 cls : 'card-img-top',
2240 src: this.header_image // escape?
2245 cls : 'card-img-top d-none'
2251 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2255 if (this.collapsable || this.rotateable) {
2258 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2265 if (this.title.length) {
2269 src: this.title // escape?
2273 if (this.subtitle.length) {
2277 src: this.subtitle // escape?
2283 cls : 'roo-card-body-ctr'
2286 if (this.html.length) {
2292 // fixme ? handle objects?
2294 if (this.footer.length) {
2297 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2302 cfg.cn.push({cls : 'card-footer d-none'});
2311 getCardHeader : function()
2313 var ret = this.el.select('.card-header',true).first();
2314 if (ret.hasClass('d-none')) {
2315 ret.removeClass('d-none');
2320 getCardFooter : function()
2322 var ret = this.el.select('.card-footer',true).first();
2323 if (ret.hasClass('d-none')) {
2324 ret.removeClass('d-none');
2329 getCardImageTop : function()
2331 var ret = this.header_imageEl;
2332 if (ret.hasClass('d-none')) {
2333 ret.removeClass('d-none');
2339 getChildContainer : function()
2345 return this.el.select('.roo-card-body-ctr',true).first();
2348 initEvents: function()
2350 this.bodyEl = this.el.select('.card-body',true).first();
2351 this.containerEl = this.getChildContainer();
2353 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2354 containerScroll: true,
2355 ddGroup: this.drag_group || 'default_card_drag_group'
2357 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2359 if (this.dropable) {
2360 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2361 containerScroll: true,
2362 ddGroup: this.drop_group || 'default_card_drag_group'
2364 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2365 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2366 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2367 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2368 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2371 if (this.collapsable) {
2372 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2374 if (this.rotateable) {
2375 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2377 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2379 this.footerEl = this.el.select('.card-footer',true).first();
2380 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2381 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2382 this.headerEl = this.el.select('.card-header',true).first();
2385 this.el.addClass('roo-card-rotated');
2386 this.fireEvent('rotate', this, true);
2388 this.header_imageEl = this.el.select('.card-img-top',true).first();
2389 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2392 getDragData : function(e)
2394 var target = this.getEl();
2396 //this.handleSelection(e);
2401 nodes: this.getEl(),
2406 dragData.ddel = target.dom ; // the div element
2407 Roo.log(target.getWidth( ));
2408 dragData.ddel.style.width = target.getWidth() + 'px';
2415 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2416 * whole Element becomes the target, and this causes the drop gesture to append.
2418 * Returns an object:
2421 position : 'below' or 'above'
2422 card : relateive to card OBJECT (or true for no cards listed)
2423 items_n : relative to nth item in list
2424 card_n : relative to nth card in list
2429 getTargetFromEvent : function(e, dragged_card_el)
2431 var target = e.getTarget();
2432 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2433 target = target.parentNode;
2444 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2445 // see if target is one of the 'cards'...
2448 //Roo.log(this.items.length);
2451 var last_card_n = 0;
2453 for (var i = 0;i< this.items.length;i++) {
2455 if (!this.items[i].el.hasClass('card')) {
2458 pos = this.getDropPoint(e, this.items[i].el.dom);
2460 cards_len = ret.cards.length;
2461 //Roo.log(this.items[i].el.dom.id);
2462 ret.cards.push(this.items[i]);
2464 if (ret.card_n < 0 && pos == 'above') {
2465 ret.position = cards_len > 0 ? 'below' : pos;
2466 ret.items_n = i > 0 ? i - 1 : 0;
2467 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2468 ret.card = ret.cards[ret.card_n];
2471 if (!ret.cards.length) {
2473 ret.position = 'below';
2477 // could not find a card.. stick it at the end..
2478 if (ret.card_n < 0) {
2479 ret.card_n = last_card_n;
2480 ret.card = ret.cards[last_card_n];
2481 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2482 ret.position = 'below';
2485 if (this.items[ret.items_n].el == dragged_card_el) {
2489 if (ret.position == 'below') {
2490 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2492 if (card_after && card_after.el == dragged_card_el) {
2499 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2501 if (card_before && card_before.el == dragged_card_el) {
2508 onNodeEnter : function(n, dd, e, data){
2511 onNodeOver : function(n, dd, e, data)
2514 var target_info = this.getTargetFromEvent(e,data.source.el);
2515 if (target_info === false) {
2516 this.dropPlaceHolder('hide');
2519 Roo.log(['getTargetFromEvent', target_info ]);
2522 if (this.fireEvent('cardover', this, [ data ]) === false) {
2526 this.dropPlaceHolder('show', target_info,data);
2530 onNodeOut : function(n, dd, e, data){
2531 this.dropPlaceHolder('hide');
2534 onNodeDrop : function(n, dd, e, data)
2537 // call drop - return false if
2539 // this could actually fail - if the Network drops..
2540 // we will ignore this at present..- client should probably reload
2541 // the whole set of cards if stuff like that fails.
2544 var info = this.getTargetFromEvent(e,data.source.el);
2545 if (info === false) {
2548 this.dropPlaceHolder('hide');
2552 this.acceptCard(data.source, info.position, info.card, info.items_n);
2556 firstChildCard : function()
2558 for (var i = 0;i< this.items.length;i++) {
2560 if (!this.items[i].el.hasClass('card')) {
2563 return this.items[i];
2565 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2570 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2572 acceptCard : function(move_card, position, next_to_card )
2574 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2578 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2580 move_card.parent().removeCard(move_card);
2583 var dom = move_card.el.dom;
2584 dom.style.width = ''; // clear with - which is set by drag.
2586 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2587 var cardel = next_to_card.el.dom;
2589 if (position == 'above' ) {
2590 cardel.parentNode.insertBefore(dom, cardel);
2591 } else if (cardel.nextSibling) {
2592 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2594 cardel.parentNode.append(dom);
2597 // card container???
2598 this.containerEl.dom.append(dom);
2601 //FIXME HANDLE card = true
2603 // add this to the correct place in items.
2605 // remove Card from items.
2608 if (this.items.length) {
2610 //Roo.log([info.items_n, info.position, this.items.length]);
2611 for (var i =0; i < this.items.length; i++) {
2612 if (i == to_items_n && position == 'above') {
2613 nitems.push(move_card);
2615 nitems.push(this.items[i]);
2616 if (i == to_items_n && position == 'below') {
2617 nitems.push(move_card);
2620 this.items = nitems;
2621 Roo.log(this.items);
2623 this.items.push(move_card);
2626 move_card.parentId = this.id;
2632 removeCard : function(c)
2634 this.items = this.items.filter(function(e) { return e != c });
2637 dom.parentNode.removeChild(dom);
2638 dom.style.width = ''; // clear with - which is set by drag.
2643 /** Decide whether to drop above or below a View node. */
2644 getDropPoint : function(e, n, dd)
2649 if (n == this.containerEl.dom) {
2652 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2653 var c = t + (b - t) / 2;
2654 var y = Roo.lib.Event.getPageY(e);
2661 onToggleCollapse : function(e)
2663 if (this.collapsed) {
2664 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2665 this.collapsableEl.addClass('show');
2666 this.collapsed = false;
2669 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2670 this.collapsableEl.removeClass('show');
2671 this.collapsed = true;
2676 onToggleRotate : function(e)
2678 this.collapsableEl.removeClass('show');
2679 this.footerEl.removeClass('d-none');
2680 this.el.removeClass('roo-card-rotated');
2681 this.el.removeClass('d-none');
2684 this.collapsableEl.addClass('show');
2685 this.rotated = false;
2686 this.fireEvent('rotate', this, this.rotated);
2689 this.el.addClass('roo-card-rotated');
2690 this.footerEl.addClass('d-none');
2691 this.el.select('.roo-collapsable').removeClass('show');
2693 this.rotated = true;
2694 this.fireEvent('rotate', this, this.rotated);
2698 dropPlaceHolder: function (action, info, data)
2700 if (this.dropEl === false) {
2701 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2705 this.dropEl.removeClass(['d-none', 'd-block']);
2706 if (action == 'hide') {
2708 this.dropEl.addClass('d-none');
2711 // FIXME - info.card == true!!!
2712 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2714 if (info.card !== true) {
2715 var cardel = info.card.el.dom;
2717 if (info.position == 'above') {
2718 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2719 } else if (cardel.nextSibling) {
2720 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2722 cardel.parentNode.append(this.dropEl.dom);
2725 // card container???
2726 this.containerEl.dom.append(this.dropEl.dom);
2729 this.dropEl.addClass('d-block roo-card-dropzone');
2731 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2738 setHeaderText: function(html)
2741 if (this.headerContainerEl) {
2742 this.headerContainerEl.dom.innerHTML = html;
2745 onHeaderImageLoad : function(ev, he)
2747 if (!this.header_image_fit_square) {
2751 var hw = he.naturalHeight / he.naturalWidth;
2754 //var w = he.dom.naturalWidth;
2757 he.style.position = 'relative';
2759 var nw = (ww * (1/hw));
2760 Roo.get(he).setSize( ww * (1/hw), ww);
2761 he.style.left = ((ww - nw)/ 2) + 'px';
2762 he.style.position = 'relative';
2773 * Card header - holder for the card header elements.
2778 * @class Roo.bootstrap.CardHeader
2779 * @extends Roo.bootstrap.Element
2780 * @parent Roo.bootstrap.Card
2781 * @children Roo.bootstrap.Component
2782 * Bootstrap CardHeader class
2784 * Create a new Card Header - that you can embed children into
2785 * @param {Object} config The config object
2788 Roo.bootstrap.CardHeader = function(config){
2789 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2792 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2795 container_method : 'getCardHeader'
2808 * Card footer - holder for the card footer elements.
2813 * @class Roo.bootstrap.CardFooter
2814 * @extends Roo.bootstrap.Element
2815 * @parent Roo.bootstrap.Card
2816 * @children Roo.bootstrap.Component
2817 * Bootstrap CardFooter class
2820 * Create a new Card Footer - that you can embed children into
2821 * @param {Object} config The config object
2824 Roo.bootstrap.CardFooter = function(config){
2825 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2828 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2831 container_method : 'getCardFooter'
2844 * Card header - holder for the card header elements.
2849 * @class Roo.bootstrap.CardImageTop
2850 * @extends Roo.bootstrap.Element
2851 * @parent Roo.bootstrap.Card
2852 * @children Roo.bootstrap.Component
2853 * Bootstrap CardImageTop class
2856 * Create a new Card Image Top container
2857 * @param {Object} config The config object
2860 Roo.bootstrap.CardImageTop = function(config){
2861 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2864 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2867 container_method : 'getCardImageTop'
2882 * @class Roo.bootstrap.ButtonUploader
2883 * @extends Roo.bootstrap.Button
2884 * Bootstrap Button Uploader class - it's a button which when you add files to it
2887 * @cfg {Number} errorTimeout default 3000
2888 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2889 * @cfg {Array} html The button text.
2890 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2893 * Create a new CardUploader
2894 * @param {Object} config The config object
2897 Roo.bootstrap.ButtonUploader = function(config){
2901 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2907 * @event beforeselect
2908 * When button is pressed, before show upload files dialog is shown
2909 * @param {Roo.bootstrap.UploaderButton} this
2912 'beforeselect' : true,
2914 * @event fired when files have been selected,
2915 * When a the download link is clicked
2916 * @param {Roo.bootstrap.UploaderButton} this
2917 * @param {Array} Array of files that have been uploaded
2924 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2927 errorTimeout : 3000,
2931 fileCollection : false,
2936 getAutoCreate : function()
2943 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2951 initEvents : function()
2954 Roo.bootstrap.Button.prototype.initEvents.call(this);
2960 this.urlAPI = (window.createObjectURL && window) ||
2961 (window.URL && URL.revokeObjectURL && URL) ||
2962 (window.webkitURL && webkitURL);
2967 cls : 'd-none roo-card-upload-selector'
2970 if (this.multiple) {
2971 im.multiple = 'multiple';
2973 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2975 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2977 this.selectorEl.on('change', this.onFileSelected, this);
2984 onClick : function(e)
2988 if ( this.fireEvent('beforeselect', this) === false) {
2992 this.selectorEl.dom.click();
2996 onFileSelected : function(e)
3000 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3003 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3004 this.selectorEl.dom.value = '';// hopefully reset..
3006 this.fireEvent('uploaded', this, files );
3014 * addCard - add an Attachment to the uploader
3015 * @param data - the data about the image to upload
3019 title : "Title of file",
3020 is_uploaded : false,
3021 src : "http://.....",
3022 srcfile : { the File upload object },
3023 mimetype : file.type,
3026 .. any other data...
3051 * @class Roo.bootstrap.Img
3052 * @extends Roo.bootstrap.Component
3053 * Bootstrap Img class
3054 * @cfg {Boolean} imgResponsive false | true
3055 * @cfg {String} border rounded | circle | thumbnail
3056 * @cfg {String} src image source
3057 * @cfg {String} alt image alternative text
3058 * @cfg {String} href a tag href
3059 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3060 * @cfg {String} xsUrl xs image source
3061 * @cfg {String} smUrl sm image source
3062 * @cfg {String} mdUrl md image source
3063 * @cfg {String} lgUrl lg image source
3064 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3067 * Create a new Input
3068 * @param {Object} config The config object
3071 Roo.bootstrap.Img = function(config){
3072 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3078 * The img click event for the img.
3079 * @param {Roo.EventObject} e
3084 * The when any image loads
3085 * @param {Roo.EventObject} e
3091 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3093 imgResponsive: true,
3102 backgroundContain : false,
3104 getAutoCreate : function()
3106 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3107 return this.createSingleImg();
3112 cls: 'roo-image-responsive-group',
3117 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3119 if(!_this[size + 'Url']){
3125 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3126 html: _this.html || cfg.html,
3127 src: _this[size + 'Url']
3130 img.cls += ' roo-image-responsive-' + size;
3132 var s = ['xs', 'sm', 'md', 'lg'];
3134 s.splice(s.indexOf(size), 1);
3136 Roo.each(s, function(ss){
3137 img.cls += ' hidden-' + ss;
3140 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3141 cfg.cls += ' img-' + _this.border;
3145 cfg.alt = _this.alt;
3158 a.target = _this.target;
3162 cfg.cn.push((_this.href) ? a : img);
3169 createSingleImg : function()
3173 cls: (this.imgResponsive) ? 'img-responsive' : '',
3175 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3178 if (this.backgroundContain) {
3179 cfg.cls += ' background-contain';
3182 cfg.html = this.html || cfg.html;
3184 if (this.backgroundContain) {
3185 cfg.style="background-image: url(" + this.src + ')';
3187 cfg.src = this.src || cfg.src;
3190 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3191 cfg.cls += ' img-' + this.border;
3208 a.target = this.target;
3213 return (this.href) ? a : cfg;
3216 initEvents: function()
3219 this.el.on('click', this.onClick, this);
3221 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3222 this.el.on('load', this.onImageLoad, this);
3224 // not sure if this works.. not tested
3225 this.el.select('img', true).on('load', this.onImageLoad, this);
3230 onClick : function(e)
3232 Roo.log('img onclick');
3233 this.fireEvent('click', this, e);
3235 onImageLoad: function(e)
3237 Roo.log('img load');
3238 this.fireEvent('load', this, e);
3242 * Sets the url of the image - used to update it
3243 * @param {String} url the url of the image
3246 setSrc : function(url)
3250 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3251 if (this.backgroundContain) {
3252 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3254 this.el.dom.src = url;
3259 this.el.select('img', true).first().dom.src = url;
3275 * @class Roo.bootstrap.Link
3276 * @extends Roo.bootstrap.Component
3277 * @children Roo.bootstrap.Component
3278 * Bootstrap Link Class (eg. '<a href>')
3280 * @cfg {String} alt image alternative text
3281 * @cfg {String} href a tag href
3282 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3283 * @cfg {String} html the content of the link.
3284 * @cfg {String} anchor name for the anchor link
3285 * @cfg {String} fa - favicon
3287 * @cfg {Boolean} preventDefault (true | false) default false
3291 * Create a new Input
3292 * @param {Object} config The config object
3295 Roo.bootstrap.Link = function(config){
3296 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3302 * The img click event for the img.
3303 * @param {Roo.EventObject} e
3309 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3313 preventDefault: false,
3319 getAutoCreate : function()
3321 var html = this.html || '';
3323 if (this.fa !== false) {
3324 html = '<i class="fa fa-' + this.fa + '"></i>';
3329 // anchor's do not require html/href...
3330 if (this.anchor === false) {
3332 cfg.href = this.href || '#';
3334 cfg.name = this.anchor;
3335 if (this.html !== false || this.fa !== false) {
3338 if (this.href !== false) {
3339 cfg.href = this.href;
3343 if(this.alt !== false){
3348 if(this.target !== false) {
3349 cfg.target = this.target;
3355 initEvents: function() {
3357 if(!this.href || this.preventDefault){
3358 this.el.on('click', this.onClick, this);
3362 onClick : function(e)
3364 if(this.preventDefault){
3367 //Roo.log('img onclick');
3368 this.fireEvent('click', this, e);
3381 * @class Roo.bootstrap.Header
3382 * @extends Roo.bootstrap.Component
3383 * @children Roo.bootstrap.Component
3384 * Bootstrap Header class
3387 * @cfg {String} html content of header
3388 * @cfg {Number} level (1|2|3|4|5|6) default 1
3391 * Create a new Header
3392 * @param {Object} config The config object
3396 Roo.bootstrap.Header = function(config){
3397 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3400 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3408 getAutoCreate : function(){
3413 tag: 'h' + (1 *this.level),
3414 html: this.html || ''
3425 * @class Roo.bootstrap.MenuMgr
3427 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3430 Roo.bootstrap.menu.Manager = function(){
3431 var menus, active, groups = {}, attached = false, lastShow = new Date();
3433 // private - called when first menu is created
3436 active = new Roo.util.MixedCollection();
3437 Roo.get(document).addKeyListener(27, function(){
3438 if(active.length > 0){
3446 if(active && active.length > 0){
3447 var c = active.clone();
3457 if(active.length < 1){
3458 Roo.get(document).un("mouseup", onMouseDown);
3466 var last = active.last();
3467 lastShow = new Date();
3470 Roo.get(document).on("mouseup", onMouseDown);
3475 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3476 m.parentMenu.activeChild = m;
3477 }else if(last && last.isVisible()){
3478 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3483 function onBeforeHide(m){
3485 m.activeChild.hide();
3487 if(m.autoHideTimer){
3488 clearTimeout(m.autoHideTimer);
3489 delete m.autoHideTimer;
3494 function onBeforeShow(m){
3495 var pm = m.parentMenu;
3496 if(!pm && !m.allowOtherMenus){
3498 }else if(pm && pm.activeChild && active != m){
3499 pm.activeChild.hide();
3503 // private this should really trigger on mouseup..
3504 function onMouseDown(e){
3505 Roo.log("on Mouse Up");
3507 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3508 Roo.log("MenuManager hideAll");
3517 function onBeforeCheck(mi, state){
3519 var g = groups[mi.group];
3520 for(var i = 0, l = g.length; i < l; i++){
3522 g[i].setChecked(false);
3531 * Hides all menus that are currently visible
3533 hideAll : function(){
3538 register : function(menu){
3542 menus[menu.id] = menu;
3543 menu.on("beforehide", onBeforeHide);
3544 menu.on("hide", onHide);
3545 menu.on("beforeshow", onBeforeShow);
3546 menu.on("show", onShow);
3548 if(g && menu.events["checkchange"]){
3552 groups[g].push(menu);
3553 menu.on("checkchange", onCheck);
3558 * Returns a {@link Roo.menu.Menu} object
3559 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3560 * be used to generate and return a new Menu instance.
3562 get : function(menu){
3563 if(typeof menu == "string"){ // menu id
3565 }else if(menu.events){ // menu instance
3568 /*else if(typeof menu.length == 'number'){ // array of menu items?
3569 return new Roo.bootstrap.Menu({items:menu});
3570 }else{ // otherwise, must be a config
3571 return new Roo.bootstrap.Menu(menu);
3578 unregister : function(menu){
3579 delete menus[menu.id];
3580 menu.un("beforehide", onBeforeHide);
3581 menu.un("hide", onHide);
3582 menu.un("beforeshow", onBeforeShow);
3583 menu.un("show", onShow);
3585 if(g && menu.events["checkchange"]){
3586 groups[g].remove(menu);
3587 menu.un("checkchange", onCheck);
3592 registerCheckable : function(menuItem){
3593 var g = menuItem.group;
3598 groups[g].push(menuItem);
3599 menuItem.on("beforecheckchange", onBeforeCheck);
3604 unregisterCheckable : function(menuItem){
3605 var g = menuItem.group;
3607 groups[g].remove(menuItem);
3608 menuItem.un("beforecheckchange", onBeforeCheck);
3614 * @class Roo.bootstrap.menu.Menu
3615 * @extends Roo.bootstrap.Component
3617 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3619 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3621 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3622 * @cfg {bool} hidden if the menu should be hidden when rendered.
3623 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3624 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3625 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3626 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3630 * @param {Object} config The config objectQ
3634 Roo.bootstrap.menu.Menu = function(config){
3636 if (config.type == 'treeview') {
3637 // normally menu's are drawn attached to the document to handle layering etc..
3638 // however treeview (used by the docs menu is drawn into the parent element)
3639 this.container_method = 'getChildContainer';
3642 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3643 if (this.registerMenu && this.type != 'treeview') {
3644 Roo.bootstrap.menu.Manager.register(this);
3651 * Fires before this menu is displayed (return false to block)
3652 * @param {Roo.menu.Menu} this
3657 * Fires before this menu is hidden (return false to block)
3658 * @param {Roo.menu.Menu} this
3663 * Fires after this menu is displayed
3664 * @param {Roo.menu.Menu} this
3669 * Fires after this menu is hidden
3670 * @param {Roo.menu.Menu} this
3675 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3676 * @param {Roo.menu.Menu} this
3677 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3678 * @param {Roo.EventObject} e
3683 * Fires when the mouse is hovering over this menu
3684 * @param {Roo.menu.Menu} this
3685 * @param {Roo.EventObject} e
3686 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3691 * Fires when the mouse exits this menu
3692 * @param {Roo.menu.Menu} this
3693 * @param {Roo.EventObject} e
3694 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3699 * Fires when a menu item contained in this menu is clicked
3700 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3701 * @param {Roo.EventObject} e
3705 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3708 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3712 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3715 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3717 registerMenu : true,
3719 menuItems :false, // stores the menu items..
3729 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3731 hideTrigger : false,
3736 getChildContainer : function() {
3740 getAutoCreate : function(){
3742 //if (['right'].indexOf(this.align)!==-1) {
3743 // cfg.cn[1].cls += ' pull-right'
3748 cls : 'dropdown-menu shadow' ,
3749 style : 'z-index:1000'
3753 if (this.type === 'submenu') {
3754 cfg.cls = 'submenu active';
3756 if (this.type === 'treeview') {
3757 cfg.cls = 'treeview-menu';
3762 initEvents : function() {
3764 // Roo.log("ADD event");
3765 // Roo.log(this.triggerEl.dom);
3766 if (this.triggerEl) {
3768 this.triggerEl.on('click', this.onTriggerClick, this);
3770 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3772 if (!this.hideTrigger) {
3773 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3774 // dropdown toggle on the 'a' in BS4?
3775 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3777 this.triggerEl.addClass('dropdown-toggle');
3783 this.el.on('touchstart' , this.onTouch, this);
3785 this.el.on('click' , this.onClick, this);
3787 this.el.on("mouseover", this.onMouseOver, this);
3788 this.el.on("mouseout", this.onMouseOut, this);
3792 findTargetItem : function(e)
3794 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3798 //Roo.log(t); Roo.log(t.id);
3800 //Roo.log(this.menuitems);
3801 return this.menuitems.get(t.id);
3803 //return this.items.get(t.menuItemId);
3809 onTouch : function(e)
3811 Roo.log("menu.onTouch");
3812 //e.stopEvent(); this make the user popdown broken
3816 onClick : function(e)
3818 Roo.log("menu.onClick");
3820 var t = this.findTargetItem(e);
3821 if(!t || t.isContainer){
3826 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3827 if(t == this.activeItem && t.shouldDeactivate(e)){
3828 this.activeItem.deactivate();
3829 delete this.activeItem;
3833 this.setActiveItem(t, true);
3841 Roo.log('pass click event');
3845 this.fireEvent("click", this, t, e);
3849 if(!t.href.length || t.href == '#'){
3850 (function() { _this.hide(); }).defer(100);
3855 onMouseOver : function(e){
3856 var t = this.findTargetItem(e);
3859 // if(t.canActivate && !t.disabled){
3860 // this.setActiveItem(t, true);
3864 this.fireEvent("mouseover", this, e, t);
3866 isVisible : function(){
3867 return !this.hidden;
3869 onMouseOut : function(e){
3870 var t = this.findTargetItem(e);
3873 // if(t == this.activeItem && t.shouldDeactivate(e)){
3874 // this.activeItem.deactivate();
3875 // delete this.activeItem;
3878 this.fireEvent("mouseout", this, e, t);
3883 * Displays this menu relative to another element
3884 * @param {String/HTMLElement/Roo.Element} element The element to align to
3885 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3886 * the element (defaults to this.defaultAlign)
3887 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3889 show : function(el, pos, parentMenu)
3891 if (false === this.fireEvent("beforeshow", this)) {
3892 Roo.log("show canceled");
3895 this.parentMenu = parentMenu;
3899 this.el.addClass('show'); // show otherwise we do not know how big we are..
3901 var xy = this.el.getAlignToXY(el, pos);
3903 // bl-tl << left align below
3904 // tl-bl << left align
3906 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3907 // if it goes to far to the right.. -> align left.
3908 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3911 // was left align - go right?
3912 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3915 // goes down the bottom
3916 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3918 var a = this.align.replace('?', '').split('-');
3919 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3923 this.showAt( xy , parentMenu, false);
3926 * Displays this menu at a specific xy position
3927 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3928 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3930 showAt : function(xy, parentMenu, /* private: */_e){
3931 this.parentMenu = parentMenu;
3936 this.fireEvent("beforeshow", this);
3937 //xy = this.el.adjustForConstraints(xy);
3941 this.hideMenuItems();
3942 this.hidden = false;
3943 if (this.triggerEl) {
3944 this.triggerEl.addClass('open');
3947 this.el.addClass('show');
3951 // reassign x when hitting right
3953 // reassign y when hitting bottom
3955 // but the list may align on trigger left or trigger top... should it be a properity?
3957 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3962 this.fireEvent("show", this);
3968 this.doFocus.defer(50, this);
3972 doFocus : function(){
3974 this.focusEl.focus();
3979 * Hides this menu and optionally all parent menus
3980 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3982 hide : function(deep)
3984 if (false === this.fireEvent("beforehide", this)) {
3985 Roo.log("hide canceled");
3988 this.hideMenuItems();
3989 if(this.el && this.isVisible()){
3991 if(this.activeItem){
3992 this.activeItem.deactivate();
3993 this.activeItem = null;
3995 if (this.triggerEl) {
3996 this.triggerEl.removeClass('open');
3999 this.el.removeClass('show');
4001 this.fireEvent("hide", this);
4003 if(deep === true && this.parentMenu){
4004 this.parentMenu.hide(true);
4008 onTriggerClick : function(e)
4010 Roo.log('trigger click');
4012 var target = e.getTarget();
4014 Roo.log(target.nodeName.toLowerCase());
4016 if(target.nodeName.toLowerCase() === 'i'){
4022 onTriggerPress : function(e)
4024 Roo.log('trigger press');
4025 //Roo.log(e.getTarget());
4026 // Roo.log(this.triggerEl.dom);
4028 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4029 var pel = Roo.get(e.getTarget());
4030 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4031 Roo.log('is treeview or dropdown?');
4035 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4039 if (this.isVisible()) {
4045 this.show(this.triggerEl, this.align, false);
4048 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4055 hideMenuItems : function()
4057 Roo.log("hide Menu Items");
4062 this.el.select('.open',true).each(function(aa) {
4064 aa.removeClass('open');
4068 addxtypeChild : function (tree, cntr) {
4069 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4071 this.menuitems.add(comp);
4083 this.getEl().dom.innerHTML = '';
4084 this.menuitems.clear();
4090 * @class Roo.bootstrap.menu.Item
4091 * @extends Roo.bootstrap.Component
4092 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4093 * @parent Roo.bootstrap.menu.Menu
4095 * Bootstrap MenuItem class
4097 * @cfg {String} html the menu label
4098 * @cfg {String} href the link
4099 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4100 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4101 * @cfg {Boolean} active used on sidebars to highlight active itesm
4102 * @cfg {String} fa favicon to show on left of menu item.
4103 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4107 * Create a new MenuItem
4108 * @param {Object} config The config object
4112 Roo.bootstrap.menu.Item = function(config){
4113 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4118 * The raw click event for the entire grid.
4119 * @param {Roo.bootstrap.menu.Item} this
4120 * @param {Roo.EventObject} e
4126 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4130 preventDefault: false,
4131 isContainer : false,
4135 getAutoCreate : function(){
4137 if(this.isContainer){
4140 cls: 'dropdown-menu-item '
4150 cls : 'dropdown-item',
4155 if (this.fa !== false) {
4158 cls : 'fa fa-' + this.fa
4167 cls: 'dropdown-menu-item',
4170 if (this.parent().type == 'treeview') {
4171 cfg.cls = 'treeview-menu';
4174 cfg.cls += ' active';
4179 anc.href = this.href || cfg.cn[0].href ;
4180 ctag.html = this.html || cfg.cn[0].html ;
4184 initEvents: function()
4186 if (this.parent().type == 'treeview') {
4187 this.el.select('a').on('click', this.onClick, this);
4191 this.menu.parentType = this.xtype;
4192 this.menu.triggerEl = this.el;
4193 this.menu = this.addxtype(Roo.apply({}, this.menu));
4197 onClick : function(e)
4199 //Roo.log('item on click ');
4201 if(this.href === false || this.preventDefault){
4204 //this.parent().hideMenuItems();
4206 this.fireEvent('click', this, e);
4220 * @class Roo.bootstrap.menu.Separator
4221 * @extends Roo.bootstrap.Component
4223 * @parent Roo.bootstrap.menu.Menu
4224 * Bootstrap Separator class
4227 * Create a new Separator
4228 * @param {Object} config The config object
4232 Roo.bootstrap.menu.Separator = function(config){
4233 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4236 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4238 getAutoCreate : function(){
4241 cls: 'dropdown-divider divider'
4257 * @class Roo.bootstrap.Modal
4258 * @extends Roo.bootstrap.Component
4259 * @parent none builder
4260 * @children Roo.bootstrap.Component
4261 * Bootstrap Modal class
4262 * @cfg {String} title Title of dialog
4263 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4264 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4265 * @cfg {Boolean} specificTitle default false
4266 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4267 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4268 * @cfg {Boolean} animate default true
4269 * @cfg {Boolean} allow_close default true
4270 * @cfg {Boolean} fitwindow default false
4271 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4272 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4273 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4274 * @cfg {String} size (sm|lg|xl) default empty
4275 * @cfg {Number} max_width set the max width of modal
4276 * @cfg {Boolean} editableTitle can the title be edited
4281 * Create a new Modal Dialog
4282 * @param {Object} config The config object
4285 Roo.bootstrap.Modal = function(config){
4286 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4291 * The raw btnclick event for the button
4292 * @param {Roo.EventObject} e
4297 * Fire when dialog resize
4298 * @param {Roo.bootstrap.Modal} this
4299 * @param {Roo.EventObject} e
4303 * @event titlechanged
4304 * Fire when the editable title has been changed
4305 * @param {Roo.bootstrap.Modal} this
4306 * @param {Roo.EventObject} value
4308 "titlechanged" : true
4311 this.buttons = this.buttons || [];
4314 this.tmpl = Roo.factory(this.tmpl);
4319 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4321 title : 'test dialog',
4331 specificTitle: false,
4333 buttonPosition: 'right',
4355 editableTitle : false,
4357 onRender : function(ct, position)
4359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4362 var cfg = Roo.apply({}, this.getAutoCreate());
4365 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4367 //if (!cfg.name.length) {
4371 cfg.cls += ' ' + this.cls;
4374 cfg.style = this.style;
4376 this.el = Roo.get(document.body).createChild(cfg, position);
4378 //var type = this.el.dom.type;
4381 if(this.tabIndex !== undefined){
4382 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4385 this.dialogEl = this.el.select('.modal-dialog',true).first();
4386 this.bodyEl = this.el.select('.modal-body',true).first();
4387 this.closeEl = this.el.select('.modal-header .close', true).first();
4388 this.headerEl = this.el.select('.modal-header',true).first();
4389 this.titleEl = this.el.select('.modal-title',true).first();
4390 this.footerEl = this.el.select('.modal-footer',true).first();
4392 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4394 //this.el.addClass("x-dlg-modal");
4396 if (this.buttons.length) {
4397 Roo.each(this.buttons, function(bb) {
4398 var b = Roo.apply({}, bb);
4399 b.xns = b.xns || Roo.bootstrap;
4400 b.xtype = b.xtype || 'Button';
4401 if (typeof(b.listeners) == 'undefined') {
4402 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4405 var btn = Roo.factory(b);
4407 btn.render(this.getButtonContainer());
4411 // render the children.
4414 if(typeof(this.items) != 'undefined'){
4415 var items = this.items;
4418 for(var i =0;i < items.length;i++) {
4419 // we force children not to montor widnow resize - as we do that for them.
4420 items[i].monitorWindowResize = false;
4421 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4425 this.items = nitems;
4427 // where are these used - they used to be body/close/footer
4431 //this.el.addClass([this.fieldClass, this.cls]);
4435 getAutoCreate : function()
4437 // we will default to modal-body-overflow - might need to remove or make optional later.
4439 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4440 html : this.html || ''
4445 cls : 'modal-title',
4449 if(this.specificTitle){ // WTF is this?
4454 if (this.allow_close && Roo.bootstrap.version == 3) {
4464 if (this.editableTitle) {
4466 cls: 'form-control roo-editable-title d-none',
4472 if (this.allow_close && Roo.bootstrap.version == 4) {
4482 if(this.size.length){
4483 size = 'modal-' + this.size;
4486 var footer = Roo.bootstrap.version == 3 ?
4488 cls : 'modal-footer',
4492 cls: 'btn-' + this.buttonPosition
4497 { // BS4 uses mr-auto on left buttons....
4498 cls : 'modal-footer'
4509 cls: "modal-dialog " + size,
4512 cls : "modal-content",
4515 cls : 'modal-header',
4530 modal.cls += ' fade';
4536 getChildContainer : function() {
4541 getButtonContainer : function() {
4543 return Roo.bootstrap.version == 4 ?
4544 this.el.select('.modal-footer',true).first()
4545 : this.el.select('.modal-footer div',true).first();
4549 closeClick : function()
4554 initEvents : function()
4556 if (this.allow_close) {
4557 this.closeEl.on('click', this.closeClick, this);
4559 Roo.EventManager.onWindowResize(this.resize, this, true);
4560 if (this.editableTitle) {
4561 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4562 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4563 this.headerEditEl.on('keyup', function(e) {
4564 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4565 this.toggleHeaderInput(false)
4568 this.headerEditEl.on('blur', function(e) {
4569 this.toggleHeaderInput(false)
4578 this.maskEl.setSize(
4579 Roo.lib.Dom.getViewWidth(true),
4580 Roo.lib.Dom.getViewHeight(true)
4583 if (this.fitwindow) {
4585 this.dialogEl.setStyle( { 'max-width' : '100%' });
4587 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4588 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4593 if(this.max_width !== 0) {
4595 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4598 this.setSize(w, this.height);
4602 if(this.max_height) {
4603 this.setSize(w,Math.min(
4605 Roo.lib.Dom.getViewportHeight(true) - 60
4611 if(!this.fit_content) {
4612 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4616 this.setSize(w, Math.min(
4618 this.headerEl.getHeight() +
4619 this.footerEl.getHeight() +
4620 this.getChildHeight(this.bodyEl.dom.childNodes),
4621 Roo.lib.Dom.getViewportHeight(true) - 60)
4627 setSize : function(w,h)
4634 // any layout/border etc.. resize..
4636 this.items.forEach( function(e) {
4637 e.layout ? e.layout() : false;
4646 if (!this.rendered) {
4649 this.toggleHeaderInput(false);
4650 //this.el.setStyle('display', 'block');
4651 this.el.removeClass('hideing');
4652 this.el.dom.style.display='block';
4654 Roo.get(document.body).addClass('modal-open');
4656 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4659 this.el.addClass('show');
4660 this.el.addClass('in');
4663 this.el.addClass('show');
4664 this.el.addClass('in');
4667 // not sure how we can show data in here..
4669 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4672 Roo.get(document.body).addClass("x-body-masked");
4674 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4675 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4676 this.maskEl.dom.style.display = 'block';
4677 this.maskEl.addClass('show');
4682 this.fireEvent('show', this);
4684 // set zindex here - otherwise it appears to be ignored...
4685 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4688 // this is for children that are... layout.Border
4690 this.items.forEach( function(e) {
4691 e.layout ? e.layout() : false;
4699 if(this.fireEvent("beforehide", this) !== false){
4701 this.maskEl.removeClass('show');
4703 this.maskEl.dom.style.display = '';
4704 Roo.get(document.body).removeClass("x-body-masked");
4705 this.el.removeClass('in');
4706 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4708 if(this.animate){ // why
4709 this.el.addClass('hideing');
4710 this.el.removeClass('show');
4712 if (!this.el.hasClass('hideing')) {
4713 return; // it's been shown again...
4716 this.el.dom.style.display='';
4718 Roo.get(document.body).removeClass('modal-open');
4719 this.el.removeClass('hideing');
4723 this.el.removeClass('show');
4724 this.el.dom.style.display='';
4725 Roo.get(document.body).removeClass('modal-open');
4728 this.fireEvent('hide', this);
4731 isVisible : function()
4734 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4738 addButton : function(str, cb)
4742 var b = Roo.apply({}, { html : str } );
4743 b.xns = b.xns || Roo.bootstrap;
4744 b.xtype = b.xtype || 'Button';
4745 if (typeof(b.listeners) == 'undefined') {
4746 b.listeners = { click : cb.createDelegate(this) };
4749 var btn = Roo.factory(b);
4751 btn.render(this.getButtonContainer());
4757 setDefaultButton : function(btn)
4759 //this.el.select('.modal-footer').()
4762 resizeTo: function(w,h)
4764 this.dialogEl.setWidth(w);
4766 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4768 this.bodyEl.setHeight(h - diff);
4770 this.fireEvent('resize', this);
4773 setContentSize : function(w, h)
4777 onButtonClick: function(btn,e)
4780 this.fireEvent('btnclick', btn.name, e);
4783 * Set the title of the Dialog
4784 * @param {String} str new Title
4786 setTitle: function(str) {
4787 this.titleEl.dom.innerHTML = str;
4791 * Set the body of the Dialog
4792 * @param {String} str new Title
4794 setBody: function(str) {
4795 this.bodyEl.dom.innerHTML = str;
4798 * Set the body of the Dialog using the template
4799 * @param {Obj} data - apply this data to the template and replace the body contents.
4801 applyBody: function(obj)
4804 Roo.log("Error - using apply Body without a template");
4807 this.tmpl.overwrite(this.bodyEl, obj);
4810 getChildHeight : function(child_nodes)
4814 child_nodes.length == 0
4819 var child_height = 0;
4821 for(var i = 0; i < child_nodes.length; i++) {
4824 * for modal with tabs...
4825 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4827 var layout_childs = child_nodes[i].childNodes;
4829 for(var j = 0; j < layout_childs.length; j++) {
4831 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4833 var layout_body_childs = layout_childs[j].childNodes;
4835 for(var k = 0; k < layout_body_childs.length; k++) {
4837 if(layout_body_childs[k].classList.contains('navbar')) {
4838 child_height += layout_body_childs[k].offsetHeight;
4842 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4844 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4846 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4848 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4849 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4864 child_height += child_nodes[i].offsetHeight;
4865 // Roo.log(child_nodes[i].offsetHeight);
4868 return child_height;
4870 toggleHeaderInput : function(is_edit)
4872 if (!this.editableTitle) {
4873 return; // not editable.
4875 if (is_edit && this.is_header_editing) {
4876 return; // already editing..
4880 this.headerEditEl.dom.value = this.title;
4881 this.headerEditEl.removeClass('d-none');
4882 this.headerEditEl.dom.focus();
4883 this.titleEl.addClass('d-none');
4885 this.is_header_editing = true;
4888 // flip back to not editing.
4889 this.title = this.headerEditEl.dom.value;
4890 this.headerEditEl.addClass('d-none');
4891 this.titleEl.removeClass('d-none');
4892 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4893 this.is_header_editing = false;
4894 this.fireEvent('titlechanged', this, this.title);
4903 Roo.apply(Roo.bootstrap.Modal, {
4905 * Button config that displays a single OK button
4914 * Button config that displays Yes and No buttons
4930 * Button config that displays OK and Cancel buttons
4945 * Button config that displays Yes, No and Cancel buttons
4970 * messagebox - can be used as a replace
4974 * @class Roo.MessageBox
4975 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4979 Roo.Msg.alert('Status', 'Changes saved successfully.');
4981 // Prompt for user data:
4982 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4984 // process text value...
4988 // Show a dialog using config options:
4990 title:'Save Changes?',
4991 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4992 buttons: Roo.Msg.YESNOCANCEL,
4999 Roo.bootstrap.MessageBox = function(){
5000 var dlg, opt, mask, waitTimer;
5001 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
5002 var buttons, activeTextEl, bwidth;
5006 var handleButton = function(button){
5008 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5012 var handleHide = function(){
5014 dlg.el.removeClass(opt.cls);
5017 // Roo.TaskMgr.stop(waitTimer);
5018 // waitTimer = null;
5023 var updateButtons = function(b){
5026 buttons["ok"].hide();
5027 buttons["cancel"].hide();
5028 buttons["yes"].hide();
5029 buttons["no"].hide();
5030 dlg.footerEl.hide();
5034 dlg.footerEl.show();
5035 for(var k in buttons){
5036 if(typeof buttons[k] != "function"){
5039 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5040 width += buttons[k].el.getWidth()+15;
5050 var handleEsc = function(d, k, e){
5051 if(opt && opt.closable !== false){
5061 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5062 * @return {Roo.BasicDialog} The BasicDialog element
5064 getDialog : function(){
5066 dlg = new Roo.bootstrap.Modal( {
5069 //constraintoviewport:false,
5071 //collapsible : false,
5076 //buttonAlign:"center",
5077 closeClick : function(){
5078 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5081 handleButton("cancel");
5086 dlg.on("hide", handleHide);
5088 //dlg.addKeyListener(27, handleEsc);
5090 this.buttons = buttons;
5091 var bt = this.buttonText;
5092 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5093 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5094 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5095 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5097 bodyEl = dlg.bodyEl.createChild({
5099 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5100 '<textarea class="roo-mb-textarea"></textarea>' +
5101 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5103 msgEl = bodyEl.dom.firstChild;
5104 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5105 textboxEl.enableDisplayMode();
5106 textboxEl.addKeyListener([10,13], function(){
5107 if(dlg.isVisible() && opt && opt.buttons){
5110 }else if(opt.buttons.yes){
5111 handleButton("yes");
5115 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5116 textareaEl.enableDisplayMode();
5117 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5118 progressEl.enableDisplayMode();
5120 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5121 var pf = progressEl.dom.firstChild;
5123 pp = Roo.get(pf.firstChild);
5124 pp.setHeight(pf.offsetHeight);
5132 * Updates the message box body text
5133 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5134 * the XHTML-compliant non-breaking space character '&#160;')
5135 * @return {Roo.MessageBox} This message box
5137 updateText : function(text)
5139 if(!dlg.isVisible() && !opt.width){
5140 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5141 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5143 msgEl.innerHTML = text || ' ';
5145 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5146 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5148 Math.min(opt.width || cw , this.maxWidth),
5149 Math.max(opt.minWidth || this.minWidth, bwidth)
5152 activeTextEl.setWidth(w);
5154 if(dlg.isVisible()){
5155 dlg.fixedcenter = false;
5157 // to big, make it scroll. = But as usual stupid IE does not support
5160 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5161 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5162 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5164 bodyEl.dom.style.height = '';
5165 bodyEl.dom.style.overflowY = '';
5168 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5170 bodyEl.dom.style.overflowX = '';
5173 dlg.setContentSize(w, bodyEl.getHeight());
5174 if(dlg.isVisible()){
5175 dlg.fixedcenter = true;
5181 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5182 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5183 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5184 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5185 * @return {Roo.MessageBox} This message box
5187 updateProgress : function(value, text){
5189 this.updateText(text);
5192 if (pp) { // weird bug on my firefox - for some reason this is not defined
5193 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5194 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5200 * Returns true if the message box is currently displayed
5201 * @return {Boolean} True if the message box is visible, else false
5203 isVisible : function(){
5204 return dlg && dlg.isVisible();
5208 * Hides the message box if it is displayed
5211 if(this.isVisible()){
5217 * Displays a new message box, or reinitializes an existing message box, based on the config options
5218 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5219 * The following config object properties are supported:
5221 Property Type Description
5222 ---------- --------------- ------------------------------------------------------------------------------------
5223 animEl String/Element An id or Element from which the message box should animate as it opens and
5224 closes (defaults to undefined)
5225 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5226 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5227 closable Boolean False to hide the top-right close button (defaults to true). Note that
5228 progress and wait dialogs will ignore this property and always hide the
5229 close button as they can only be closed programmatically.
5230 cls String A custom CSS class to apply to the message box element
5231 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5232 displayed (defaults to 75)
5233 fn Function A callback function to execute after closing the dialog. The arguments to the
5234 function will be btn (the name of the button that was clicked, if applicable,
5235 e.g. "ok"), and text (the value of the active text field, if applicable).
5236 Progress and wait dialogs will ignore this option since they do not respond to
5237 user actions and can only be closed programmatically, so any required function
5238 should be called by the same code after it closes the dialog.
5239 icon String A CSS class that provides a background image to be used as an icon for
5240 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5241 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5242 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5243 modal Boolean False to allow user interaction with the page while the message box is
5244 displayed (defaults to true)
5245 msg String A string that will replace the existing message box body text (defaults
5246 to the XHTML-compliant non-breaking space character ' ')
5247 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5248 progress Boolean True to display a progress bar (defaults to false)
5249 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5250 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5251 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5252 title String The title text
5253 value String The string value to set into the active textbox element if displayed
5254 wait Boolean True to display a progress bar (defaults to false)
5255 width Number The width of the dialog in pixels
5262 msg: 'Please enter your address:',
5264 buttons: Roo.MessageBox.OKCANCEL,
5267 animEl: 'addAddressBtn'
5270 * @param {Object} config Configuration options
5271 * @return {Roo.MessageBox} This message box
5273 show : function(options)
5276 // this causes nightmares if you show one dialog after another
5277 // especially on callbacks..
5279 if(this.isVisible()){
5282 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5283 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5284 Roo.log("New Dialog Message:" + options.msg )
5285 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5286 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5289 var d = this.getDialog();
5291 d.setTitle(opt.title || " ");
5292 d.closeEl.setDisplayed(opt.closable !== false);
5293 activeTextEl = textboxEl;
5294 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5299 textareaEl.setHeight(typeof opt.multiline == "number" ?
5300 opt.multiline : this.defaultTextHeight);
5301 activeTextEl = textareaEl;
5310 progressEl.setDisplayed(opt.progress === true);
5312 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5314 this.updateProgress(0);
5315 activeTextEl.dom.value = opt.value || "";
5317 dlg.setDefaultButton(activeTextEl);
5319 var bs = opt.buttons;
5323 }else if(bs && bs.yes){
5324 db = buttons["yes"];
5326 dlg.setDefaultButton(db);
5328 bwidth = updateButtons(opt.buttons);
5329 this.updateText(opt.msg);
5331 d.el.addClass(opt.cls);
5333 d.proxyDrag = opt.proxyDrag === true;
5334 d.modal = opt.modal !== false;
5335 d.mask = opt.modal !== false ? mask : false;
5337 // force it to the end of the z-index stack so it gets a cursor in FF
5338 document.body.appendChild(dlg.el.dom);
5339 d.animateTarget = null;
5340 d.show(options.animEl);
5346 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5347 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5348 * and closing the message box when the process is complete.
5349 * @param {String} title The title bar text
5350 * @param {String} msg The message box body text
5351 * @return {Roo.MessageBox} This message box
5353 progress : function(title, msg){
5360 minWidth: this.minProgressWidth,
5367 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5368 * If a callback function is passed it will be called after the user clicks the button, and the
5369 * id of the button that was clicked will be passed as the only parameter to the callback
5370 * (could also be the top-right close button).
5371 * @param {String} title The title bar text
5372 * @param {String} msg The message box body text
5373 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5374 * @param {Object} scope (optional) The scope of the callback function
5375 * @return {Roo.MessageBox} This message box
5377 alert : function(title, msg, fn, scope)
5392 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5393 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5394 * You are responsible for closing the message box when the process is complete.
5395 * @param {String} msg The message box body text
5396 * @param {String} title (optional) The title bar text
5397 * @return {Roo.MessageBox} This message box
5399 wait : function(msg, title){
5410 waitTimer = Roo.TaskMgr.start({
5412 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5420 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5421 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5422 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @return {Roo.MessageBox} This message box
5429 confirm : function(title, msg, fn, scope){
5433 buttons: this.YESNO,
5442 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5443 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5444 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5445 * (could also be the top-right close button) and the text that was entered will be passed as the two
5446 * parameters to the callback.
5447 * @param {String} title The title bar text
5448 * @param {String} msg The message box body text
5449 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5450 * @param {Object} scope (optional) The scope of the callback function
5451 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5452 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5453 * @return {Roo.MessageBox} This message box
5455 prompt : function(title, msg, fn, scope, multiline){
5459 buttons: this.OKCANCEL,
5464 multiline: multiline,
5471 * Button config that displays a single OK button
5476 * Button config that displays Yes and No buttons
5479 YESNO : {yes:true, no:true},
5481 * Button config that displays OK and Cancel buttons
5484 OKCANCEL : {ok:true, cancel:true},
5486 * Button config that displays Yes, No and Cancel buttons
5489 YESNOCANCEL : {yes:true, no:true, cancel:true},
5492 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5495 defaultTextHeight : 75,
5497 * The maximum width in pixels of the message box (defaults to 600)
5502 * The minimum width in pixels of the message box (defaults to 100)
5507 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5508 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5511 minProgressWidth : 250,
5513 * An object containing the default button text strings that can be overriden for localized language support.
5514 * Supported properties are: ok, cancel, yes and no.
5515 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5528 * Shorthand for {@link Roo.MessageBox}
5530 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5531 Roo.Msg = Roo.Msg || Roo.MessageBox;
5540 * @class Roo.bootstrap.nav.Bar
5541 * @extends Roo.bootstrap.Component
5543 * Bootstrap Navbar class
5546 * Create a new Navbar
5547 * @param {Object} config The config object
5551 Roo.bootstrap.nav.Bar = function(config){
5552 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5556 * @event beforetoggle
5557 * Fire before toggle the menu
5558 * @param {Roo.EventObject} e
5560 "beforetoggle" : true
5564 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5573 getAutoCreate : function(){
5576 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5580 initEvents :function ()
5582 //Roo.log(this.el.select('.navbar-toggle',true));
5583 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5590 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5592 var size = this.el.getSize();
5593 this.maskEl.setSize(size.width, size.height);
5594 this.maskEl.enableDisplayMode("block");
5603 getChildContainer : function()
5605 if (this.el && this.el.select('.collapse').getCount()) {
5606 return this.el.select('.collapse',true).first();
5621 onToggle : function()
5624 if(this.fireEvent('beforetoggle', this) === false){
5627 var ce = this.el.select('.navbar-collapse',true).first();
5629 if (!ce.hasClass('show')) {
5639 * Expand the navbar pulldown
5641 expand : function ()
5644 var ce = this.el.select('.navbar-collapse',true).first();
5645 if (ce.hasClass('collapsing')) {
5648 ce.dom.style.height = '';
5650 ce.addClass('in'); // old...
5651 ce.removeClass('collapse');
5652 ce.addClass('show');
5653 var h = ce.getHeight();
5655 ce.removeClass('show');
5656 // at this point we should be able to see it..
5657 ce.addClass('collapsing');
5659 ce.setHeight(0); // resize it ...
5660 ce.on('transitionend', function() {
5661 //Roo.log('done transition');
5662 ce.removeClass('collapsing');
5663 ce.addClass('show');
5664 ce.removeClass('collapse');
5666 ce.dom.style.height = '';
5667 }, this, { single: true} );
5669 ce.dom.scrollTop = 0;
5672 * Collapse the navbar pulldown
5674 collapse : function()
5676 var ce = this.el.select('.navbar-collapse',true).first();
5678 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5679 // it's collapsed or collapsing..
5682 ce.removeClass('in'); // old...
5683 ce.setHeight(ce.getHeight());
5684 ce.removeClass('show');
5685 ce.addClass('collapsing');
5687 ce.on('transitionend', function() {
5688 ce.dom.style.height = '';
5689 ce.removeClass('collapsing');
5690 ce.addClass('collapse');
5691 }, this, { single: true} );
5711 * @class Roo.bootstrap.nav.Simplebar
5712 * @extends Roo.bootstrap.nav.Bar
5713 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5714 * Bootstrap Sidebar class
5716 * @cfg {Boolean} inverse is inverted color
5718 * @cfg {String} type (nav | pills | tabs)
5719 * @cfg {Boolean} arrangement stacked | justified
5720 * @cfg {String} align (left | right) alignment
5722 * @cfg {Boolean} main (true|false) main nav bar? default false
5723 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5725 * @cfg {String} tag (header|footer|nav|div) default is nav
5727 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5731 * Create a new Sidebar
5732 * @param {Object} config The config object
5736 Roo.bootstrap.nav.Simplebar = function(config){
5737 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5740 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5756 getAutoCreate : function(){
5760 tag : this.tag || 'div',
5761 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5763 if (['light','white'].indexOf(this.weight) > -1) {
5764 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5766 cfg.cls += ' bg-' + this.weight;
5769 cfg.cls += ' navbar-inverse';
5773 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5775 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5784 cls: 'nav nav-' + this.xtype,
5790 this.type = this.type || 'nav';
5791 if (['tabs','pills'].indexOf(this.type) != -1) {
5792 cfg.cn[0].cls += ' nav-' + this.type
5796 if (this.type!=='nav') {
5797 Roo.log('nav type must be nav/tabs/pills')
5799 cfg.cn[0].cls += ' navbar-nav'
5805 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5806 cfg.cn[0].cls += ' nav-' + this.arrangement;
5810 if (this.align === 'right') {
5811 cfg.cn[0].cls += ' navbar-right';
5836 * navbar-expand-md fixed-top
5840 * @class Roo.bootstrap.nav.Headerbar
5841 * @extends Roo.bootstrap.nav.Simplebar
5842 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5843 * Bootstrap Sidebar class
5845 * @cfg {String} brand what is brand
5846 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5847 * @cfg {String} brand_href href of the brand
5848 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5849 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5850 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5851 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5854 * Create a new Sidebar
5855 * @param {Object} config The config object
5859 Roo.bootstrap.nav.Headerbar = function(config){
5860 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5864 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5871 desktopCenter : false,
5874 getAutoCreate : function(){
5877 tag: this.nav || 'nav',
5878 cls: 'navbar navbar-expand-md',
5884 if (this.desktopCenter) {
5885 cn.push({cls : 'container', cn : []});
5893 cls: 'navbar-toggle navbar-toggler',
5894 'data-toggle': 'collapse',
5899 html: 'Toggle navigation'
5903 cls: 'icon-bar navbar-toggler-icon'
5916 cn.push( Roo.bootstrap.version == 4 ? btn : {
5918 cls: 'navbar-header',
5927 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5931 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5933 if (['light','white'].indexOf(this.weight) > -1) {
5934 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5936 cfg.cls += ' bg-' + this.weight;
5939 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5940 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5942 // tag can override this..
5944 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5947 if (this.brand !== '') {
5948 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5949 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5951 href: this.brand_href ? this.brand_href : '#',
5952 cls: 'navbar-brand',
5960 cfg.cls += ' main-nav';
5968 getHeaderChildContainer : function()
5970 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5971 return this.el.select('.navbar-header',true).first();
5974 return this.getChildContainer();
5977 getChildContainer : function()
5980 return this.el.select('.roo-navbar-collapse',true).first();
5985 initEvents : function()
5987 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5989 if (this.autohide) {
5994 Roo.get(document).on('scroll',function(e) {
5995 var ns = Roo.get(document).getScroll().top;
5996 var os = prevScroll;
6000 ft.removeClass('slideDown');
6001 ft.addClass('slideUp');
6004 ft.removeClass('slideUp');
6005 ft.addClass('slideDown');
6026 * @class Roo.bootstrap.nav.Sidebar
6027 * @extends Roo.bootstrap.nav.Bar
6028 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6029 * Bootstrap Sidebar class
6032 * Create a new Sidebar
6033 * @param {Object} config The config object
6037 Roo.bootstrap.nav.Sidebar = function(config){
6038 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6041 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6043 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6045 getAutoCreate : function(){
6050 cls: 'sidebar sidebar-nav'
6072 * @class Roo.bootstrap.nav.Group
6073 * @extends Roo.bootstrap.Component
6074 * @children Roo.bootstrap.nav.Item
6075 * Bootstrap NavGroup class
6076 * @cfg {String} align (left|right)
6077 * @cfg {Boolean} inverse
6078 * @cfg {String} type (nav|pills|tab) default nav
6079 * @cfg {String} navId - reference Id for navbar.
6080 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6083 * Create a new nav group
6084 * @param {Object} config The config object
6087 Roo.bootstrap.nav.Group = function(config){
6088 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6091 Roo.bootstrap.nav.Group.register(this);
6095 * Fires when the active item changes
6096 * @param {Roo.bootstrap.nav.Group} this
6097 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6098 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6105 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6117 getAutoCreate : function()
6119 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6125 if (Roo.bootstrap.version == 4) {
6126 if (['tabs','pills'].indexOf(this.type) != -1) {
6127 cfg.cls += ' nav-' + this.type;
6129 // trying to remove so header bar can right align top?
6130 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6131 // do not use on header bar...
6132 cfg.cls += ' navbar-nav';
6137 if (['tabs','pills'].indexOf(this.type) != -1) {
6138 cfg.cls += ' nav-' + this.type
6140 if (this.type !== 'nav') {
6141 Roo.log('nav type must be nav/tabs/pills')
6143 cfg.cls += ' navbar-nav'
6147 if (this.parent() && this.parent().sidebar) {
6150 cls: 'dashboard-menu sidebar-menu'
6156 if (this.form === true) {
6159 cls: 'navbar-form form-inline'
6161 //nav navbar-right ml-md-auto
6162 if (this.align === 'right') {
6163 cfg.cls += ' navbar-right ml-md-auto';
6165 cfg.cls += ' navbar-left';
6169 if (this.align === 'right') {
6170 cfg.cls += ' navbar-right ml-md-auto';
6172 cfg.cls += ' mr-auto';
6176 cfg.cls += ' navbar-inverse';
6184 * sets the active Navigation item
6185 * @param {Roo.bootstrap.nav.Item} the new current navitem
6187 setActiveItem : function(item)
6190 Roo.each(this.navItems, function(v){
6195 v.setActive(false, true);
6202 item.setActive(true, true);
6203 this.fireEvent('changed', this, item, prev);
6208 * gets the active Navigation item
6209 * @return {Roo.bootstrap.nav.Item} the current navitem
6211 getActive : function()
6215 Roo.each(this.navItems, function(v){
6226 indexOfNav : function()
6230 Roo.each(this.navItems, function(v,i){
6241 * adds a Navigation item
6242 * @param {Roo.bootstrap.nav.Item} the navitem to add
6244 addItem : function(cfg)
6246 if (this.form && Roo.bootstrap.version == 4) {
6249 var cn = new Roo.bootstrap.nav.Item(cfg);
6251 cn.parentId = this.id;
6252 cn.onRender(this.el, null);
6256 * register a Navigation item
6257 * @param {Roo.bootstrap.nav.Item} the navitem to add
6259 register : function(item)
6261 this.navItems.push( item);
6262 item.navId = this.navId;
6267 * clear all the Navigation item
6270 clearAll : function()
6273 this.el.dom.innerHTML = '';
6276 getNavItem: function(tabId)
6279 Roo.each(this.navItems, function(e) {
6280 if (e.tabId == tabId) {
6290 setActiveNext : function()
6292 var i = this.indexOfNav(this.getActive());
6293 if (i > this.navItems.length) {
6296 this.setActiveItem(this.navItems[i+1]);
6298 setActivePrev : function()
6300 var i = this.indexOfNav(this.getActive());
6304 this.setActiveItem(this.navItems[i-1]);
6306 clearWasActive : function(except) {
6307 Roo.each(this.navItems, function(e) {
6308 if (e.tabId != except.tabId && e.was_active) {
6309 e.was_active = false;
6316 getWasActive : function ()
6319 Roo.each(this.navItems, function(e) {
6334 Roo.apply(Roo.bootstrap.nav.Group, {
6338 * register a Navigation Group
6339 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6341 register : function(navgrp)
6343 this.groups[navgrp.navId] = navgrp;
6347 * fetch a Navigation Group based on the navigation ID
6348 * @param {string} the navgroup to add
6349 * @returns {Roo.bootstrap.nav.Group} the navgroup
6351 get: function(navId) {
6352 if (typeof(this.groups[navId]) == 'undefined') {
6354 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6356 return this.groups[navId] ;
6364 * @class Roo.bootstrap.nav.Item
6365 * @extends Roo.bootstrap.Component
6366 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6367 * @parent Roo.bootstrap.nav.Group
6369 * Bootstrap Navbar.NavItem class
6371 * @cfg {String} href link to
6372 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6373 * @cfg {Boolean} button_outline show and outlined button
6374 * @cfg {String} html content of button
6375 * @cfg {String} badge text inside badge
6376 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6377 * @cfg {String} glyphicon DEPRICATED - use fa
6378 * @cfg {String} icon DEPRICATED - use fa
6379 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6380 * @cfg {Boolean} active Is item active
6381 * @cfg {Boolean} disabled Is item disabled
6382 * @cfg {String} linkcls Link Class
6383 * @cfg {Boolean} preventDefault (true | false) default false
6384 * @cfg {String} tabId the tab that this item activates.
6385 * @cfg {String} tagtype (a|span) render as a href or span?
6386 * @cfg {Boolean} animateRef (true|false) link to element default false
6387 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6390 * Create a new Navbar Item
6391 * @param {Object} config The config object
6393 Roo.bootstrap.nav.Item = function(config){
6394 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6399 * The raw click event for the entire grid.
6400 * @param {Roo.EventObject} e
6405 * Fires when the active item active state changes
6406 * @param {Roo.bootstrap.nav.Item} this
6407 * @param {boolean} state the new state
6413 * Fires when scroll to element
6414 * @param {Roo.bootstrap.nav.Item} this
6415 * @param {Object} options
6416 * @param {Roo.EventObject} e
6424 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6433 preventDefault : false,
6441 button_outline : false,
6445 getAutoCreate : function(){
6452 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6455 cfg.cls += ' active' ;
6457 if (this.disabled) {
6458 cfg.cls += ' disabled';
6462 if (this.button_weight.length) {
6463 cfg.tag = this.href ? 'a' : 'button';
6464 cfg.html = this.html || '';
6465 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6467 cfg.href = this.href;
6470 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6472 cfg.cls += " nav-html";
6475 // menu .. should add dropdown-menu class - so no need for carat..
6477 if (this.badge !== '') {
6479 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6484 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6488 href : this.href || "#",
6489 html: this.html || '',
6493 if (this.tagtype == 'a') {
6494 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6498 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6499 } else if (this.fa) {
6500 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6501 } else if(this.glyphicon) {
6502 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6504 cfg.cn[0].cls += " nav-html";
6508 cfg.cn[0].html += " <span class='caret'></span>";
6512 if (this.badge !== '') {
6513 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6521 onRender : function(ct, position)
6523 // Roo.log("Call onRender: " + this.xtype);
6524 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6528 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6529 this.navLink = this.el.select('.nav-link',true).first();
6530 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6535 initEvents: function()
6537 if (typeof (this.menu) != 'undefined') {
6538 this.menu.parentType = this.xtype;
6539 this.menu.triggerEl = this.el;
6540 this.menu = this.addxtype(Roo.apply({}, this.menu));
6543 this.el.on('click', this.onClick, this);
6545 //if(this.tagtype == 'span'){
6546 // this.el.select('span',true).on('click', this.onClick, this);
6549 // at this point parent should be available..
6550 this.parent().register(this);
6553 onClick : function(e)
6555 if (e.getTarget('.dropdown-menu-item')) {
6556 // did you click on a menu itemm.... - then don't trigger onclick..
6561 this.preventDefault ||
6562 this.href === false ||
6565 //Roo.log("NavItem - prevent Default?");
6569 if (this.disabled) {
6573 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6574 if (tg && tg.transition) {
6575 Roo.log("waiting for the transitionend");
6581 //Roo.log("fire event clicked");
6582 if(this.fireEvent('click', this, e) === false){
6586 if(this.tagtype == 'span'){
6590 //Roo.log(this.href);
6591 var ael = this.el.select('a',true).first();
6594 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6595 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6596 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6597 return; // ignore... - it's a 'hash' to another page.
6599 Roo.log("NavItem - prevent Default?");
6601 this.scrollToElement(e);
6605 var p = this.parent();
6607 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6608 if (typeof(p.setActiveItem) !== 'undefined') {
6609 p.setActiveItem(this);
6613 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6614 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6615 // remove the collapsed menu expand...
6616 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6620 isActive: function () {
6623 setActive : function(state, fire, is_was_active)
6625 if (this.active && !state && this.navId) {
6626 this.was_active = true;
6627 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6629 nv.clearWasActive(this);
6633 this.active = state;
6636 this.el.removeClass('active');
6637 this.navLink ? this.navLink.removeClass('active') : false;
6638 } else if (!this.el.hasClass('active')) {
6640 this.el.addClass('active');
6641 if (Roo.bootstrap.version == 4 && this.navLink ) {
6642 this.navLink.addClass('active');
6647 this.fireEvent('changed', this, state);
6650 // show a panel if it's registered and related..
6652 if (!this.navId || !this.tabId || !state || is_was_active) {
6656 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6660 var pan = tg.getPanelByName(this.tabId);
6664 // if we can not flip to new panel - go back to old nav highlight..
6665 if (false == tg.showPanel(pan)) {
6666 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6668 var onav = nv.getWasActive();
6670 onav.setActive(true, false, true);
6679 // this should not be here...
6680 setDisabled : function(state)
6682 this.disabled = state;
6684 this.el.removeClass('disabled');
6685 } else if (!this.el.hasClass('disabled')) {
6686 this.el.addClass('disabled');
6692 * Fetch the element to display the tooltip on.
6693 * @return {Roo.Element} defaults to this.el
6695 tooltipEl : function()
6697 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6700 scrollToElement : function(e)
6702 var c = document.body;
6705 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6707 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6708 c = document.documentElement;
6711 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6717 var o = target.calcOffsetsTo(c);
6724 this.fireEvent('scrollto', this, options, e);
6726 Roo.get(c).scrollTo('top', options.value, true);
6731 * Set the HTML (text content) of the item
6732 * @param {string} html content for the nav item
6734 setHtml : function(html)
6737 this.htmlEl.dom.innerHTML = html;
6749 * <span> icon </span>
6750 * <span> text </span>
6751 * <span>badge </span>
6755 * @class Roo.bootstrap.nav.SidebarItem
6756 * @extends Roo.bootstrap.nav.Item
6757 * Bootstrap Navbar.NavSidebarItem class
6759 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6760 * {Boolean} open is the menu open
6761 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6762 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6763 * {String} buttonSize (sm|md|lg)the extra classes for the button
6764 * {Boolean} showArrow show arrow next to the text (default true)
6766 * Create a new Navbar Button
6767 * @param {Object} config The config object
6769 Roo.bootstrap.nav.SidebarItem = function(config){
6770 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6775 * The raw click event for the entire grid.
6776 * @param {Roo.EventObject} e
6781 * Fires when the active item active state changes
6782 * @param {Roo.bootstrap.nav.SidebarItem} this
6783 * @param {boolean} state the new state
6791 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6793 badgeWeight : 'default',
6799 buttonWeight : 'default',
6805 getAutoCreate : function(){
6810 href : this.href || '#',
6816 if(this.buttonView){
6819 href : this.href || '#',
6820 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6833 cfg.cls += ' active';
6836 if (this.disabled) {
6837 cfg.cls += ' disabled';
6840 cfg.cls += ' open x-open';
6843 if (this.glyphicon || this.icon) {
6844 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6845 a.cn.push({ tag : 'i', cls : c }) ;
6848 if(!this.buttonView){
6851 html : this.html || ''
6858 if (this.badge !== '') {
6859 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6865 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6868 a.cls += ' dropdown-toggle treeview' ;
6874 initEvents : function()
6876 if (typeof (this.menu) != 'undefined') {
6877 this.menu.parentType = this.xtype;
6878 this.menu.triggerEl = this.el;
6879 this.menu = this.addxtype(Roo.apply({}, this.menu));
6882 this.el.on('click', this.onClick, this);
6884 if(this.badge !== ''){
6885 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6890 onClick : function(e)
6897 if(this.preventDefault){
6901 this.fireEvent('click', this, e);
6904 disable : function()
6906 this.setDisabled(true);
6911 this.setDisabled(false);
6914 setDisabled : function(state)
6916 if(this.disabled == state){
6920 this.disabled = state;
6923 this.el.addClass('disabled');
6927 this.el.removeClass('disabled');
6932 setActive : function(state)
6934 if(this.active == state){
6938 this.active = state;
6941 this.el.addClass('active');
6945 this.el.removeClass('active');
6950 isActive: function ()
6955 setBadge : function(str)
6961 this.badgeEl.dom.innerHTML = str;
6978 * @class Roo.bootstrap.nav.ProgressBar
6979 * @extends Roo.bootstrap.Component
6980 * @children Roo.bootstrap.nav.ProgressBarItem
6981 * Bootstrap NavProgressBar class
6984 * Create a new nav progress bar - a bar indicating step along a process
6985 * @param {Object} config The config object
6988 Roo.bootstrap.nav.ProgressBar = function(config){
6989 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6991 this.bullets = this.bullets || [];
6993 // Roo.bootstrap.nav.ProgressBar.register(this);
6997 * Fires when the active item changes
6998 * @param {Roo.bootstrap.nav.ProgressBar} this
6999 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
7000 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7007 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7009 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7010 * Bullets for the Nav Progress bar for the toolbar
7015 getAutoCreate : function()
7017 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7021 cls : 'roo-navigation-bar-group',
7025 cls : 'roo-navigation-top-bar'
7029 cls : 'roo-navigation-bullets-bar',
7033 cls : 'roo-navigation-bar'
7040 cls : 'roo-navigation-bottom-bar'
7050 initEvents: function()
7055 onRender : function(ct, position)
7057 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7059 if(this.bullets.length){
7060 Roo.each(this.bullets, function(b){
7069 addItem : function(cfg)
7071 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7073 item.parentId = this.id;
7074 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7077 var top = new Roo.bootstrap.Element({
7079 cls : 'roo-navigation-bar-text'
7082 var bottom = new Roo.bootstrap.Element({
7084 cls : 'roo-navigation-bar-text'
7087 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7088 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7090 var topText = new Roo.bootstrap.Element({
7092 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7095 var bottomText = new Roo.bootstrap.Element({
7097 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7100 topText.onRender(top.el, null);
7101 bottomText.onRender(bottom.el, null);
7104 item.bottomEl = bottom;
7107 this.barItems.push(item);
7112 getActive : function()
7116 Roo.each(this.barItems, function(v){
7118 if (!v.isActive()) {
7130 setActiveItem : function(item)
7134 Roo.each(this.barItems, function(v){
7135 if (v.rid == item.rid) {
7145 item.setActive(true);
7147 this.fireEvent('changed', this, item, prev);
7150 getBarItem: function(rid)
7154 Roo.each(this.barItems, function(e) {
7166 indexOfItem : function(item)
7170 Roo.each(this.barItems, function(v, i){
7172 if (v.rid != item.rid) {
7183 setActiveNext : function()
7185 var i = this.indexOfItem(this.getActive());
7187 if (i > this.barItems.length) {
7191 this.setActiveItem(this.barItems[i+1]);
7194 setActivePrev : function()
7196 var i = this.indexOfItem(this.getActive());
7202 this.setActiveItem(this.barItems[i-1]);
7207 if(!this.barItems.length){
7211 var width = 100 / this.barItems.length;
7213 Roo.each(this.barItems, function(i){
7214 i.el.setStyle('width', width + '%');
7215 i.topEl.el.setStyle('width', width + '%');
7216 i.bottomEl.el.setStyle('width', width + '%');
7230 * @class Roo.bootstrap.nav.ProgressBarItem
7231 * @extends Roo.bootstrap.Component
7232 * Bootstrap NavProgressBarItem class
7233 * @cfg {String} rid the reference id
7234 * @cfg {Boolean} active (true|false) Is item active default false
7235 * @cfg {Boolean} disabled (true|false) Is item active default false
7236 * @cfg {String} html
7237 * @cfg {String} position (top|bottom) text position default bottom
7238 * @cfg {String} icon show icon instead of number
7241 * Create a new NavProgressBarItem
7242 * @param {Object} config The config object
7244 Roo.bootstrap.nav.ProgressBarItem = function(config){
7245 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7250 * The raw click event for the entire grid.
7251 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7252 * @param {Roo.EventObject} e
7259 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7265 position : 'bottom',
7268 getAutoCreate : function()
7270 var iconCls = 'roo-navigation-bar-item-icon';
7272 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7276 cls: 'roo-navigation-bar-item',
7286 cfg.cls += ' active';
7289 cfg.cls += ' disabled';
7295 disable : function()
7297 this.setDisabled(true);
7302 this.setDisabled(false);
7305 initEvents: function()
7307 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7309 this.iconEl.on('click', this.onClick, this);
7312 onClick : function(e)
7320 if(this.fireEvent('click', this, e) === false){
7324 this.parent().setActiveItem(this);
7327 isActive: function ()
7332 setActive : function(state)
7334 if(this.active == state){
7338 this.active = state;
7341 this.el.addClass('active');
7345 this.el.removeClass('active');
7350 setDisabled : function(state)
7352 if(this.disabled == state){
7356 this.disabled = state;
7359 this.el.addClass('disabled');
7363 this.el.removeClass('disabled');
7366 tooltipEl : function()
7368 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7379 Roo.namespace('Roo.bootstrap.breadcrumb');
7383 * @class Roo.bootstrap.breadcrumb.Nav
7384 * @extends Roo.bootstrap.Component
7385 * Bootstrap Breadcrumb Nav Class
7387 * @children Roo.bootstrap.breadcrumb.Item
7390 * Create a new breadcrumb.Nav
7391 * @param {Object} config The config object
7395 Roo.bootstrap.breadcrumb.Nav = function(config){
7396 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7401 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7403 getAutoCreate : function()
7420 initEvents: function()
7422 this.olEl = this.el.select('ol',true).first();
7424 getChildContainer : function()
7440 * @class Roo.bootstrap.breadcrumb.Nav
7441 * @extends Roo.bootstrap.Component
7442 * @children Roo.bootstrap.Component
7443 * @parent Roo.bootstrap.breadcrumb.Nav
7444 * Bootstrap Breadcrumb Nav Class
7447 * @cfg {String} html the content of the link.
7448 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7449 * @cfg {Boolean} active is it active
7453 * Create a new breadcrumb.Nav
7454 * @param {Object} config The config object
7457 Roo.bootstrap.breadcrumb.Item = function(config){
7458 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7463 * The img click event for the img.
7464 * @param {Roo.EventObject} e
7471 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7476 getAutoCreate : function()
7481 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7483 if (this.href !== false) {
7490 cfg.html = this.html;
7496 initEvents: function()
7499 this.el.select('a', true).first().on('click',this.onClick, this)
7503 onClick : function(e)
7506 this.fireEvent('click',this, e);
7519 * @class Roo.bootstrap.Row
7520 * @extends Roo.bootstrap.Component
7521 * @children Roo.bootstrap.Component
7522 * Bootstrap Row class (contains columns...)
7526 * @param {Object} config The config object
7529 Roo.bootstrap.Row = function(config){
7530 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7535 getAutoCreate : function(){
7554 * @class Roo.bootstrap.Pagination
7555 * @extends Roo.bootstrap.Component
7556 * @children Roo.bootstrap.Pagination
7557 * Bootstrap Pagination class
7559 * @cfg {String} size (xs|sm|md|lg|xl)
7560 * @cfg {Boolean} inverse
7563 * Create a new Pagination
7564 * @param {Object} config The config object
7567 Roo.bootstrap.Pagination = function(config){
7568 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7571 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7577 getAutoCreate : function(){
7583 cfg.cls += ' inverse';
7589 cfg.cls += " " + this.cls;
7607 * @class Roo.bootstrap.PaginationItem
7608 * @extends Roo.bootstrap.Component
7609 * Bootstrap PaginationItem class
7610 * @cfg {String} html text
7611 * @cfg {String} href the link
7612 * @cfg {Boolean} preventDefault (true | false) default true
7613 * @cfg {Boolean} active (true | false) default false
7614 * @cfg {Boolean} disabled default false
7618 * Create a new PaginationItem
7619 * @param {Object} config The config object
7623 Roo.bootstrap.PaginationItem = function(config){
7624 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7629 * The raw click event for the entire grid.
7630 * @param {Roo.EventObject} e
7636 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7640 preventDefault: true,
7645 getAutoCreate : function(){
7651 href : this.href ? this.href : '#',
7652 html : this.html ? this.html : ''
7662 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7666 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7672 initEvents: function() {
7674 this.el.on('click', this.onClick, this);
7677 onClick : function(e)
7679 Roo.log('PaginationItem on click ');
7680 if(this.preventDefault){
7688 this.fireEvent('click', this, e);
7704 * @class Roo.bootstrap.Slider
7705 * @extends Roo.bootstrap.Component
7706 * Bootstrap Slider class
7709 * Create a new Slider
7710 * @param {Object} config The config object
7713 Roo.bootstrap.Slider = function(config){
7714 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7717 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7719 getAutoCreate : function(){
7723 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7727 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7739 * Ext JS Library 1.1.1
7740 * Copyright(c) 2006-2007, Ext JS, LLC.
7742 * Originally Released Under LGPL - original licence link has changed is not relivant.
7745 * <script type="text/javascript">
7748 * @extends Roo.dd.DDProxy
7749 * @class Roo.grid.SplitDragZone
7750 * Support for Column Header resizing
7752 * @param {Object} config
7755 // This is a support class used internally by the Grid components
7756 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7758 this.view = grid.getView();
7759 this.proxy = this.view.resizeProxy;
7760 Roo.grid.SplitDragZone.superclass.constructor.call(
7763 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7765 dragElId : Roo.id(this.proxy.dom),
7770 this.setHandleElId(Roo.id(hd));
7771 if (hd2 !== false) {
7772 this.setOuterHandleElId(Roo.id(hd2));
7775 this.scroll = false;
7777 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7778 fly: Roo.Element.fly,
7780 b4StartDrag : function(x, y){
7781 this.view.headersDisabled = true;
7782 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7783 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7785 this.proxy.setHeight(h);
7787 // for old system colWidth really stored the actual width?
7788 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7789 // which in reality did not work.. - it worked only for fixed sizes
7790 // for resizable we need to use actual sizes.
7791 var w = this.cm.getColumnWidth(this.cellIndex);
7792 if (!this.view.mainWrap) {
7794 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7799 // this was w-this.grid.minColumnWidth;
7800 // doesnt really make sense? - w = thie curren width or the rendered one?
7801 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7802 this.resetConstraints();
7803 this.setXConstraint(minw, 1000);
7804 this.setYConstraint(0, 0);
7805 this.minX = x - minw;
7806 this.maxX = x + 1000;
7808 if (!this.view.mainWrap) { // this is Bootstrap code..
7809 this.getDragEl().style.display='block';
7812 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7816 handleMouseDown : function(e){
7817 ev = Roo.EventObject.setEvent(e);
7818 var t = this.fly(ev.getTarget());
7819 if(t.hasClass("x-grid-split")){
7820 this.cellIndex = this.view.getCellIndex(t.dom);
7822 this.cm = this.grid.colModel;
7823 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7824 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7829 endDrag : function(e){
7830 this.view.headersDisabled = false;
7831 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7832 var diff = endX - this.startPos;
7834 var w = this.cm.getColumnWidth(this.cellIndex);
7835 if (!this.view.mainWrap) {
7838 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7841 autoOffset : function(){
7846 * Ext JS Library 1.1.1
7847 * Copyright(c) 2006-2007, Ext JS, LLC.
7849 * Originally Released Under LGPL - original licence link has changed is not relivant.
7852 * <script type="text/javascript">
7856 * @class Roo.grid.AbstractSelectionModel
7857 * @extends Roo.util.Observable
7859 * Abstract base class for grid SelectionModels. It provides the interface that should be
7860 * implemented by descendant classes. This class should not be directly instantiated.
7863 Roo.grid.AbstractSelectionModel = function(){
7864 this.locked = false;
7865 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7868 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7869 /** @ignore Called by the grid automatically. Do not call directly. */
7870 init : function(grid){
7876 * Locks the selections.
7883 * Unlocks the selections.
7885 unlock : function(){
7886 this.locked = false;
7890 * Returns true if the selections are locked.
7893 isLocked : function(){
7898 * Ext JS Library 1.1.1
7899 * Copyright(c) 2006-2007, Ext JS, LLC.
7901 * Originally Released Under LGPL - original licence link has changed is not relivant.
7904 * <script type="text/javascript">
7907 * @extends Roo.grid.AbstractSelectionModel
7908 * @class Roo.grid.RowSelectionModel
7909 * The default SelectionModel used by {@link Roo.grid.Grid}.
7910 * It supports multiple selections and keyboard selection/navigation.
7912 * @param {Object} config
7914 Roo.grid.RowSelectionModel = function(config){
7915 Roo.apply(this, config);
7916 this.selections = new Roo.util.MixedCollection(false, function(o){
7921 this.lastActive = false;
7925 * @event selectionchange
7926 * Fires when the selection changes
7927 * @param {SelectionModel} this
7929 "selectionchange" : true,
7931 * @event afterselectionchange
7932 * Fires after the selection changes (eg. by key press or clicking)
7933 * @param {SelectionModel} this
7935 "afterselectionchange" : true,
7937 * @event beforerowselect
7938 * Fires when a row is selected being selected, return false to cancel.
7939 * @param {SelectionModel} this
7940 * @param {Number} rowIndex The selected index
7941 * @param {Boolean} keepExisting False if other selections will be cleared
7943 "beforerowselect" : true,
7946 * Fires when a row is selected.
7947 * @param {SelectionModel} this
7948 * @param {Number} rowIndex The selected index
7949 * @param {Roo.data.Record} r The record
7953 * @event rowdeselect
7954 * Fires when a row is deselected.
7955 * @param {SelectionModel} this
7956 * @param {Number} rowIndex The selected index
7958 "rowdeselect" : true
7960 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7961 this.locked = false;
7964 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7966 * @cfg {Boolean} singleSelect
7967 * True to allow selection of only one row at a time (defaults to false)
7969 singleSelect : false,
7972 initEvents : function(){
7974 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7975 this.grid.on("mousedown", this.handleMouseDown, this);
7976 }else{ // allow click to work like normal
7977 this.grid.on("rowclick", this.handleDragableRowClick, this);
7979 // bootstrap does not have a view..
7980 var view = this.grid.view ? this.grid.view : this.grid;
7981 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7984 this.selectPrevious(e.shiftKey);
7985 }else if(this.last !== false && this.lastActive !== false){
7986 var last = this.last;
7987 this.selectRange(this.last, this.lastActive-1);
7988 view.focusRow(this.lastActive);
7993 this.selectFirstRow();
7995 this.fireEvent("afterselectionchange", this);
7997 "down" : function(e){
7999 this.selectNext(e.shiftKey);
8000 }else if(this.last !== false && this.lastActive !== false){
8001 var last = this.last;
8002 this.selectRange(this.last, this.lastActive+1);
8003 view.focusRow(this.lastActive);
8008 this.selectFirstRow();
8010 this.fireEvent("afterselectionchange", this);
8016 view.on("refresh", this.onRefresh, this);
8017 view.on("rowupdated", this.onRowUpdated, this);
8018 view.on("rowremoved", this.onRemove, this);
8022 onRefresh : function(){
8023 var ds = this.grid.ds, i, v = this.grid.view;
8024 var s = this.selections;
8026 if((i = ds.indexOfId(r.id)) != -1){
8028 s.add(ds.getAt(i)); // updating the selection relate data
8036 onRemove : function(v, index, r){
8037 this.selections.remove(r);
8041 onRowUpdated : function(v, index, r){
8042 if(this.isSelected(r)){
8043 v.onRowSelect(index);
8049 * @param {Array} records The records to select
8050 * @param {Boolean} keepExisting (optional) True to keep existing selections
8052 selectRecords : function(records, keepExisting){
8054 this.clearSelections();
8056 var ds = this.grid.ds;
8057 for(var i = 0, len = records.length; i < len; i++){
8058 this.selectRow(ds.indexOf(records[i]), true);
8063 * Gets the number of selected rows.
8066 getCount : function(){
8067 return this.selections.length;
8071 * Selects the first row in the grid.
8073 selectFirstRow : function(){
8078 * Select the last row.
8079 * @param {Boolean} keepExisting (optional) True to keep existing selections
8081 selectLastRow : function(keepExisting){
8082 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8086 * Selects the row immediately following the last selected row.
8087 * @param {Boolean} keepExisting (optional) True to keep existing selections
8089 selectNext : function(keepExisting){
8090 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8091 this.selectRow(this.last+1, keepExisting);
8092 var view = this.grid.view ? this.grid.view : this.grid;
8093 view.focusRow(this.last);
8098 * Selects the row that precedes the last selected row.
8099 * @param {Boolean} keepExisting (optional) True to keep existing selections
8101 selectPrevious : function(keepExisting){
8103 this.selectRow(this.last-1, keepExisting);
8104 var view = this.grid.view ? this.grid.view : this.grid;
8105 view.focusRow(this.last);
8110 * Returns the selected records
8111 * @return {Array} Array of selected records
8113 getSelections : function(){
8114 return [].concat(this.selections.items);
8118 * Returns the first selected record.
8121 getSelected : function(){
8122 return this.selections.itemAt(0);
8127 * Clears all selections.
8129 clearSelections : function(fast){
8134 var ds = this.grid.ds;
8135 var s = this.selections;
8137 this.deselectRow(ds.indexOfId(r.id));
8141 this.selections.clear();
8150 selectAll : function(){
8154 this.selections.clear();
8155 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8156 this.selectRow(i, true);
8161 * Returns True if there is a selection.
8164 hasSelection : function(){
8165 return this.selections.length > 0;
8169 * Returns True if the specified row is selected.
8170 * @param {Number/Record} record The record or index of the record to check
8173 isSelected : function(index){
8174 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8175 return (r && this.selections.key(r.id) ? true : false);
8179 * Returns True if the specified record id is selected.
8180 * @param {String} id The id of record to check
8183 isIdSelected : function(id){
8184 return (this.selections.key(id) ? true : false);
8188 handleMouseDown : function(e, t)
8190 var view = this.grid.view ? this.grid.view : this.grid;
8192 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8195 if(e.shiftKey && this.last !== false){
8196 var last = this.last;
8197 this.selectRange(last, rowIndex, e.ctrlKey);
8198 this.last = last; // reset the last
8199 view.focusRow(rowIndex);
8201 var isSelected = this.isSelected(rowIndex);
8202 if(e.button !== 0 && isSelected){
8203 view.focusRow(rowIndex);
8204 }else if(e.ctrlKey && isSelected){
8205 this.deselectRow(rowIndex);
8206 }else if(!isSelected){
8207 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8208 view.focusRow(rowIndex);
8211 this.fireEvent("afterselectionchange", this);
8214 handleDragableRowClick : function(grid, rowIndex, e)
8216 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8217 this.selectRow(rowIndex, false);
8218 var view = this.grid.view ? this.grid.view : this.grid;
8219 view.focusRow(rowIndex);
8220 this.fireEvent("afterselectionchange", this);
8225 * Selects multiple rows.
8226 * @param {Array} rows Array of the indexes of the row to select
8227 * @param {Boolean} keepExisting (optional) True to keep existing selections
8229 selectRows : function(rows, keepExisting){
8231 this.clearSelections();
8233 for(var i = 0, len = rows.length; i < len; i++){
8234 this.selectRow(rows[i], true);
8239 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8240 * @param {Number} startRow The index of the first row in the range
8241 * @param {Number} endRow The index of the last row in the range
8242 * @param {Boolean} keepExisting (optional) True to retain existing selections
8244 selectRange : function(startRow, endRow, keepExisting){
8249 this.clearSelections();
8251 if(startRow <= endRow){
8252 for(var i = startRow; i <= endRow; i++){
8253 this.selectRow(i, true);
8256 for(var i = startRow; i >= endRow; i--){
8257 this.selectRow(i, true);
8263 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8264 * @param {Number} startRow The index of the first row in the range
8265 * @param {Number} endRow The index of the last row in the range
8267 deselectRange : function(startRow, endRow, preventViewNotify){
8271 for(var i = startRow; i <= endRow; i++){
8272 this.deselectRow(i, preventViewNotify);
8278 * @param {Number} row The index of the row to select
8279 * @param {Boolean} keepExisting (optional) True to keep existing selections
8281 selectRow : function(index, keepExisting, preventViewNotify){
8282 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8285 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8286 if(!keepExisting || this.singleSelect){
8287 this.clearSelections();
8289 var r = this.grid.ds.getAt(index);
8290 this.selections.add(r);
8291 this.last = this.lastActive = index;
8292 if(!preventViewNotify){
8293 var view = this.grid.view ? this.grid.view : this.grid;
8294 view.onRowSelect(index);
8296 this.fireEvent("rowselect", this, index, r);
8297 this.fireEvent("selectionchange", this);
8303 * @param {Number} row The index of the row to deselect
8305 deselectRow : function(index, preventViewNotify){
8309 if(this.last == index){
8312 if(this.lastActive == index){
8313 this.lastActive = false;
8315 var r = this.grid.ds.getAt(index);
8316 this.selections.remove(r);
8317 if(!preventViewNotify){
8318 var view = this.grid.view ? this.grid.view : this.grid;
8319 view.onRowDeselect(index);
8321 this.fireEvent("rowdeselect", this, index);
8322 this.fireEvent("selectionchange", this);
8326 restoreLast : function(){
8328 this.last = this._last;
8333 acceptsNav : function(row, col, cm){
8334 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8338 onEditorKey : function(field, e){
8339 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8344 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8346 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8348 }else if(k == e.ENTER && !e.ctrlKey){
8352 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8354 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8356 }else if(k == e.ESC){
8360 g.startEditing(newCell[0], newCell[1]);
8365 * Ext JS Library 1.1.1
8366 * Copyright(c) 2006-2007, Ext JS, LLC.
8368 * Originally Released Under LGPL - original licence link has changed is not relivant.
8371 * <script type="text/javascript">
8376 * @class Roo.grid.ColumnModel
8377 * @extends Roo.util.Observable
8378 * This is the default implementation of a ColumnModel used by the Grid. It defines
8379 * the columns in the grid.
8382 var colModel = new Roo.grid.ColumnModel([
8383 {header: "Ticker", width: 60, sortable: true, locked: true},
8384 {header: "Company Name", width: 150, sortable: true},
8385 {header: "Market Cap.", width: 100, sortable: true},
8386 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8387 {header: "Employees", width: 100, sortable: true, resizable: false}
8392 * The config options listed for this class are options which may appear in each
8393 * individual column definition.
8394 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8396 * @param {Object} config An Array of column config objects. See this class's
8397 * config objects for details.
8399 Roo.grid.ColumnModel = function(config){
8401 * The config passed into the constructor
8403 this.config = []; //config;
8406 // if no id, create one
8407 // if the column does not have a dataIndex mapping,
8408 // map it to the order it is in the config
8409 for(var i = 0, len = config.length; i < len; i++){
8410 this.addColumn(config[i]);
8415 * The width of columns which have no width specified (defaults to 100)
8418 this.defaultWidth = 100;
8421 * Default sortable of columns which have no sortable specified (defaults to false)
8424 this.defaultSortable = false;
8428 * @event widthchange
8429 * Fires when the width of a column changes.
8430 * @param {ColumnModel} this
8431 * @param {Number} columnIndex The column index
8432 * @param {Number} newWidth The new width
8434 "widthchange": true,
8436 * @event headerchange
8437 * Fires when the text of a header changes.
8438 * @param {ColumnModel} this
8439 * @param {Number} columnIndex The column index
8440 * @param {Number} newText The new header text
8442 "headerchange": true,
8444 * @event hiddenchange
8445 * Fires when a column is hidden or "unhidden".
8446 * @param {ColumnModel} this
8447 * @param {Number} columnIndex The column index
8448 * @param {Boolean} hidden true if hidden, false otherwise
8450 "hiddenchange": true,
8452 * @event columnmoved
8453 * Fires when a column is moved.
8454 * @param {ColumnModel} this
8455 * @param {Number} oldIndex
8456 * @param {Number} newIndex
8458 "columnmoved" : true,
8460 * @event columlockchange
8461 * Fires when a column's locked state is changed
8462 * @param {ColumnModel} this
8463 * @param {Number} colIndex
8464 * @param {Boolean} locked true if locked
8466 "columnlockchange" : true
8468 Roo.grid.ColumnModel.superclass.constructor.call(this);
8470 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8472 * @cfg {String} header [required] The header text to display in the Grid view.
8475 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8478 * @cfg {String} smHeader Header at Bootsrap Small width
8481 * @cfg {String} mdHeader Header at Bootsrap Medium width
8484 * @cfg {String} lgHeader Header at Bootsrap Large width
8487 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8490 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8491 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8492 * specified, the column's index is used as an index into the Record's data Array.
8495 * @cfg {Number} width The initial width in pixels of the column. Using this
8496 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8499 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8500 * Defaults to the value of the {@link #defaultSortable} property.
8501 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8504 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8507 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8510 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8513 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8516 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8517 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8518 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8519 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8522 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8525 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8528 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8531 * @cfg {String} cursor ( auto|default|none|context-menu|help|pointer|progress|wait|cell|crosshair|text|vertical-text|alias|copy|move|no-drop|not-allowed|e-resize|n-resize|ne-resize|nw-resize|s-resize|se-resize|sw-resize|w-resize|ew-resize|ns-resize|nesw-resize|nwse-resize|col-resize|row-resize|all-scroll|zoom-in|zoom-out|grab|grabbing)
8534 * @cfg {String} tooltip mouse over tooltip text
8537 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8540 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8543 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8546 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8549 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8552 * Returns the id of the column at the specified index.
8553 * @param {Number} index The column index
8554 * @return {String} the id
8556 getColumnId : function(index){
8557 return this.config[index].id;
8561 * Returns the column for a specified id.
8562 * @param {String} id The column id
8563 * @return {Object} the column
8565 getColumnById : function(id){
8566 return this.lookup[id];
8571 * Returns the column Object for a specified dataIndex.
8572 * @param {String} dataIndex The column dataIndex
8573 * @return {Object|Boolean} the column or false if not found
8575 getColumnByDataIndex: function(dataIndex){
8576 var index = this.findColumnIndex(dataIndex);
8577 return index > -1 ? this.config[index] : false;
8581 * Returns the index for a specified column id.
8582 * @param {String} id The column id
8583 * @return {Number} the index, or -1 if not found
8585 getIndexById : function(id){
8586 for(var i = 0, len = this.config.length; i < len; i++){
8587 if(this.config[i].id == id){
8595 * Returns the index for a specified column dataIndex.
8596 * @param {String} dataIndex The column dataIndex
8597 * @return {Number} the index, or -1 if not found
8600 findColumnIndex : function(dataIndex){
8601 for(var i = 0, len = this.config.length; i < len; i++){
8602 if(this.config[i].dataIndex == dataIndex){
8610 moveColumn : function(oldIndex, newIndex){
8611 var c = this.config[oldIndex];
8612 this.config.splice(oldIndex, 1);
8613 this.config.splice(newIndex, 0, c);
8614 this.dataMap = null;
8615 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8618 isLocked : function(colIndex){
8619 return this.config[colIndex].locked === true;
8622 setLocked : function(colIndex, value, suppressEvent){
8623 if(this.isLocked(colIndex) == value){
8626 this.config[colIndex].locked = value;
8628 this.fireEvent("columnlockchange", this, colIndex, value);
8632 getTotalLockedWidth : function(){
8634 for(var i = 0; i < this.config.length; i++){
8635 if(this.isLocked(i) && !this.isHidden(i)){
8636 this.totalWidth += this.getColumnWidth(i);
8642 getLockedCount : function(){
8643 for(var i = 0, len = this.config.length; i < len; i++){
8644 if(!this.isLocked(i)){
8649 return this.config.length;
8653 * Returns the number of columns.
8656 getColumnCount : function(visibleOnly){
8657 if(visibleOnly === true){
8659 for(var i = 0, len = this.config.length; i < len; i++){
8660 if(!this.isHidden(i)){
8666 return this.config.length;
8670 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8671 * @param {Function} fn
8672 * @param {Object} scope (optional)
8673 * @return {Array} result
8675 getColumnsBy : function(fn, scope){
8677 for(var i = 0, len = this.config.length; i < len; i++){
8678 var c = this.config[i];
8679 if(fn.call(scope||this, c, i) === true){
8687 * Returns true if the specified column is sortable.
8688 * @param {Number} col The column index
8691 isSortable : function(col){
8692 if(typeof this.config[col].sortable == "undefined"){
8693 return this.defaultSortable;
8695 return this.config[col].sortable;
8699 * Returns the rendering (formatting) function defined for the column.
8700 * @param {Number} col The column index.
8701 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8703 getRenderer : function(col){
8704 if(!this.config[col].renderer){
8705 return Roo.grid.ColumnModel.defaultRenderer;
8707 return this.config[col].renderer;
8711 * Sets the rendering (formatting) function for a column.
8712 * @param {Number} col The column index
8713 * @param {Function} fn The function to use to process the cell's raw data
8714 * to return HTML markup for the grid view. The render function is called with
8715 * the following parameters:<ul>
8716 * <li>Data value.</li>
8717 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8718 * <li>css A CSS style string to apply to the table cell.</li>
8719 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8720 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8721 * <li>Row index</li>
8722 * <li>Column index</li>
8723 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8725 setRenderer : function(col, fn){
8726 this.config[col].renderer = fn;
8730 * Returns the width for the specified column.
8731 * @param {Number} col The column index
8732 * @param (optional) {String} gridSize bootstrap width size.
8735 getColumnWidth : function(col, gridSize)
8737 var cfg = this.config[col];
8739 if (typeof(gridSize) == 'undefined') {
8740 return cfg.width * 1 || this.defaultWidth;
8742 if (gridSize === false) { // if we set it..
8743 return cfg.width || false;
8745 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8747 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8748 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8751 return cfg[ sizes[i] ];
8758 * Sets the width for a column.
8759 * @param {Number} col The column index
8760 * @param {Number} width The new width
8762 setColumnWidth : function(col, width, suppressEvent){
8763 this.config[col].width = width;
8764 this.totalWidth = null;
8766 this.fireEvent("widthchange", this, col, width);
8771 * Returns the total width of all columns.
8772 * @param {Boolean} includeHidden True to include hidden column widths
8775 getTotalWidth : function(includeHidden){
8776 if(!this.totalWidth){
8777 this.totalWidth = 0;
8778 for(var i = 0, len = this.config.length; i < len; i++){
8779 if(includeHidden || !this.isHidden(i)){
8780 this.totalWidth += this.getColumnWidth(i);
8784 return this.totalWidth;
8788 * Returns the header for the specified column.
8789 * @param {Number} col The column index
8792 getColumnHeader : function(col){
8793 return this.config[col].header;
8797 * Sets the header for a column.
8798 * @param {Number} col The column index
8799 * @param {String} header The new header
8801 setColumnHeader : function(col, header){
8802 this.config[col].header = header;
8803 this.fireEvent("headerchange", this, col, header);
8807 * Returns the tooltip for the specified column.
8808 * @param {Number} col The column index
8811 getColumnTooltip : function(col){
8812 return this.config[col].tooltip;
8815 * Sets the tooltip for a column.
8816 * @param {Number} col The column index
8817 * @param {String} tooltip The new tooltip
8819 setColumnTooltip : function(col, tooltip){
8820 this.config[col].tooltip = tooltip;
8824 * Returns the dataIndex for the specified column.
8825 * @param {Number} col The column index
8828 getDataIndex : function(col){
8829 return this.config[col].dataIndex;
8833 * Sets the dataIndex for a column.
8834 * @param {Number} col The column index
8835 * @param {Number} dataIndex The new dataIndex
8837 setDataIndex : function(col, dataIndex){
8838 this.config[col].dataIndex = dataIndex;
8844 * Returns true if the cell is editable.
8845 * @param {Number} colIndex The column index
8846 * @param {Number} rowIndex The row index - this is nto actually used..?
8849 isCellEditable : function(colIndex, rowIndex){
8850 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8854 * Returns the editor defined for the cell/column.
8855 * return false or null to disable editing.
8856 * @param {Number} colIndex The column index
8857 * @param {Number} rowIndex The row index
8860 getCellEditor : function(colIndex, rowIndex){
8861 return this.config[colIndex].editor;
8865 * Sets if a column is editable.
8866 * @param {Number} col The column index
8867 * @param {Boolean} editable True if the column is editable
8869 setEditable : function(col, editable){
8870 this.config[col].editable = editable;
8875 * Returns true if the column is hidden.
8876 * @param {Number} colIndex The column index
8879 isHidden : function(colIndex){
8880 return this.config[colIndex].hidden;
8885 * Returns true if the column width cannot be changed
8887 isFixed : function(colIndex){
8888 return this.config[colIndex].fixed;
8892 * Returns true if the column can be resized
8895 isResizable : function(colIndex){
8896 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8899 * Sets if a column is hidden.
8900 * @param {Number} colIndex The column index
8901 * @param {Boolean} hidden True if the column is hidden
8903 setHidden : function(colIndex, hidden){
8904 this.config[colIndex].hidden = hidden;
8905 this.totalWidth = null;
8906 this.fireEvent("hiddenchange", this, colIndex, hidden);
8910 * Sets the editor for a column.
8911 * @param {Number} col The column index
8912 * @param {Object} editor The editor object
8914 setEditor : function(col, editor){
8915 this.config[col].editor = editor;
8918 * Add a column (experimental...) - defaults to adding to the end..
8919 * @param {Object} config
8921 addColumn : function(c)
8924 var i = this.config.length;
8927 if(typeof c.dataIndex == "undefined"){
8930 if(typeof c.renderer == "string"){
8931 c.renderer = Roo.util.Format[c.renderer];
8933 if(typeof c.id == "undefined"){
8936 if(c.editor && c.editor.xtype){
8937 c.editor = Roo.factory(c.editor, Roo.grid);
8939 if(c.editor && c.editor.isFormField){
8940 c.editor = new Roo.grid.GridEditor(c.editor);
8942 this.lookup[c.id] = c;
8947 Roo.grid.ColumnModel.defaultRenderer = function(value)
8949 if(typeof value == "object") {
8952 if(typeof value == "string" && value.length < 1){
8956 return String.format("{0}", value);
8959 // Alias for backwards compatibility
8960 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8963 * Ext JS Library 1.1.1
8964 * Copyright(c) 2006-2007, Ext JS, LLC.
8966 * Originally Released Under LGPL - original licence link has changed is not relivant.
8969 * <script type="text/javascript">
8973 * @class Roo.LoadMask
8974 * A simple utility class for generically masking elements while loading data. If the element being masked has
8975 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8976 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8977 * element's UpdateManager load indicator and will be destroyed after the initial load.
8979 * Create a new LoadMask
8980 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8981 * @param {Object} config The config object
8983 Roo.LoadMask = function(el, config){
8984 this.el = Roo.get(el);
8985 Roo.apply(this, config);
8987 this.store.on('beforeload', this.onBeforeLoad, this);
8988 this.store.on('load', this.onLoad, this);
8989 this.store.on('loadexception', this.onLoadException, this);
8990 this.removeMask = false;
8992 var um = this.el.getUpdateManager();
8993 um.showLoadIndicator = false; // disable the default indicator
8994 um.on('beforeupdate', this.onBeforeLoad, this);
8995 um.on('update', this.onLoad, this);
8996 um.on('failure', this.onLoad, this);
8997 this.removeMask = true;
9001 Roo.LoadMask.prototype = {
9003 * @cfg {Boolean} removeMask
9004 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9005 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9010 * The text to display in a centered loading message box (defaults to 'Loading...')
9014 * @cfg {String} msgCls
9015 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9017 msgCls : 'x-mask-loading',
9020 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9026 * Disables the mask to prevent it from being displayed
9028 disable : function(){
9029 this.disabled = true;
9033 * Enables the mask so that it can be displayed
9035 enable : function(){
9036 this.disabled = false;
9039 onLoadException : function()
9043 if (typeof(arguments[3]) != 'undefined') {
9044 Roo.MessageBox.alert("Error loading",arguments[3]);
9048 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9049 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9056 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9061 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9065 onBeforeLoad : function(){
9067 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9072 destroy : function(){
9074 this.store.un('beforeload', this.onBeforeLoad, this);
9075 this.store.un('load', this.onLoad, this);
9076 this.store.un('loadexception', this.onLoadException, this);
9078 var um = this.el.getUpdateManager();
9079 um.un('beforeupdate', this.onBeforeLoad, this);
9080 um.un('update', this.onLoad, this);
9081 um.un('failure', this.onLoad, this);
9085 * @class Roo.bootstrap.Table
9087 * @extends Roo.bootstrap.Component
9088 * @children Roo.bootstrap.TableBody
9089 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9090 * Similar to Roo.grid.Grid
9092 var table = Roo.factory({
9094 xns : Roo.bootstrap,
9095 autoSizeColumns: true,
9102 sortInfo : { direction : 'ASC', field: 'name' },
9104 xtype : 'HttpProxy',
9107 url : 'https://example.com/some.data.url.json'
9110 xtype : 'JsonReader',
9112 fields : [ 'id', 'name', whatever' ],
9119 xtype : 'ColumnModel',
9123 dataIndex : 'is_in_group',
9126 renderer : function(v, x , r) {
9128 return String.format("{0}", v)
9134 xtype : 'RowSelectionModel',
9135 xns : Roo.bootstrap.Table
9136 // you can add listeners to catch selection change here....
9142 grid.render(Roo.get("some-div"));
9145 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9150 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9151 * @cfg {Roo.data.Store} store The data store to use
9152 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9154 * @cfg {String} cls table class
9157 * @cfg {string} empty_results Text to display for no results
9158 * @cfg {boolean} striped Should the rows be alternative striped
9159 * @cfg {boolean} bordered Add borders to the table
9160 * @cfg {boolean} hover Add hover highlighting
9161 * @cfg {boolean} condensed Format condensed
9162 * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
9163 * also adds table-responsive (see bootstrap docs for details)
9164 * @cfg {Boolean} loadMask (true|false) default false
9165 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9166 * @cfg {Boolean} footerRow (true|false) generate tfoot with columns of values, default false
9167 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9168 * @cfg {Boolean} rowSelection (true|false) default false
9169 * @cfg {Boolean} cellSelection (true|false) default false
9170 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9171 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9172 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9173 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9174 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9175 * @cfg {Boolean} disableAutoSize disable autoSize() and initCSS()
9178 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9181 * Create a new Table
9182 * @param {Object} config The config object
9185 Roo.bootstrap.Table = function(config)
9187 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9190 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9191 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9192 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9193 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9195 this.view = this; // compat with grid.
9197 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9199 this.sm.grid = this;
9200 this.selModel = Roo.factory(this.sm, Roo.grid);
9201 this.sm = this.selModel;
9202 this.sm.xmodule = this.xmodule || false;
9205 if (this.cm && typeof(this.cm.config) == 'undefined') {
9206 this.colModel = new Roo.grid.ColumnModel(this.cm);
9207 this.cm = this.colModel;
9208 this.cm.xmodule = this.xmodule || false;
9211 this.store= Roo.factory(this.store, Roo.data);
9212 this.ds = this.store;
9213 this.ds.xmodule = this.xmodule || false;
9216 if (this.footer && this.store) {
9217 this.footer.dataSource = this.ds;
9218 this.footer = Roo.factory(this.footer);
9225 * Fires when a cell is clicked
9226 * @param {Roo.bootstrap.Table} this
9227 * @param {Roo.Element} el
9228 * @param {Number} rowIndex
9229 * @param {Number} columnIndex
9230 * @param {Roo.EventObject} e
9234 * @event celldblclick
9235 * Fires when a cell is double clicked
9236 * @param {Roo.bootstrap.Table} this
9237 * @param {Roo.Element} el
9238 * @param {Number} rowIndex
9239 * @param {Number} columnIndex
9240 * @param {Roo.EventObject} e
9242 "celldblclick" : true,
9245 * Fires when a row is clicked
9246 * @param {Roo.bootstrap.Table} this
9247 * @param {Roo.Element} el
9248 * @param {Number} rowIndex
9249 * @param {Roo.EventObject} e
9253 * @event rowdblclick
9254 * Fires when a row is double clicked
9255 * @param {Roo.bootstrap.Table} this
9256 * @param {Roo.Element} el
9257 * @param {Number} rowIndex
9258 * @param {Roo.EventObject} e
9260 "rowdblclick" : true,
9263 * Fires when a mouseover occur
9264 * @param {Roo.bootstrap.Table} this
9265 * @param {Roo.Element} el
9266 * @param {Number} rowIndex
9267 * @param {Number} columnIndex
9268 * @param {Roo.EventObject} e
9273 * Fires when a mouseout occur
9274 * @param {Roo.bootstrap.Table} this
9275 * @param {Roo.Element} el
9276 * @param {Number} rowIndex
9277 * @param {Number} columnIndex
9278 * @param {Roo.EventObject} e
9283 * Fires when a row is rendered, so you can change add a style to it.
9284 * @param {Roo.bootstrap.Table} this
9285 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9289 * @event rowsrendered
9290 * Fires when all the rows have been rendered
9291 * @param {Roo.bootstrap.Table} this
9293 'rowsrendered' : true,
9295 * @event contextmenu
9296 * The raw contextmenu event for the entire grid.
9297 * @param {Roo.EventObject} e
9299 "contextmenu" : true,
9301 * @event rowcontextmenu
9302 * Fires when a row is right clicked
9303 * @param {Roo.bootstrap.Table} this
9304 * @param {Number} rowIndex
9305 * @param {Roo.EventObject} e
9307 "rowcontextmenu" : true,
9309 * @event cellcontextmenu
9310 * Fires when a cell is right clicked
9311 * @param {Roo.bootstrap.Table} this
9312 * @param {Number} rowIndex
9313 * @param {Number} cellIndex
9314 * @param {Roo.EventObject} e
9316 "cellcontextmenu" : true,
9318 * @event headercontextmenu
9319 * Fires when a header is right clicked
9320 * @param {Roo.bootstrap.Table} this
9321 * @param {Number} columnIndex
9322 * @param {Roo.EventObject} e
9324 "headercontextmenu" : true,
9327 * The raw mousedown event for the entire grid.
9328 * @param {Roo.EventObject} e
9335 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9353 enableColumnResize: true,
9354 disableAutoSize: false,
9356 rowSelection : false,
9357 cellSelection : false,
9360 minColumnWidth : 50,
9362 // Roo.Element - the tbody
9363 bodyEl: false, // <tbody> Roo.Element - thead element
9364 headEl: false, // <thead> Roo.Element - thead element
9365 resizeProxy : false, // proxy element for dragging?
9369 container: false, // used by gridpanel...
9375 auto_hide_footer : false,
9377 view: false, // actually points to this..
9379 getAutoCreate : function()
9381 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9388 // this get's auto added by panel.Grid
9389 if (this.scrollBody) {
9390 cfg.cls += ' table-body-fixed';
9393 cfg.cls += ' table-striped';
9397 cfg.cls += ' table-hover';
9399 if (this.bordered) {
9400 cfg.cls += ' table-bordered';
9402 if (this.condensed) {
9403 cfg.cls += ' table-condensed';
9406 if (this.responsive) {
9407 cfg.cls += ' table-responsive';
9411 cfg.cls+= ' ' +this.cls;
9417 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9420 if(this.store || this.cm){
9421 if(this.headerShow){
9422 cfg.cn.push(this.renderHeader());
9425 cfg.cn.push(this.renderBody());
9427 if(this.footerShow || this.footerRow){
9428 cfg.cn.push(this.renderFooter());
9431 // where does this come from?
9432 //cfg.cls+= ' TableGrid';
9435 return { cn : [ cfg ] };
9438 initEvents : function()
9440 if(!this.store || !this.cm){
9443 if (this.selModel) {
9444 this.selModel.initEvents();
9448 //Roo.log('initEvents with ds!!!!');
9450 this.bodyEl = this.el.select('tbody', true).first();
9451 this.headEl = this.el.select('thead', true).first();
9452 this.mainFoot = this.el.select('tfoot', true).first();
9457 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9458 e.on('click', this.sort, this);
9462 // why is this done????? = it breaks dialogs??
9463 //this.parent().el.setStyle('position', 'relative');
9467 this.footer.parentId = this.id;
9468 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9471 this.el.select('tfoot tr td').first().addClass('hide');
9476 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9479 this.store.on('load', this.onLoad, this);
9480 this.store.on('beforeload', this.onBeforeLoad, this);
9481 this.store.on('update', this.onUpdate, this);
9482 this.store.on('add', this.onAdd, this);
9483 this.store.on("clear", this.clear, this);
9485 this.el.on("contextmenu", this.onContextMenu, this);
9488 this.cm.on("headerchange", this.onHeaderChange, this);
9489 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9491 //?? does bodyEl get replaced on render?
9492 this.bodyEl.on("click", this.onClick, this);
9493 this.bodyEl.on("dblclick", this.onDblClick, this);
9494 this.bodyEl.on('scroll', this.onBodyScroll, this);
9496 // guessing mainbody will work - this relays usually caught by selmodel at present.
9497 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9500 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9503 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9504 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9509 // Compatibility with grid - we implement all the view features at present.
9510 getView : function()
9515 initCSS : function()
9517 if(this.disableAutoSize) {
9521 var cm = this.cm, styles = [];
9522 this.CSS.removeStyleSheet(this.id + '-cssrules');
9523 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9524 // we can honour xs/sm/md/xl as widths...
9525 // we first have to decide what widht we are currently at...
9526 var sz = Roo.getGridSize();
9530 var cols = []; // visable cols.
9532 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9533 var w = cm.getColumnWidth(i, false);
9535 cols.push( { rel : false, abs : 0 });
9539 cols.push( { rel : false, abs : w });
9541 last = i; // not really..
9544 var w = cm.getColumnWidth(i, sz);
9549 cols.push( { rel : w, abs : false });
9552 var avail = this.bodyEl.dom.clientWidth - total_abs;
9554 var unitWidth = Math.floor(avail / total);
9555 var rem = avail - (unitWidth * total);
9557 var hidden, width, pos = 0 , splithide , left;
9558 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9560 hidden = 'display:none;';
9562 width = 'width:0px;';
9564 if(!cm.isHidden(i)){
9568 // we can honour xs/sm/md/xl ?
9569 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9571 hidden = 'display:none;';
9573 // width should return a small number...
9575 w+=rem; // add the remaining with..
9578 left = "left:" + (pos -4) + "px;";
9579 width = "width:" + w+ "px;";
9582 if (this.responsive) {
9585 hidden = cm.isHidden(i) ? 'display:none;' : '';
9586 splithide = 'display: none;';
9589 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9592 splithide = 'display:none;';
9595 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9596 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9597 // this is the popover version..
9598 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9603 //Roo.log(styles.join(''));
9604 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9610 onContextMenu : function(e, t)
9612 this.processEvent("contextmenu", e);
9615 processEvent : function(name, e)
9617 if (name != 'touchstart' ) {
9618 this.fireEvent(name, e);
9621 var t = e.getTarget();
9623 var cell = Roo.get(t);
9629 if(cell.findParent('tfoot', false, true)){
9633 if(cell.findParent('thead', false, true)){
9635 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9636 cell = Roo.get(t).findParent('th', false, true);
9638 Roo.log("failed to find th in thead?");
9639 Roo.log(e.getTarget());
9644 var cellIndex = cell.dom.cellIndex;
9646 var ename = name == 'touchstart' ? 'click' : name;
9647 this.fireEvent("header" + ename, this, cellIndex, e);
9652 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9653 cell = Roo.get(t).findParent('td', false, true);
9655 Roo.log("failed to find th in tbody?");
9656 Roo.log(e.getTarget());
9661 var row = cell.findParent('tr', false, true);
9662 var cellIndex = cell.dom.cellIndex;
9663 var rowIndex = row.dom.rowIndex - 1;
9667 this.fireEvent("row" + name, this, rowIndex, e);
9671 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9677 onMouseover : function(e, el)
9679 var cell = Roo.get(el);
9685 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9686 cell = cell.findParent('td', false, true);
9689 var row = cell.findParent('tr', false, true);
9690 var cellIndex = cell.dom.cellIndex;
9691 var rowIndex = row.dom.rowIndex - 1; // start from 0
9693 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9697 onMouseout : function(e, el)
9699 var cell = Roo.get(el);
9705 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9706 cell = cell.findParent('td', false, true);
9709 var row = cell.findParent('tr', false, true);
9710 var cellIndex = cell.dom.cellIndex;
9711 var rowIndex = row.dom.rowIndex - 1; // start from 0
9713 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9717 onClick : function(e, el)
9719 var cell = Roo.get(el);
9721 if(!cell || (!this.cellSelection && !this.rowSelection)){
9725 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9726 cell = cell.findParent('td', false, true);
9729 if(!cell || typeof(cell) == 'undefined'){
9733 var row = cell.findParent('tr', false, true);
9735 if(!row || typeof(row) == 'undefined'){
9739 var cellIndex = cell.dom.cellIndex;
9740 var rowIndex = this.getRowIndex(row);
9742 // why??? - should these not be based on SelectionModel?
9743 //if(this.cellSelection){
9744 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9747 //if(this.rowSelection){
9748 this.fireEvent('rowclick', this, row, rowIndex, e);
9753 onDblClick : function(e,el)
9755 var cell = Roo.get(el);
9757 if(!cell || (!this.cellSelection && !this.rowSelection)){
9761 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9762 cell = cell.findParent('td', false, true);
9765 if(!cell || typeof(cell) == 'undefined'){
9769 var row = cell.findParent('tr', false, true);
9771 if(!row || typeof(row) == 'undefined'){
9775 var cellIndex = cell.dom.cellIndex;
9776 var rowIndex = this.getRowIndex(row);
9778 if(this.cellSelection){
9779 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9782 if(this.rowSelection){
9783 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9786 findRowIndex : function(el)
9788 var cell = Roo.get(el);
9792 var row = cell.findParent('tr', false, true);
9794 if(!row || typeof(row) == 'undefined'){
9797 return this.getRowIndex(row);
9799 sort : function(e,el)
9801 var col = Roo.get(el);
9803 if(!col.hasClass('sortable')){
9807 var sort = col.attr('sort');
9810 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9814 this.store.sortInfo = {field : sort, direction : dir};
9817 Roo.log("calling footer first");
9818 this.footer.onClick('first');
9821 this.store.load({ params : { start : 0 } });
9825 renderHeader : function()
9833 this.totalWidth = 0;
9835 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9837 var config = cm.config[i];
9841 cls : 'x-hcol-' + i,
9844 html: cm.getColumnHeader(i)
9847 var tooltip = cm.getColumnTooltip(i);
9849 c.tooltip = tooltip;
9855 if(typeof(config.sortable) != 'undefined' && config.sortable){
9856 c.cls += ' sortable';
9857 c.html = '<i class="fa"></i>' + c.html;
9860 // could use BS4 hidden-..-down
9862 if(typeof(config.lgHeader) != 'undefined'){
9863 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9866 if(typeof(config.mdHeader) != 'undefined'){
9867 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9870 if(typeof(config.smHeader) != 'undefined'){
9871 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9874 if(typeof(config.xsHeader) != 'undefined'){
9875 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9882 if(typeof(config.tooltip) != 'undefined'){
9883 c.tooltip = config.tooltip;
9886 if(typeof(config.colspan) != 'undefined'){
9887 c.colspan = config.colspan;
9890 // hidden is handled by CSS now
9892 if(typeof(config.dataIndex) != 'undefined'){
9893 c.sort = config.dataIndex;
9898 if(typeof(config.align) != 'undefined' && config.align.length){
9899 c.style += ' text-align:' + config.align + ';';
9902 /* width is done in CSS
9903 *if(typeof(config.width) != 'undefined'){
9904 c.style += ' width:' + config.width + 'px;';
9905 this.totalWidth += config.width;
9907 this.totalWidth += 100; // assume minimum of 100 per column?
9911 if(typeof(config.cls) != 'undefined'){
9912 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9914 // this is the bit that doesnt reall work at all...
9916 if (this.responsive) {
9919 ['xs','sm','md','lg'].map(function(size){
9921 if(typeof(config[size]) == 'undefined'){
9925 if (!config[size]) { // 0 = hidden
9926 // BS 4 '0' is treated as hide that column and below.
9927 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9931 c.cls += ' col-' + size + '-' + config[size] + (
9932 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9940 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9951 renderBody : function()
9961 colspan : this.cm.getColumnCount()
9971 renderFooter : function()
9981 colspan : this.cm.getColumnCount()
9993 // Roo.log('ds onload');
9998 var ds = this.store;
10000 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10001 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
10002 if (_this.store.sortInfo) {
10004 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
10005 e.select('i', true).addClass(['fa-arrow-up']);
10008 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
10009 e.select('i', true).addClass(['fa-arrow-down']);
10014 var tbody = this.bodyEl;
10016 if(ds.getCount() > 0){
10017 ds.data.each(function(d,rowIndex){
10018 var row = this.renderRow(cm, ds, rowIndex);
10020 tbody.createChild(row);
10024 if(row.cellObjects.length){
10025 Roo.each(row.cellObjects, function(r){
10026 _this.renderCellObject(r);
10031 } else if (this.empty_results.length) {
10032 this.el.mask(this.empty_results, 'no-spinner');
10035 var tfoot = this.el.select('tfoot', true).first();
10037 if(this.footerShow && !this.footerRow && this.auto_hide_footer && this.mainFoot){
10039 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10041 var total = this.ds.getTotalCount();
10043 if(this.footer.pageSize < total){
10044 this.mainFoot.show();
10048 if(!this.footerShow && this.footerRow) {
10055 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10056 var footer = typeof(cm.config[i].footer) == "function" ? cm.config[i].footer(ds, cm.config[i]) : cm.config[i].footer;
10059 cls : ' x-fcol-' + i,
10067 tfoot.dom.innerHTML = '';
10069 tfoot.createChild(tr);
10072 Roo.each(this.el.select('tbody td', true).elements, function(e){
10073 e.on('mouseover', _this.onMouseover, _this);
10076 Roo.each(this.el.select('tbody td', true).elements, function(e){
10077 e.on('mouseout', _this.onMouseout, _this);
10079 this.fireEvent('rowsrendered', this);
10083 this.initCSS(); /// resize cols
10089 onUpdate : function(ds,record)
10091 this.refreshRow(record);
10095 onRemove : function(ds, record, index, isUpdate){
10096 if(isUpdate !== true){
10097 this.fireEvent("beforerowremoved", this, index, record);
10099 var bt = this.bodyEl.dom;
10101 var rows = this.el.select('tbody > tr', true).elements;
10103 if(typeof(rows[index]) != 'undefined'){
10104 bt.removeChild(rows[index].dom);
10107 // if(bt.rows[index]){
10108 // bt.removeChild(bt.rows[index]);
10111 if(isUpdate !== true){
10112 //this.stripeRows(index);
10113 //this.syncRowHeights(index, index);
10115 this.fireEvent("rowremoved", this, index, record);
10119 onAdd : function(ds, records, rowIndex)
10121 //Roo.log('on Add called');
10122 // - note this does not handle multiple adding very well..
10123 var bt = this.bodyEl.dom;
10124 for (var i =0 ; i < records.length;i++) {
10125 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10126 //Roo.log(records[i]);
10127 //Roo.log(this.store.getAt(rowIndex+i));
10128 this.insertRow(this.store, rowIndex + i, false);
10135 refreshRow : function(record){
10136 var ds = this.store, index;
10137 if(typeof record == 'number'){
10139 record = ds.getAt(index);
10141 index = ds.indexOf(record);
10143 return; // should not happen - but seems to
10146 this.insertRow(ds, index, true);
10148 this.onRemove(ds, record, index+1, true);
10150 //this.syncRowHeights(index, index);
10152 this.fireEvent("rowupdated", this, index, record);
10154 // private - called by RowSelection
10155 onRowSelect : function(rowIndex){
10156 var row = this.getRowDom(rowIndex);
10157 row.addClass(['bg-info','info']);
10159 // private - called by RowSelection
10160 onRowDeselect : function(rowIndex)
10162 if (rowIndex < 0) {
10165 var row = this.getRowDom(rowIndex);
10166 row.removeClass(['bg-info','info']);
10169 * Focuses the specified row.
10170 * @param {Number} row The row index
10172 focusRow : function(row)
10174 //Roo.log('GridView.focusRow');
10175 var x = this.bodyEl.dom.scrollLeft;
10176 this.focusCell(row, 0, false);
10177 this.bodyEl.dom.scrollLeft = x;
10181 * Focuses the specified cell.
10182 * @param {Number} row The row index
10183 * @param {Number} col The column index
10184 * @param {Boolean} hscroll false to disable horizontal scrolling
10186 focusCell : function(row, col, hscroll)
10188 //Roo.log('GridView.focusCell');
10189 var el = this.ensureVisible(row, col, hscroll);
10190 // not sure what focusEL achives = it's a <a> pos relative
10191 //this.focusEl.alignTo(el, "tl-tl");
10193 // this.focusEl.focus();
10195 // this.focusEl.focus.defer(1, this.focusEl);
10200 * Scrolls the specified cell into view
10201 * @param {Number} row The row index
10202 * @param {Number} col The column index
10203 * @param {Boolean} hscroll false to disable horizontal scrolling
10205 ensureVisible : function(row, col, hscroll)
10207 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10208 //return null; //disable for testing.
10209 if(typeof row != "number"){
10210 row = row.rowIndex;
10212 if(row < 0 && row >= this.ds.getCount()){
10215 col = (col !== undefined ? col : 0);
10217 while(cm.isHidden(col)){
10221 var el = this.getCellDom(row, col);
10225 var c = this.bodyEl.dom;
10227 var ctop = parseInt(el.offsetTop, 10);
10228 var cleft = parseInt(el.offsetLeft, 10);
10229 var cbot = ctop + el.offsetHeight;
10230 var cright = cleft + el.offsetWidth;
10232 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10233 var ch = 0; //?? header is not withing the area?
10234 var stop = parseInt(c.scrollTop, 10);
10235 var sleft = parseInt(c.scrollLeft, 10);
10236 var sbot = stop + ch;
10237 var sright = sleft + c.clientWidth;
10239 Roo.log('GridView.ensureVisible:' +
10241 ' c.clientHeight:' + c.clientHeight +
10242 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10250 c.scrollTop = ctop;
10251 //Roo.log("set scrolltop to ctop DISABLE?");
10252 }else if(cbot > sbot){
10253 //Roo.log("set scrolltop to cbot-ch");
10254 c.scrollTop = cbot-ch;
10257 if(hscroll !== false){
10259 c.scrollLeft = cleft;
10260 }else if(cright > sright){
10261 c.scrollLeft = cright-c.clientWidth;
10269 insertRow : function(dm, rowIndex, isUpdate){
10272 this.fireEvent("beforerowsinserted", this, rowIndex);
10274 //var s = this.getScrollState();
10275 var row = this.renderRow(this.cm, this.store, rowIndex);
10276 // insert before rowIndex..
10277 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10281 if(row.cellObjects.length){
10282 Roo.each(row.cellObjects, function(r){
10283 _this.renderCellObject(r);
10288 this.fireEvent("rowsinserted", this, rowIndex);
10289 //this.syncRowHeights(firstRow, lastRow);
10290 //this.stripeRows(firstRow);
10297 getRowDom : function(rowIndex)
10299 var rows = this.el.select('tbody > tr', true).elements;
10301 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10304 getCellDom : function(rowIndex, colIndex)
10306 var row = this.getRowDom(rowIndex);
10307 if (row === false) {
10310 var cols = row.select('td', true).elements;
10311 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10315 // returns the object tree for a tr..
10318 renderRow : function(cm, ds, rowIndex)
10320 var d = ds.getAt(rowIndex);
10324 cls : 'x-row-' + rowIndex,
10328 var cellObjects = [];
10330 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10331 var config = cm.config[i];
10333 var renderer = cm.getRenderer(i);
10337 if(typeof(renderer) !== 'undefined'){
10338 value = renderer.call(config, d.data[cm.getDataIndex(i)], false, d);
10340 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10341 // and are rendered into the cells after the row is rendered - using the id for the element.
10343 if(typeof(value) === 'object'){
10353 rowIndex : rowIndex,
10358 this.fireEvent('rowclass', this, rowcfg);
10362 // this might end up displaying HTML?
10363 // this is too messy... - better to only do it on columsn you know are going to be too long
10364 //tooltip : (typeof(value) === 'object') ? '' : value,
10365 cls : rowcfg.rowClass + ' x-col-' + i,
10367 html: (typeof(value) === 'object') ? '' : value
10374 if(typeof(config.colspan) != 'undefined'){
10375 td.colspan = config.colspan;
10380 if(typeof(config.align) != 'undefined' && config.align.length){
10381 td.style += ' text-align:' + config.align + ';';
10383 if(typeof(config.valign) != 'undefined' && config.valign.length){
10384 td.style += ' vertical-align:' + config.valign + ';';
10387 if(typeof(config.width) != 'undefined'){
10388 td.style += ' width:' + config.width + 'px;';
10392 if(typeof(config.cursor) != 'undefined'){
10393 td.style += ' cursor:' + config.cursor + ';';
10396 if(typeof(config.cls) != 'undefined'){
10397 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10399 if (this.responsive) {
10400 ['xs','sm','md','lg'].map(function(size){
10402 if(typeof(config[size]) == 'undefined'){
10408 if (!config[size]) { // 0 = hidden
10409 // BS 4 '0' is treated as hide that column and below.
10410 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10414 td.cls += ' col-' + size + '-' + config[size] + (
10415 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10425 row.cellObjects = cellObjects;
10433 onBeforeLoad : function()
10435 this.el.unmask(); // if needed.
10442 this.el.select('tbody', true).first().dom.innerHTML = '';
10445 * Show or hide a row.
10446 * @param {Number} rowIndex to show or hide
10447 * @param {Boolean} state hide
10449 setRowVisibility : function(rowIndex, state)
10451 var bt = this.bodyEl.dom;
10453 var rows = this.el.select('tbody > tr', true).elements;
10455 if(typeof(rows[rowIndex]) == 'undefined'){
10458 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10463 getSelectionModel : function(){
10464 if(!this.selModel){
10465 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10467 return this.selModel;
10470 * Render the Roo.bootstrap object from renderder
10472 renderCellObject : function(r)
10476 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10478 var t = r.cfg.render(r.container);
10481 Roo.each(r.cfg.cn, function(c){
10483 container: t.getChildContainer(),
10486 _this.renderCellObject(child);
10491 * get the Row Index from a dom element.
10492 * @param {Roo.Element} row The row to look for
10493 * @returns {Number} the row
10495 getRowIndex : function(row)
10499 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10510 * get the header TH element for columnIndex
10511 * @param {Number} columnIndex
10512 * @returns {Roo.Element}
10514 getHeaderIndex: function(colIndex)
10516 var cols = this.headEl.select('th', true).elements;
10517 return cols[colIndex];
10520 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10521 * @param {domElement} cell to look for
10522 * @returns {Number} the column
10524 getCellIndex : function(cell)
10526 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10528 return parseInt(id[1], 10);
10533 * Returns the grid's underlying element = used by panel.Grid
10534 * @return {Element} The element
10536 getGridEl : function(){
10540 * Forces a resize - used by panel.Grid
10541 * @return {Element} The element
10543 autoSize : function()
10545 if(this.disableAutoSize) {
10548 //var ctr = Roo.get(this.container.dom.parentElement);
10549 var ctr = Roo.get(this.el.dom);
10551 var thd = this.getGridEl().select('thead',true).first();
10552 var tbd = this.getGridEl().select('tbody', true).first();
10553 var tfd = this.getGridEl().select('tfoot', true).first();
10555 var cw = ctr.getWidth();
10556 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10560 tbd.setWidth(ctr.getWidth());
10561 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10562 // this needs fixing for various usage - currently only hydra job advers I think..
10564 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10566 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10569 cw = Math.max(cw, this.totalWidth);
10570 this.getGridEl().select('tbody tr',true).setWidth(cw);
10573 // resize 'expandable coloumn?
10575 return; // we doe not have a view in this design..
10578 onBodyScroll: function()
10580 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10582 this.headEl.setStyle({
10583 'position' : 'relative',
10584 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10590 var scrollHeight = this.bodyEl.dom.scrollHeight;
10592 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10594 var height = this.bodyEl.getHeight();
10596 if(scrollHeight - height == scrollTop) {
10598 var total = this.ds.getTotalCount();
10600 if(this.footer.cursor + this.footer.pageSize < total){
10602 this.footer.ds.load({
10604 start : this.footer.cursor + this.footer.pageSize,
10605 limit : this.footer.pageSize
10614 onColumnSplitterMoved : function(i, diff)
10616 this.userResized = true;
10618 var cm = this.colModel;
10620 var w = this.getHeaderIndex(i).getWidth() + diff;
10623 cm.setColumnWidth(i, w, true);
10625 //var cid = cm.getColumnId(i); << not used in this version?
10626 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10628 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10629 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10630 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10632 //this.updateSplitters();
10633 //this.layout(); << ??
10634 this.fireEvent("columnresize", i, w);
10636 onHeaderChange : function()
10638 var header = this.renderHeader();
10639 var table = this.el.select('table', true).first();
10641 this.headEl.remove();
10642 this.headEl = table.createChild(header, this.bodyEl, false);
10644 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10645 e.on('click', this.sort, this);
10648 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10649 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10654 onHiddenChange : function(colModel, colIndex, hidden)
10657 this.cm.setHidden()
10658 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10659 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10661 this.CSS.updateRule(thSelector, "display", "");
10662 this.CSS.updateRule(tdSelector, "display", "");
10665 this.CSS.updateRule(thSelector, "display", "none");
10666 this.CSS.updateRule(tdSelector, "display", "none");
10669 // onload calls initCSS()
10670 this.onHeaderChange();
10674 setColumnWidth: function(col_index, width)
10676 // width = "md-2 xs-2..."
10677 if(!this.colModel.config[col_index]) {
10681 var w = width.split(" ");
10683 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10685 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10688 for(var j = 0; j < w.length; j++) {
10694 var size_cls = w[j].split("-");
10696 if(!Number.isInteger(size_cls[1] * 1)) {
10700 if(!this.colModel.config[col_index][size_cls[0]]) {
10704 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10708 h_row[0].classList.replace(
10709 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10710 "col-"+size_cls[0]+"-"+size_cls[1]
10713 for(var i = 0; i < rows.length; i++) {
10715 var size_cls = w[j].split("-");
10717 if(!Number.isInteger(size_cls[1] * 1)) {
10721 if(!this.colModel.config[col_index][size_cls[0]]) {
10725 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10729 rows[i].classList.replace(
10730 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10731 "col-"+size_cls[0]+"-"+size_cls[1]
10735 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10740 // currently only used to find the split on drag..
10741 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10746 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10747 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10756 * @class Roo.bootstrap.TableCell
10757 * @extends Roo.bootstrap.Component
10758 * @children Roo.bootstrap.Component
10759 * @parent Roo.bootstrap.TableRow
10760 * Bootstrap TableCell class
10762 * @cfg {String} html cell contain text
10763 * @cfg {String} cls cell class
10764 * @cfg {String} tag cell tag (td|th) default td
10765 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10766 * @cfg {String} align Aligns the content in a cell
10767 * @cfg {String} axis Categorizes cells
10768 * @cfg {String} bgcolor Specifies the background color of a cell
10769 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10770 * @cfg {Number} colspan Specifies the number of columns a cell should span
10771 * @cfg {String} headers Specifies one or more header cells a cell is related to
10772 * @cfg {Number} height Sets the height of a cell
10773 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10774 * @cfg {Number} rowspan Sets the number of rows a cell should span
10775 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10776 * @cfg {String} valign Vertical aligns the content in a cell
10777 * @cfg {Number} width Specifies the width of a cell
10780 * Create a new TableCell
10781 * @param {Object} config The config object
10784 Roo.bootstrap.TableCell = function(config){
10785 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10788 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10808 getAutoCreate : function(){
10809 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10816 cfg.tag = this.tag;
10829 cfg.align=this.align
10834 if (this.bgcolor) {
10835 cfg.bgcolor=this.bgcolor
10837 if (this.charoff) {
10838 cfg.charoff=this.charoff
10840 if (this.colspan) {
10841 cfg.colspan=this.colspan
10843 if (this.headers) {
10844 cfg.headers=this.headers
10847 cfg.height=this.height
10850 cfg.nowrap=this.nowrap
10852 if (this.rowspan) {
10853 cfg.rowspan=this.rowspan
10856 cfg.scope=this.scope
10859 cfg.valign=this.valign
10862 cfg.width=this.width
10881 * @class Roo.bootstrap.TableRow
10882 * @extends Roo.bootstrap.Component
10883 * @children Roo.bootstrap.TableCell
10884 * @parent Roo.bootstrap.TableBody
10885 * Bootstrap TableRow class
10886 * @cfg {String} cls row class
10887 * @cfg {String} align Aligns the content in a table row
10888 * @cfg {String} bgcolor Specifies a background color for a table row
10889 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10890 * @cfg {String} valign Vertical aligns the content in a table row
10893 * Create a new TableRow
10894 * @param {Object} config The config object
10897 Roo.bootstrap.TableRow = function(config){
10898 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10901 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10909 getAutoCreate : function(){
10910 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10917 cfg.cls = this.cls;
10920 cfg.align = this.align;
10923 cfg.bgcolor = this.bgcolor;
10926 cfg.charoff = this.charoff;
10929 cfg.valign = this.valign;
10947 * @class Roo.bootstrap.TableBody
10948 * @extends Roo.bootstrap.Component
10949 * @children Roo.bootstrap.TableRow
10950 * @parent Roo.bootstrap.Table
10951 * Bootstrap TableBody class
10952 * @cfg {String} cls element class
10953 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10954 * @cfg {String} align Aligns the content inside the element
10955 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10956 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10959 * Create a new TableBody
10960 * @param {Object} config The config object
10963 Roo.bootstrap.TableBody = function(config){
10964 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10967 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10975 getAutoCreate : function(){
10976 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10986 cfg.tag = this.tag;
10990 cfg.align = this.align;
10993 cfg.charoff = this.charoff;
10996 cfg.valign = this.valign;
11003 // initEvents : function()
11006 // if(!this.store){
11010 // this.store = Roo.factory(this.store, Roo.data);
11011 // this.store.on('load', this.onLoad, this);
11013 // this.store.load();
11017 // onLoad: function ()
11019 // this.fireEvent('load', this);
11029 * Ext JS Library 1.1.1
11030 * Copyright(c) 2006-2007, Ext JS, LLC.
11032 * Originally Released Under LGPL - original licence link has changed is not relivant.
11035 * <script type="text/javascript">
11038 // as we use this in bootstrap.
11039 Roo.namespace('Roo.form');
11041 * @class Roo.form.Action
11042 * Internal Class used to handle form actions
11044 * @param {Roo.form.BasicForm} el The form element or its id
11045 * @param {Object} config Configuration options
11050 // define the action interface
11051 Roo.form.Action = function(form, options){
11053 this.options = options || {};
11056 * Client Validation Failed
11059 Roo.form.Action.CLIENT_INVALID = 'client';
11061 * Server Validation Failed
11064 Roo.form.Action.SERVER_INVALID = 'server';
11066 * Connect to Server Failed
11069 Roo.form.Action.CONNECT_FAILURE = 'connect';
11071 * Reading Data from Server Failed
11074 Roo.form.Action.LOAD_FAILURE = 'load';
11076 Roo.form.Action.prototype = {
11078 failureType : undefined,
11079 response : undefined,
11080 result : undefined,
11082 // interface method
11083 run : function(options){
11087 // interface method
11088 success : function(response){
11092 // interface method
11093 handleResponse : function(response){
11097 // default connection failure
11098 failure : function(response){
11100 this.response = response;
11101 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11102 this.form.afterAction(this, false);
11105 processResponse : function(response){
11106 this.response = response;
11107 if(!response.responseText){
11110 this.result = this.handleResponse(response);
11111 return this.result;
11114 // utility functions used internally
11115 getUrl : function(appendParams){
11116 var url = this.options.url || this.form.url || this.form.el.dom.action;
11118 var p = this.getParams();
11120 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11126 getMethod : function(){
11127 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11130 getParams : function(){
11131 var bp = this.form.baseParams;
11132 var p = this.options.params;
11134 if(typeof p == "object"){
11135 p = Roo.urlEncode(Roo.applyIf(p, bp));
11136 }else if(typeof p == 'string' && bp){
11137 p += '&' + Roo.urlEncode(bp);
11140 p = Roo.urlEncode(bp);
11145 createCallback : function(){
11147 success: this.success,
11148 failure: this.failure,
11150 timeout: (this.form.timeout*1000),
11151 upload: this.form.fileUpload ? this.success : undefined
11156 Roo.form.Action.Submit = function(form, options){
11157 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11160 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11163 haveProgress : false,
11164 uploadComplete : false,
11166 // uploadProgress indicator.
11167 uploadProgress : function()
11169 if (!this.form.progressUrl) {
11173 if (!this.haveProgress) {
11174 Roo.MessageBox.progress("Uploading", "Uploading");
11176 if (this.uploadComplete) {
11177 Roo.MessageBox.hide();
11181 this.haveProgress = true;
11183 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11185 var c = new Roo.data.Connection();
11187 url : this.form.progressUrl,
11192 success : function(req){
11193 //console.log(data);
11197 rdata = Roo.decode(req.responseText)
11199 Roo.log("Invalid data from server..");
11203 if (!rdata || !rdata.success) {
11205 Roo.MessageBox.alert(Roo.encode(rdata));
11208 var data = rdata.data;
11210 if (this.uploadComplete) {
11211 Roo.MessageBox.hide();
11216 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11217 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11220 this.uploadProgress.defer(2000,this);
11223 failure: function(data) {
11224 Roo.log('progress url failed ');
11235 // run get Values on the form, so it syncs any secondary forms.
11236 this.form.getValues();
11238 var o = this.options;
11239 var method = this.getMethod();
11240 var isPost = method == 'POST';
11241 if(o.clientValidation === false || this.form.isValid()){
11243 if (this.form.progressUrl) {
11244 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11245 (new Date() * 1) + '' + Math.random());
11250 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11251 form:this.form.el.dom,
11252 url:this.getUrl(!isPost),
11254 params:isPost ? this.getParams() : null,
11255 isUpload: this.form.fileUpload,
11256 formData : this.form.formData
11259 this.uploadProgress();
11261 }else if (o.clientValidation !== false){ // client validation failed
11262 this.failureType = Roo.form.Action.CLIENT_INVALID;
11263 this.form.afterAction(this, false);
11267 success : function(response)
11269 this.uploadComplete= true;
11270 if (this.haveProgress) {
11271 Roo.MessageBox.hide();
11275 var result = this.processResponse(response);
11276 if(result === true || result.success){
11277 this.form.afterAction(this, true);
11281 this.form.markInvalid(result.errors);
11282 this.failureType = Roo.form.Action.SERVER_INVALID;
11284 this.form.afterAction(this, false);
11286 failure : function(response)
11288 this.uploadComplete= true;
11289 if (this.haveProgress) {
11290 Roo.MessageBox.hide();
11293 this.response = response;
11294 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11295 this.form.afterAction(this, false);
11298 handleResponse : function(response){
11299 if(this.form.errorReader){
11300 var rs = this.form.errorReader.read(response);
11303 for(var i = 0, len = rs.records.length; i < len; i++) {
11304 var r = rs.records[i];
11305 errors[i] = r.data;
11308 if(errors.length < 1){
11312 success : rs.success,
11318 var rt = response.responseText;
11319 if (rt.match(/^\<!--\[CDATA\[/)) {
11320 rt = rt.replace(/^\<!--\[CDATA\[/,'');
11321 rt = rt.replace(/\]\]--\>$/,'');
11324 ret = Roo.decode(rt);
11328 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11338 Roo.form.Action.Load = function(form, options){
11339 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11340 this.reader = this.form.reader;
11343 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11348 Roo.Ajax.request(Roo.apply(
11349 this.createCallback(), {
11350 method:this.getMethod(),
11351 url:this.getUrl(false),
11352 params:this.getParams()
11356 success : function(response){
11358 var result = this.processResponse(response);
11359 if(result === true || !result.success || !result.data){
11360 this.failureType = Roo.form.Action.LOAD_FAILURE;
11361 this.form.afterAction(this, false);
11364 this.form.clearInvalid();
11365 this.form.setValues(result.data);
11366 this.form.afterAction(this, true);
11369 handleResponse : function(response){
11370 if(this.form.reader){
11371 var rs = this.form.reader.read(response);
11372 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11374 success : rs.success,
11378 return Roo.decode(response.responseText);
11382 Roo.form.Action.ACTION_TYPES = {
11383 'load' : Roo.form.Action.Load,
11384 'submit' : Roo.form.Action.Submit
11393 * @class Roo.bootstrap.form.Form
11394 * @extends Roo.bootstrap.Component
11395 * @children Roo.bootstrap.Component
11396 * Bootstrap Form class
11397 * @cfg {String} method GET | POST (default POST)
11398 * @cfg {String} labelAlign top | left (default top)
11399 * @cfg {String} align left | right - for navbars
11400 * @cfg {Boolean} loadMask load mask when submit (default true)
11404 * Create a new Form
11405 * @param {Object} config The config object
11409 Roo.bootstrap.form.Form = function(config){
11411 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11413 Roo.bootstrap.form.Form.popover.apply();
11417 * @event clientvalidation
11418 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11419 * @param {Form} this
11420 * @param {Boolean} valid true if the form has passed client-side validation
11422 clientvalidation: true,
11424 * @event beforeaction
11425 * Fires before any action is performed. Return false to cancel the action.
11426 * @param {Form} this
11427 * @param {Action} action The action to be performed
11429 beforeaction: true,
11431 * @event actionfailed
11432 * Fires when an action fails.
11433 * @param {Form} this
11434 * @param {Action} action The action that failed
11436 actionfailed : true,
11438 * @event actioncomplete
11439 * Fires when an action is completed.
11440 * @param {Form} this
11441 * @param {Action} action The action that completed
11443 actioncomplete : true
11447 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11450 * @cfg {String} method
11451 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11455 * @cfg {String} url
11456 * The URL to use for form actions if one isn't supplied in the action options.
11459 * @cfg {Boolean} fileUpload
11460 * Set to true if this form is a file upload.
11464 * @cfg {Object} baseParams
11465 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11469 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11473 * @cfg {Sting} align (left|right) for navbar forms
11478 activeAction : null,
11481 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11482 * element by passing it or its id or mask the form itself by passing in true.
11485 waitMsgTarget : false,
11490 * @cfg {Boolean} errorMask (true|false) default false
11495 * @cfg {Number} maskOffset Default 100
11500 * @cfg {Boolean} maskBody
11504 getAutoCreate : function(){
11508 method : this.method || 'POST',
11509 id : this.id || Roo.id(),
11512 if (this.parent().xtype.match(/^Nav/)) {
11513 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11517 if (this.labelAlign == 'left' ) {
11518 cfg.cls += ' form-horizontal';
11524 initEvents : function()
11526 this.el.on('submit', this.onSubmit, this);
11527 // this was added as random key presses on the form where triggering form submit.
11528 this.el.on('keypress', function(e) {
11529 if (e.getCharCode() != 13) {
11532 // we might need to allow it for textareas.. and some other items.
11533 // check e.getTarget().
11535 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11539 Roo.log("keypress blocked");
11541 e.preventDefault();
11547 onSubmit : function(e){
11552 * Returns true if client-side validation on the form is successful.
11555 isValid : function(){
11556 var items = this.getItems();
11558 var target = false;
11560 items.each(function(f){
11566 Roo.log('invalid field: ' + f.name);
11570 if(!target && f.el.isVisible(true)){
11576 if(this.errorMask && !valid){
11577 Roo.bootstrap.form.Form.popover.mask(this, target);
11584 * Returns true if any fields in this form have changed since their original load.
11587 isDirty : function(){
11589 var items = this.getItems();
11590 items.each(function(f){
11600 * Performs a predefined action (submit or load) or custom actions you define on this form.
11601 * @param {String} actionName The name of the action type
11602 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11603 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11604 * accept other config options):
11606 Property Type Description
11607 ---------------- --------------- ----------------------------------------------------------------------------------
11608 url String The url for the action (defaults to the form's url)
11609 method String The form method to use (defaults to the form's method, or POST if not defined)
11610 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11611 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11612 validate the form on the client (defaults to false)
11614 * @return {BasicForm} this
11616 doAction : function(action, options){
11617 if(typeof action == 'string'){
11618 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11620 if(this.fireEvent('beforeaction', this, action) !== false){
11621 this.beforeAction(action);
11622 action.run.defer(100, action);
11628 beforeAction : function(action){
11629 var o = action.options;
11634 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11636 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11639 // not really supported yet.. ??
11641 //if(this.waitMsgTarget === true){
11642 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11643 //}else if(this.waitMsgTarget){
11644 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11645 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11647 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11653 afterAction : function(action, success){
11654 this.activeAction = null;
11655 var o = action.options;
11660 Roo.get(document.body).unmask();
11666 //if(this.waitMsgTarget === true){
11667 // this.el.unmask();
11668 //}else if(this.waitMsgTarget){
11669 // this.waitMsgTarget.unmask();
11671 // Roo.MessageBox.updateProgress(1);
11672 // Roo.MessageBox.hide();
11679 Roo.callback(o.success, o.scope, [this, action]);
11680 this.fireEvent('actioncomplete', this, action);
11684 // failure condition..
11685 // we have a scenario where updates need confirming.
11686 // eg. if a locking scenario exists..
11687 // we look for { errors : { needs_confirm : true }} in the response.
11689 (typeof(action.result) != 'undefined') &&
11690 (typeof(action.result.errors) != 'undefined') &&
11691 (typeof(action.result.errors.needs_confirm) != 'undefined')
11694 Roo.log("not supported yet");
11697 Roo.MessageBox.confirm(
11698 "Change requires confirmation",
11699 action.result.errorMsg,
11704 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11714 Roo.callback(o.failure, o.scope, [this, action]);
11715 // show an error message if no failed handler is set..
11716 if (!this.hasListener('actionfailed')) {
11717 Roo.log("need to add dialog support");
11719 Roo.MessageBox.alert("Error",
11720 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11721 action.result.errorMsg :
11722 "Saving Failed, please check your entries or try again"
11727 this.fireEvent('actionfailed', this, action);
11732 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11733 * @param {String} id The value to search for
11736 findField : function(id){
11737 var items = this.getItems();
11738 var field = items.get(id);
11740 items.each(function(f){
11741 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11748 return field || null;
11751 * Mark fields in this form invalid in bulk.
11752 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11753 * @return {BasicForm} this
11755 markInvalid : function(errors){
11756 if(errors instanceof Array){
11757 for(var i = 0, len = errors.length; i < len; i++){
11758 var fieldError = errors[i];
11759 var f = this.findField(fieldError.id);
11761 f.markInvalid(fieldError.msg);
11767 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11768 field.markInvalid(errors[id]);
11772 //Roo.each(this.childForms || [], function (f) {
11773 // f.markInvalid(errors);
11780 * Set values for fields in this form in bulk.
11781 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11782 * @return {BasicForm} this
11784 setValues : function(values){
11785 if(values instanceof Array){ // array of objects
11786 for(var i = 0, len = values.length; i < len; i++){
11788 var f = this.findField(v.id);
11790 f.setValue(v.value);
11791 if(this.trackResetOnLoad){
11792 f.originalValue = f.getValue();
11796 }else{ // object hash
11799 if(typeof values[id] != 'function' && (field = this.findField(id))){
11801 if (field.setFromData &&
11802 field.valueField &&
11803 field.displayField &&
11804 // combos' with local stores can
11805 // be queried via setValue()
11806 // to set their value..
11807 (field.store && !field.store.isLocal)
11811 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11812 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11813 field.setFromData(sd);
11815 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11817 field.setFromData(values);
11820 field.setValue(values[id]);
11824 if(this.trackResetOnLoad){
11825 field.originalValue = field.getValue();
11831 //Roo.each(this.childForms || [], function (f) {
11832 // f.setValues(values);
11839 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11840 * they are returned as an array.
11841 * @param {Boolean} asString
11844 getValues : function(asString){
11845 //if (this.childForms) {
11846 // copy values from the child forms
11847 // Roo.each(this.childForms, function (f) {
11848 // this.setValues(f.getValues());
11854 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11855 if(asString === true){
11858 return Roo.urlDecode(fs);
11862 * Returns the fields in this form as an object with key/value pairs.
11863 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11866 getFieldValues : function(with_hidden)
11868 var items = this.getItems();
11870 items.each(function(f){
11872 if (!f.getName()) {
11876 var v = f.getValue();
11878 if (f.inputType =='radio') {
11879 if (typeof(ret[f.getName()]) == 'undefined') {
11880 ret[f.getName()] = ''; // empty..
11883 if (!f.el.dom.checked) {
11887 v = f.el.dom.value;
11891 if(f.xtype == 'MoneyField'){
11892 ret[f.currencyName] = f.getCurrency();
11895 // not sure if this supported any more..
11896 if ((typeof(v) == 'object') && f.getRawValue) {
11897 v = f.getRawValue() ; // dates..
11899 // combo boxes where name != hiddenName...
11900 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11901 ret[f.name] = f.getRawValue();
11903 ret[f.getName()] = v;
11910 * Clears all invalid messages in this form.
11911 * @return {BasicForm} this
11913 clearInvalid : function(){
11914 var items = this.getItems();
11916 items.each(function(f){
11924 * Resets this form.
11925 * @return {BasicForm} this
11927 reset : function(){
11928 var items = this.getItems();
11929 items.each(function(f){
11933 Roo.each(this.childForms || [], function (f) {
11941 getItems : function()
11943 var r=new Roo.util.MixedCollection(false, function(o){
11944 return o.id || (o.id = Roo.id());
11946 var iter = function(el) {
11953 Roo.each(el.items,function(e) {
11962 hideFields : function(items)
11964 Roo.each(items, function(i){
11966 var f = this.findField(i);
11977 showFields : function(items)
11979 Roo.each(items, function(i){
11981 var f = this.findField(i);
11994 Roo.apply(Roo.bootstrap.form.Form, {
12010 intervalID : false,
12016 if(this.isApplied){
12021 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
12022 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
12023 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
12024 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
12027 this.maskEl.top.enableDisplayMode("block");
12028 this.maskEl.left.enableDisplayMode("block");
12029 this.maskEl.bottom.enableDisplayMode("block");
12030 this.maskEl.right.enableDisplayMode("block");
12032 this.toolTip = new Roo.bootstrap.Tooltip({
12033 cls : 'roo-form-error-popover',
12035 'left' : ['r-l', [-2,0], 'right'],
12036 'right' : ['l-r', [2,0], 'left'],
12037 'bottom' : ['tl-bl', [0,2], 'top'],
12038 'top' : [ 'bl-tl', [0,-2], 'bottom']
12042 this.toolTip.render(Roo.get(document.body));
12044 this.toolTip.el.enableDisplayMode("block");
12046 Roo.get(document.body).on('click', function(){
12050 Roo.get(document.body).on('touchstart', function(){
12054 this.isApplied = true
12057 mask : function(form, target)
12061 this.target = target;
12063 if(!this.form.errorMask || !target.el){
12067 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12069 Roo.log(scrollable);
12071 var ot = this.target.el.calcOffsetsTo(scrollable);
12073 var scrollTo = ot[1] - this.form.maskOffset;
12075 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12077 scrollable.scrollTo('top', scrollTo);
12079 var box = this.target.el.getBox();
12081 var zIndex = Roo.bootstrap.Modal.zIndex++;
12084 this.maskEl.top.setStyle('position', 'absolute');
12085 this.maskEl.top.setStyle('z-index', zIndex);
12086 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12087 this.maskEl.top.setLeft(0);
12088 this.maskEl.top.setTop(0);
12089 this.maskEl.top.show();
12091 this.maskEl.left.setStyle('position', 'absolute');
12092 this.maskEl.left.setStyle('z-index', zIndex);
12093 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12094 this.maskEl.left.setLeft(0);
12095 this.maskEl.left.setTop(box.y - this.padding);
12096 this.maskEl.left.show();
12098 this.maskEl.bottom.setStyle('position', 'absolute');
12099 this.maskEl.bottom.setStyle('z-index', zIndex);
12100 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12101 this.maskEl.bottom.setLeft(0);
12102 this.maskEl.bottom.setTop(box.bottom + this.padding);
12103 this.maskEl.bottom.show();
12105 this.maskEl.right.setStyle('position', 'absolute');
12106 this.maskEl.right.setStyle('z-index', zIndex);
12107 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12108 this.maskEl.right.setLeft(box.right + this.padding);
12109 this.maskEl.right.setTop(box.y - this.padding);
12110 this.maskEl.right.show();
12112 this.toolTip.bindEl = this.target.el;
12114 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12116 var tip = this.target.blankText;
12118 if(this.target.getValue() !== '' ) {
12120 if (this.target.invalidText.length) {
12121 tip = this.target.invalidText;
12122 } else if (this.target.regexText.length){
12123 tip = this.target.regexText;
12127 this.toolTip.show(tip);
12129 this.intervalID = window.setInterval(function() {
12130 Roo.bootstrap.form.Form.popover.unmask();
12133 window.onwheel = function(){ return false;};
12135 (function(){ this.isMasked = true; }).defer(500, this);
12139 unmask : function()
12141 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12145 this.maskEl.top.setStyle('position', 'absolute');
12146 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12147 this.maskEl.top.hide();
12149 this.maskEl.left.setStyle('position', 'absolute');
12150 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12151 this.maskEl.left.hide();
12153 this.maskEl.bottom.setStyle('position', 'absolute');
12154 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12155 this.maskEl.bottom.hide();
12157 this.maskEl.right.setStyle('position', 'absolute');
12158 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12159 this.maskEl.right.hide();
12161 this.toolTip.hide();
12163 this.toolTip.el.hide();
12165 window.onwheel = function(){ return true;};
12167 if(this.intervalID){
12168 window.clearInterval(this.intervalID);
12169 this.intervalID = false;
12172 this.isMasked = false;
12182 * Ext JS Library 1.1.1
12183 * Copyright(c) 2006-2007, Ext JS, LLC.
12185 * Originally Released Under LGPL - original licence link has changed is not relivant.
12188 * <script type="text/javascript">
12191 * @class Roo.form.VTypes
12192 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12195 Roo.form.VTypes = function(){
12196 // closure these in so they are only created once.
12197 var alpha = /^[a-zA-Z_]+$/;
12198 var alphanum = /^[a-zA-Z0-9_]+$/;
12199 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12200 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12202 // All these messages and functions are configurable
12205 * The function used to validate email addresses
12206 * @param {String} value The email address
12208 email : function(v){
12209 return email.test(v);
12212 * The error text to display when the email validation function returns false
12215 emailText : 'This field should be an e-mail address in the format "user@domain.com"',
12217 * The keystroke filter mask to be applied on email input
12220 emailMask : /[a-z0-9_\.\-@]/i,
12223 * The function used to validate URLs
12224 * @param {String} value The URL
12227 return url.test(v);
12230 * The error text to display when the url validation function returns false
12233 urlText : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12236 * The function used to validate alpha values
12237 * @param {String} value The value
12239 alpha : function(v){
12240 return alpha.test(v);
12243 * The error text to display when the alpha validation function returns false
12246 alphaText : 'This field should only contain letters and _',
12248 * The keystroke filter mask to be applied on alpha input
12251 alphaMask : /[a-z_]/i,
12254 * The function used to validate alphanumeric values
12255 * @param {String} value The value
12257 alphanum : function(v){
12258 return alphanum.test(v);
12261 * The error text to display when the alphanumeric validation function returns false
12264 alphanumText : 'This field should only contain letters, numbers and _',
12266 * The keystroke filter mask to be applied on alphanumeric input
12269 alphanumMask : /[a-z0-9_]/i
12279 * @class Roo.bootstrap.form.Input
12280 * @extends Roo.bootstrap.Component
12281 * Bootstrap Input class
12282 * @cfg {Boolean} disabled is it disabled
12283 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12284 * @cfg {String} name name of the input
12285 * @cfg {string} fieldLabel - the label associated
12286 * @cfg {string} placeholder - placeholder to put in text.
12287 * @cfg {string} before - input group add on before
12288 * @cfg {string} after - input group add on after
12289 * @cfg {string} size - (lg|sm) or leave empty..
12290 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12291 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12292 * @cfg {Number} md colspan out of 12 for computer-sized screens
12293 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12294 * @cfg {string} value default value of the input
12295 * @cfg {Number} labelWidth set the width of label
12296 * @cfg {Number} labellg set the width of label (1-12)
12297 * @cfg {Number} labelmd set the width of label (1-12)
12298 * @cfg {Number} labelsm set the width of label (1-12)
12299 * @cfg {Number} labelxs set the width of label (1-12)
12300 * @cfg {String} labelAlign (top|left)
12301 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12302 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12303 * @cfg {String} indicatorpos (left|right) default left
12304 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12305 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12306 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12307 * @cfg {Roo.bootstrap.Button} before Button to show before
12308 * @cfg {Roo.bootstrap.Button} afterButton to show before
12309 * @cfg {String} align (left|center|right) Default left
12310 * @cfg {Boolean} forceFeedback (true|false) Default false
12313 * Create a new Input
12314 * @param {Object} config The config object
12317 Roo.bootstrap.form.Input = function(config){
12319 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12324 * Fires when this field receives input focus.
12325 * @param {Roo.form.Field} this
12330 * Fires when this field loses input focus.
12331 * @param {Roo.form.Field} this
12335 * @event specialkey
12336 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12337 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12338 * @param {Roo.form.Field} this
12339 * @param {Roo.EventObject} e The event object
12344 * Fires just before the field blurs if the field value has changed.
12345 * @param {Roo.form.Field} this
12346 * @param {Mixed} newValue The new value
12347 * @param {Mixed} oldValue The original value
12352 * Fires after the field has been marked as invalid.
12353 * @param {Roo.form.Field} this
12354 * @param {String} msg The validation message
12359 * Fires after the field has been validated with no errors.
12360 * @param {Roo.form.Field} this
12365 * Fires after the key up
12366 * @param {Roo.form.Field} this
12367 * @param {Roo.EventObject} e The event Object
12372 * Fires after the user pastes into input
12373 * @param {Roo.form.Field} this
12374 * @param {Roo.EventObject} e The event Object
12380 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12382 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12383 automatic validation (defaults to "keyup").
12385 validationEvent : "keyup",
12387 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12389 validateOnBlur : true,
12391 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12393 validationDelay : 250,
12395 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12397 focusClass : "x-form-focus", // not needed???
12401 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12403 invalidClass : "has-warning",
12406 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12408 validClass : "has-success",
12411 * @cfg {Boolean} hasFeedback (true|false) default true
12413 hasFeedback : true,
12416 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12418 invalidFeedbackClass : "glyphicon-warning-sign",
12421 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12423 validFeedbackClass : "glyphicon-ok",
12426 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12428 selectOnFocus : false,
12431 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12435 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12440 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12442 disableKeyFilter : false,
12445 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12449 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12453 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12455 blankText : "Please complete this mandatory field",
12458 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12462 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12464 maxLength : Number.MAX_VALUE,
12466 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12468 minLengthText : "The minimum length for this field is {0}",
12470 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12472 maxLengthText : "The maximum length for this field is {0}",
12476 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12477 * If available, this function will be called only after the basic validators all return true, and will be passed the
12478 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12482 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12483 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12484 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12488 * @cfg {String} regexText -- Depricated - use Invalid Text
12493 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12499 autocomplete: false,
12503 inputType : 'text',
12506 placeholder: false,
12511 preventMark: false,
12512 isFormField : true,
12515 labelAlign : false,
12518 formatedValue : false,
12519 forceFeedback : false,
12521 indicatorpos : 'left',
12531 parentLabelAlign : function()
12534 while (parent.parent()) {
12535 parent = parent.parent();
12536 if (typeof(parent.labelAlign) !='undefined') {
12537 return parent.labelAlign;
12544 getAutoCreate : function()
12551 if(this.inputType != 'hidden'){
12552 cfg.cls = 'form-group' //input-group
12558 type : this.inputType,
12559 value : this.value,
12560 cls : 'form-control',
12561 placeholder : this.placeholder || '',
12562 autocomplete : this.autocomplete || 'new-password'
12564 if (this.inputType == 'file') {
12565 input.style = 'overflow:hidden'; // why not in CSS?
12568 if(this.capture.length){
12569 input.capture = this.capture;
12572 if(this.accept.length){
12573 input.accept = this.accept + "/*";
12577 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12580 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12581 input.maxLength = this.maxLength;
12584 if (this.disabled) {
12585 input.disabled=true;
12588 if (this.readOnly) {
12589 input.readonly=true;
12593 input.name = this.name;
12597 input.cls += ' input-' + this.size;
12601 ['xs','sm','md','lg'].map(function(size){
12602 if (settings[size]) {
12603 cfg.cls += ' col-' + size + '-' + settings[size];
12607 var inputblock = input;
12611 cls: 'glyphicon form-control-feedback'
12614 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12617 cls : 'has-feedback',
12625 if (this.before || this.after) {
12628 cls : 'input-group',
12632 if (this.before && typeof(this.before) == 'string') {
12634 inputblock.cn.push({
12636 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12640 if (this.before && typeof(this.before) == 'object') {
12641 this.before = Roo.factory(this.before);
12643 inputblock.cn.push({
12645 cls : 'roo-input-before input-group-prepend input-group-' +
12646 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12650 inputblock.cn.push(input);
12652 if (this.after && typeof(this.after) == 'string') {
12653 inputblock.cn.push({
12655 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12659 if (this.after && typeof(this.after) == 'object') {
12660 this.after = Roo.factory(this.after);
12662 inputblock.cn.push({
12664 cls : 'roo-input-after input-group-append input-group-' +
12665 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12669 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12670 inputblock.cls += ' has-feedback';
12671 inputblock.cn.push(feedback);
12677 cfg = this.getAutoCreateLabel( cfg, inputblock );
12682 if (this.parentType === 'Navbar' && this.parent().bar) {
12683 cfg.cls += ' navbar-form';
12686 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12687 // on BS4 we do this only if not form
12688 cfg.cls += ' navbar-form';
12696 * autocreate the label - also used by textara... ?? and others?
12698 getAutoCreateLabel : function( cfg, inputblock )
12700 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12704 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12705 tooltip : 'This field is required'
12707 if (this.allowBlank ) {
12708 indicator.style = this.allowBlank ? ' display:none' : '';
12710 if (align ==='left' && this.fieldLabel.length) {
12712 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12719 cls : 'control-label col-form-label',
12720 html : this.fieldLabel
12731 var labelCfg = cfg.cn[1];
12732 var contentCfg = cfg.cn[2];
12734 if(this.indicatorpos == 'right'){
12739 cls : 'control-label col-form-label',
12743 html : this.fieldLabel
12757 labelCfg = cfg.cn[0];
12758 contentCfg = cfg.cn[1];
12762 if(this.labelWidth > 12){
12763 labelCfg.style = "width: " + this.labelWidth + 'px';
12766 if(this.labelWidth < 13 && this.labelmd == 0){
12767 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12770 if(this.labellg > 0){
12771 labelCfg.cls += ' col-lg-' + this.labellg;
12772 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12775 if(this.labelmd > 0){
12776 labelCfg.cls += ' col-md-' + this.labelmd;
12777 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12780 if(this.labelsm > 0){
12781 labelCfg.cls += ' col-sm-' + this.labelsm;
12782 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12785 if(this.labelxs > 0){
12786 labelCfg.cls += ' col-xs-' + this.labelxs;
12787 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12791 } else if ( this.fieldLabel.length) {
12798 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12799 tooltip : 'This field is required',
12800 style : this.allowBlank ? ' display:none' : ''
12804 //cls : 'input-group-addon',
12805 html : this.fieldLabel
12813 if(this.indicatorpos == 'right'){
12818 //cls : 'input-group-addon',
12819 html : this.fieldLabel
12824 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12825 tooltip : 'This field is required',
12826 style : this.allowBlank ? ' display:none' : ''
12850 * return the real input element.
12852 inputEl: function ()
12854 return this.el.select('input.form-control',true).first();
12857 tooltipEl : function()
12859 return this.inputEl();
12862 indicatorEl : function()
12864 if (Roo.bootstrap.version == 4) {
12865 return false; // not enabled in v4 yet.
12868 var indicator = this.el.select('i.roo-required-indicator',true).first();
12878 setDisabled : function(v)
12880 var i = this.inputEl().dom;
12882 i.removeAttribute('disabled');
12886 i.setAttribute('disabled','true');
12888 initEvents : function()
12891 this.inputEl().on("keydown" , this.fireKey, this);
12892 this.inputEl().on("focus", this.onFocus, this);
12893 this.inputEl().on("blur", this.onBlur, this);
12895 this.inputEl().relayEvent('keyup', this);
12896 this.inputEl().relayEvent('paste', this);
12898 this.indicator = this.indicatorEl();
12900 if(this.indicator){
12901 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12904 // reference to original value for reset
12905 this.originalValue = this.getValue();
12906 //Roo.form.TextField.superclass.initEvents.call(this);
12907 if(this.validationEvent == 'keyup'){
12908 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12909 this.inputEl().on('keyup', this.filterValidation, this);
12911 else if(this.validationEvent !== false){
12912 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12915 if(this.selectOnFocus){
12916 this.on("focus", this.preFocus, this);
12919 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12920 this.inputEl().on("keypress", this.filterKeys, this);
12922 this.inputEl().relayEvent('keypress', this);
12925 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12926 this.el.on("click", this.autoSize, this);
12929 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12930 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12933 if (typeof(this.before) == 'object') {
12934 this.before.render(this.el.select('.roo-input-before',true).first());
12936 if (typeof(this.after) == 'object') {
12937 this.after.render(this.el.select('.roo-input-after',true).first());
12940 this.inputEl().on('change', this.onChange, this);
12943 filterValidation : function(e){
12944 if(!e.isNavKeyPress()){
12945 this.validationTask.delay(this.validationDelay);
12949 * Validates the field value
12950 * @return {Boolean} True if the value is valid, else false
12952 validate : function(){
12953 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12954 if(this.disabled || this.validateValue(this.getRawValue())){
12959 this.markInvalid();
12965 * Validates a value according to the field's validation rules and marks the field as invalid
12966 * if the validation fails
12967 * @param {Mixed} value The value to validate
12968 * @return {Boolean} True if the value is valid, else false
12970 validateValue : function(value)
12972 if(this.getVisibilityEl().hasClass('hidden')){
12976 if(value.length < 1) { // if it's blank
12977 if(this.allowBlank){
12983 if(value.length < this.minLength){
12986 if(value.length > this.maxLength){
12990 var vt = Roo.form.VTypes;
12991 if(!vt[this.vtype](value, this)){
12995 if(typeof this.validator == "function"){
12996 var msg = this.validator(value);
12997 if (typeof(msg) == 'string') {
12998 this.invalidText = msg;
13005 if(this.regex && !this.regex.test(value)){
13013 fireKey : function(e){
13014 //Roo.log('field ' + e.getKey());
13015 if(e.isNavKeyPress()){
13016 this.fireEvent("specialkey", this, e);
13019 focus : function (selectText){
13021 this.inputEl().focus();
13022 if(selectText === true){
13023 this.inputEl().dom.select();
13029 onFocus : function(){
13030 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13031 // this.el.addClass(this.focusClass);
13033 if(!this.hasFocus){
13034 this.hasFocus = true;
13035 this.startValue = this.getValue();
13036 this.fireEvent("focus", this);
13040 beforeBlur : Roo.emptyFn,
13044 onBlur : function(){
13046 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
13047 //this.el.removeClass(this.focusClass);
13049 this.hasFocus = false;
13050 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
13053 var v = this.getValue();
13054 if(String(v) !== String(this.startValue)){
13055 this.fireEvent('change', this, v, this.startValue);
13057 this.fireEvent("blur", this);
13060 onChange : function(e)
13062 var v = this.getValue();
13063 if(String(v) !== String(this.startValue)){
13064 this.fireEvent('change', this, v, this.startValue);
13070 * Resets the current field value to the originally loaded value and clears any validation messages
13072 reset : function(){
13073 this.setValue(this.originalValue);
13077 * Returns the name of the field
13078 * @return {Mixed} name The name field
13080 getName: function(){
13084 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13085 * @return {Mixed} value The field value
13087 getValue : function(){
13089 var v = this.inputEl().getValue();
13094 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13095 * @return {Mixed} value The field value
13097 getRawValue : function(){
13098 var v = this.inputEl().getValue();
13104 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13105 * @param {Mixed} value The value to set
13107 setRawValue : function(v){
13108 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13111 selectText : function(start, end){
13112 var v = this.getRawValue();
13114 start = start === undefined ? 0 : start;
13115 end = end === undefined ? v.length : end;
13116 var d = this.inputEl().dom;
13117 if(d.setSelectionRange){
13118 d.setSelectionRange(start, end);
13119 }else if(d.createTextRange){
13120 var range = d.createTextRange();
13121 range.moveStart("character", start);
13122 range.moveEnd("character", v.length-end);
13129 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13130 * @param {Mixed} value The value to set
13132 setValue : function(v){
13135 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13141 processValue : function(value){
13142 if(this.stripCharsRe){
13143 var newValue = value.replace(this.stripCharsRe, '');
13144 if(newValue !== value){
13145 this.setRawValue(newValue);
13152 preFocus : function(){
13154 if(this.selectOnFocus){
13155 this.inputEl().dom.select();
13158 filterKeys : function(e){
13159 var k = e.getKey();
13160 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13163 var c = e.getCharCode(), cc = String.fromCharCode(c);
13164 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13167 if(!this.maskRe.test(cc)){
13172 * Clear any invalid styles/messages for this field
13174 clearInvalid : function(){
13176 if(!this.el || this.preventMark){ // not rendered
13181 this.el.removeClass([this.invalidClass, 'is-invalid']);
13183 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13185 var feedback = this.el.select('.form-control-feedback', true).first();
13188 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13193 if(this.indicator){
13194 this.indicator.removeClass('visible');
13195 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13198 this.fireEvent('valid', this);
13202 * Mark this field as valid
13204 markValid : function()
13206 if(!this.el || this.preventMark){ // not rendered...
13210 this.el.removeClass([this.invalidClass, this.validClass]);
13211 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13213 var feedback = this.el.select('.form-control-feedback', true).first();
13216 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13219 if(this.indicator){
13220 this.indicator.removeClass('visible');
13221 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13229 if(this.allowBlank && !this.getRawValue().length){
13232 if (Roo.bootstrap.version == 3) {
13233 this.el.addClass(this.validClass);
13235 this.inputEl().addClass('is-valid');
13238 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13240 var feedback = this.el.select('.form-control-feedback', true).first();
13243 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13244 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13249 this.fireEvent('valid', this);
13253 * Mark this field as invalid
13254 * @param {String} msg The validation message
13256 markInvalid : function(msg)
13258 if(!this.el || this.preventMark){ // not rendered
13262 this.el.removeClass([this.invalidClass, this.validClass]);
13263 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13265 var feedback = this.el.select('.form-control-feedback', true).first();
13268 this.el.select('.form-control-feedback', true).first().removeClass(
13269 [this.invalidFeedbackClass, this.validFeedbackClass]);
13276 if(this.allowBlank && !this.getRawValue().length){
13280 if(this.indicator){
13281 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13282 this.indicator.addClass('visible');
13284 if (Roo.bootstrap.version == 3) {
13285 this.el.addClass(this.invalidClass);
13287 this.inputEl().addClass('is-invalid');
13292 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13294 var feedback = this.el.select('.form-control-feedback', true).first();
13297 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13299 if(this.getValue().length || this.forceFeedback){
13300 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13307 this.fireEvent('invalid', this, msg);
13310 SafariOnKeyDown : function(event)
13312 // this is a workaround for a password hang bug on chrome/ webkit.
13313 if (this.inputEl().dom.type != 'password') {
13317 var isSelectAll = false;
13319 if(this.inputEl().dom.selectionEnd > 0){
13320 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13322 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13323 event.preventDefault();
13328 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13330 event.preventDefault();
13331 // this is very hacky as keydown always get's upper case.
13333 var cc = String.fromCharCode(event.getCharCode());
13334 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13338 adjustWidth : function(tag, w){
13339 tag = tag.toLowerCase();
13340 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13341 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13342 if(tag == 'input'){
13345 if(tag == 'textarea'){
13348 }else if(Roo.isOpera){
13349 if(tag == 'input'){
13352 if(tag == 'textarea'){
13360 setFieldLabel : function(v)
13362 if(!this.rendered){
13366 if(this.indicatorEl()){
13367 var ar = this.el.select('label > span',true);
13369 if (ar.elements.length) {
13370 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13371 this.fieldLabel = v;
13375 var br = this.el.select('label',true);
13377 if(br.elements.length) {
13378 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13379 this.fieldLabel = v;
13383 Roo.log('Cannot Found any of label > span || label in input');
13387 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13388 this.fieldLabel = v;
13403 * @class Roo.bootstrap.form.TextArea
13404 * @extends Roo.bootstrap.form.Input
13405 * Bootstrap TextArea class
13406 * @cfg {Number} cols Specifies the visible width of a text area
13407 * @cfg {Number} rows Specifies the visible number of lines in a text area
13408 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13409 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13410 * @cfg {string} html text
13413 * Create a new TextArea
13414 * @param {Object} config The config object
13417 Roo.bootstrap.form.TextArea = function(config){
13418 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13422 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13432 getAutoCreate : function(){
13434 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13440 if(this.inputType != 'hidden'){
13441 cfg.cls = 'form-group' //input-group
13449 value : this.value || '',
13450 html: this.html || '',
13451 cls : 'form-control',
13452 placeholder : this.placeholder || ''
13456 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13457 input.maxLength = this.maxLength;
13461 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13465 input.cols = this.cols;
13468 if (this.readOnly) {
13469 input.readonly = true;
13473 input.name = this.name;
13477 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13481 ['xs','sm','md','lg'].map(function(size){
13482 if (settings[size]) {
13483 cfg.cls += ' col-' + size + '-' + settings[size];
13487 var inputblock = input;
13489 if(this.hasFeedback && !this.allowBlank){
13493 cls: 'glyphicon form-control-feedback'
13497 cls : 'has-feedback',
13506 if (this.before || this.after) {
13509 cls : 'input-group',
13513 inputblock.cn.push({
13515 cls : 'input-group-addon',
13520 inputblock.cn.push(input);
13522 if(this.hasFeedback && !this.allowBlank){
13523 inputblock.cls += ' has-feedback';
13524 inputblock.cn.push(feedback);
13528 inputblock.cn.push({
13530 cls : 'input-group-addon',
13538 cfg = this.getAutoCreateLabel( cfg, inputblock );
13542 if (this.disabled) {
13543 input.disabled=true;
13550 * return the real textarea element.
13552 inputEl: function ()
13554 return this.el.select('textarea.form-control',true).first();
13558 * Clear any invalid styles/messages for this field
13560 clearInvalid : function()
13563 if(!this.el || this.preventMark){ // not rendered
13567 var label = this.el.select('label', true).first();
13568 //var icon = this.el.select('i.fa-star', true).first();
13570 //if(label && icon){
13573 this.el.removeClass( this.validClass);
13574 this.inputEl().removeClass('is-invalid');
13576 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13578 var feedback = this.el.select('.form-control-feedback', true).first();
13581 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13586 this.fireEvent('valid', this);
13590 * Mark this field as valid
13592 markValid : function()
13594 if(!this.el || this.preventMark){ // not rendered
13598 this.el.removeClass([this.invalidClass, this.validClass]);
13599 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13601 var feedback = this.el.select('.form-control-feedback', true).first();
13604 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13607 if(this.disabled || this.allowBlank){
13611 var label = this.el.select('label', true).first();
13612 var icon = this.el.select('i.fa-star', true).first();
13614 //if(label && icon){
13617 if (Roo.bootstrap.version == 3) {
13618 this.el.addClass(this.validClass);
13620 this.inputEl().addClass('is-valid');
13624 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13626 var feedback = this.el.select('.form-control-feedback', true).first();
13629 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13630 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13635 this.fireEvent('valid', this);
13639 * Mark this field as invalid
13640 * @param {String} msg The validation message
13642 markInvalid : function(msg)
13644 if(!this.el || this.preventMark){ // not rendered
13648 this.el.removeClass([this.invalidClass, this.validClass]);
13649 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13651 var feedback = this.el.select('.form-control-feedback', true).first();
13654 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13657 if(this.disabled || this.allowBlank){
13661 var label = this.el.select('label', true).first();
13662 //var icon = this.el.select('i.fa-star', true).first();
13664 //if(!this.getValue().length && label && !icon){
13665 /* this.el.createChild({
13667 cls : 'text-danger fa fa-lg fa-star',
13668 tooltip : 'This field is required',
13669 style : 'margin-right:5px;'
13674 if (Roo.bootstrap.version == 3) {
13675 this.el.addClass(this.invalidClass);
13677 this.inputEl().addClass('is-invalid');
13680 // fixme ... this may be depricated need to test..
13681 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13683 var feedback = this.el.select('.form-control-feedback', true).first();
13686 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13688 if(this.getValue().length || this.forceFeedback){
13689 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13696 this.fireEvent('invalid', this, msg);
13704 * trigger field - base class for combo..
13709 * @class Roo.bootstrap.form.TriggerField
13710 * @extends Roo.bootstrap.form.Input
13711 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13712 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13713 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13714 * for which you can provide a custom implementation. For example:
13716 var trigger = new Roo.bootstrap.form.TriggerField();
13717 trigger.onTriggerClick = myTriggerFn;
13718 trigger.applyTo('my-field');
13721 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13722 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13723 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13724 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13725 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13728 * Create a new TriggerField.
13729 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13730 * to the base TextField)
13732 Roo.bootstrap.form.TriggerField = function(config){
13733 this.mimicing = false;
13734 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13737 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13739 * @cfg {String} triggerClass A CSS class to apply to the trigger
13742 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13747 * @cfg {Boolean} removable (true|false) special filter default false
13751 /** @cfg {Boolean} grow @hide */
13752 /** @cfg {Number} growMin @hide */
13753 /** @cfg {Number} growMax @hide */
13759 autoSize: Roo.emptyFn,
13763 deferHeight : true,
13766 actionMode : 'wrap',
13771 getAutoCreate : function(){
13773 var align = this.labelAlign || this.parentLabelAlign();
13778 cls: 'form-group' //input-group
13785 type : this.inputType,
13786 cls : 'form-control',
13787 autocomplete: 'new-password',
13788 placeholder : this.placeholder || ''
13792 input.name = this.name;
13795 input.cls += ' input-' + this.size;
13798 if (this.disabled) {
13799 input.disabled=true;
13802 var inputblock = input;
13804 if(this.hasFeedback && !this.allowBlank){
13808 cls: 'glyphicon form-control-feedback'
13811 if(this.removable && !this.editable ){
13813 cls : 'has-feedback',
13819 cls : 'roo-combo-removable-btn close'
13826 cls : 'has-feedback',
13835 if(this.removable && !this.editable ){
13837 cls : 'roo-removable',
13843 cls : 'roo-combo-removable-btn close'
13850 if (this.before || this.after) {
13853 cls : 'input-group',
13857 inputblock.cn.push({
13859 cls : 'input-group-addon input-group-prepend input-group-text',
13864 inputblock.cn.push(input);
13866 if(this.hasFeedback && !this.allowBlank){
13867 inputblock.cls += ' has-feedback';
13868 inputblock.cn.push(feedback);
13872 inputblock.cn.push({
13874 cls : 'input-group-addon input-group-append input-group-text',
13883 var ibwrap = inputblock;
13888 cls: 'roo-select2-choices',
13892 cls: 'roo-select2-search-field',
13904 cls: 'roo-select2-container input-group',
13909 cls: 'form-hidden-field'
13915 if(!this.multiple && this.showToggleBtn){
13921 if (this.caret != false) {
13924 cls: 'fa fa-' + this.caret
13931 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13933 Roo.bootstrap.version == 3 ? caret : '',
13936 cls: 'combobox-clear',
13950 combobox.cls += ' roo-select2-container-multi';
13954 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13955 tooltip : 'This field is required'
13958 if (this.allowBlank) {
13961 style : 'display:none'
13967 if (align ==='left' && this.fieldLabel.length) {
13969 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13976 cls : 'control-label',
13977 html : this.fieldLabel
13989 var labelCfg = cfg.cn[1];
13990 var contentCfg = cfg.cn[2];
13992 if(this.indicatorpos == 'right'){
13997 cls : 'control-label',
14001 html : this.fieldLabel
14015 labelCfg = cfg.cn[0];
14016 contentCfg = cfg.cn[1];
14019 if(this.labelWidth > 12){
14020 labelCfg.style = "width: " + this.labelWidth + 'px';
14023 if(this.labelWidth < 13 && this.labelmd == 0){
14024 this.labelmd = this.labelWidth;
14027 if(this.labellg > 0){
14028 labelCfg.cls += ' col-lg-' + this.labellg;
14029 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14032 if(this.labelmd > 0){
14033 labelCfg.cls += ' col-md-' + this.labelmd;
14034 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14037 if(this.labelsm > 0){
14038 labelCfg.cls += ' col-sm-' + this.labelsm;
14039 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14042 if(this.labelxs > 0){
14043 labelCfg.cls += ' col-xs-' + this.labelxs;
14044 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14047 } else if ( this.fieldLabel.length) {
14048 // Roo.log(" label");
14053 //cls : 'input-group-addon',
14054 html : this.fieldLabel
14062 if(this.indicatorpos == 'right'){
14070 html : this.fieldLabel
14084 // Roo.log(" no label && no align");
14091 ['xs','sm','md','lg'].map(function(size){
14092 if (settings[size]) {
14093 cfg.cls += ' col-' + size + '-' + settings[size];
14104 onResize : function(w, h){
14105 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14106 // if(typeof w == 'number'){
14107 // var x = w - this.trigger.getWidth();
14108 // this.inputEl().setWidth(this.adjustWidth('input', x));
14109 // this.trigger.setStyle('left', x+'px');
14114 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14117 getResizeEl : function(){
14118 return this.inputEl();
14122 getPositionEl : function(){
14123 return this.inputEl();
14127 alignErrorIcon : function(){
14128 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14132 initEvents : function(){
14136 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14137 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14138 if(!this.multiple && this.showToggleBtn){
14139 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14140 if(this.hideTrigger){
14141 this.trigger.setDisplayed(false);
14143 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14147 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14150 if(this.removable && !this.editable && !this.tickable){
14151 var close = this.closeTriggerEl();
14154 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14155 close.on('click', this.removeBtnClick, this, close);
14159 //this.trigger.addClassOnOver('x-form-trigger-over');
14160 //this.trigger.addClassOnClick('x-form-trigger-click');
14163 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14167 closeTriggerEl : function()
14169 var close = this.el.select('.roo-combo-removable-btn', true).first();
14170 return close ? close : false;
14173 removeBtnClick : function(e, h, el)
14175 e.preventDefault();
14177 if(this.fireEvent("remove", this) !== false){
14179 this.fireEvent("afterremove", this)
14183 createList : function()
14185 this.list = Roo.get(document.body).createChild({
14186 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14187 cls: 'typeahead typeahead-long dropdown-menu shadow',
14188 style: 'display:none'
14191 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14196 initTrigger : function(){
14201 onDestroy : function(){
14203 this.trigger.removeAllListeners();
14204 // this.trigger.remove();
14207 // this.wrap.remove();
14209 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14213 onFocus : function(){
14214 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14216 if(!this.mimicing){
14217 this.wrap.addClass('x-trigger-wrap-focus');
14218 this.mimicing = true;
14219 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14220 if(this.monitorTab){
14221 this.el.on("keydown", this.checkTab, this);
14228 checkTab : function(e){
14229 if(e.getKey() == e.TAB){
14230 this.triggerBlur();
14235 onBlur : function(){
14240 mimicBlur : function(e, t){
14242 if(!this.wrap.contains(t) && this.validateBlur()){
14243 this.triggerBlur();
14249 triggerBlur : function(){
14250 this.mimicing = false;
14251 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14252 if(this.monitorTab){
14253 this.el.un("keydown", this.checkTab, this);
14255 //this.wrap.removeClass('x-trigger-wrap-focus');
14256 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14260 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14261 validateBlur : function(e, t){
14266 onDisable : function(){
14267 this.inputEl().dom.disabled = true;
14268 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14270 // this.wrap.addClass('x-item-disabled');
14275 onEnable : function(){
14276 this.inputEl().dom.disabled = false;
14277 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14279 // this.el.removeClass('x-item-disabled');
14284 onShow : function(){
14285 var ae = this.getActionEl();
14288 ae.dom.style.display = '';
14289 ae.dom.style.visibility = 'visible';
14295 onHide : function(){
14296 var ae = this.getActionEl();
14297 ae.dom.style.display = 'none';
14301 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14302 * by an implementing function.
14304 * @param {EventObject} e
14306 onTriggerClick : Roo.emptyFn
14314 * @class Roo.bootstrap.form.CardUploader
14315 * @extends Roo.bootstrap.Button
14316 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14317 * @cfg {Number} errorTimeout default 3000
14318 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14319 * @cfg {Array} html The button text.
14323 * Create a new CardUploader
14324 * @param {Object} config The config object
14327 Roo.bootstrap.form.CardUploader = function(config){
14331 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14334 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14342 * When a image is clicked on - and needs to display a slideshow or similar..
14343 * @param {Roo.bootstrap.Card} this
14344 * @param {Object} The image information data
14350 * When a the download link is clicked
14351 * @param {Roo.bootstrap.Card} this
14352 * @param {Object} The image information data contains
14359 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14362 errorTimeout : 3000,
14366 fileCollection : false,
14369 getAutoCreate : function()
14373 cls :'form-group' ,
14378 //cls : 'input-group-addon',
14379 html : this.fieldLabel
14387 value : this.value,
14388 cls : 'd-none form-control'
14393 multiple : 'multiple',
14395 cls : 'd-none roo-card-upload-selector'
14399 cls : 'roo-card-uploader-button-container w-100 mb-2'
14402 cls : 'card-columns roo-card-uploader-container'
14412 getChildContainer : function() /// what children are added to.
14414 return this.containerEl;
14417 getButtonContainer : function() /// what children are added to.
14419 return this.el.select(".roo-card-uploader-button-container").first();
14422 initEvents : function()
14425 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14429 xns: Roo.bootstrap,
14432 container_method : 'getButtonContainer' ,
14433 html : this.html, // fix changable?
14436 'click' : function(btn, e) {
14445 this.urlAPI = (window.createObjectURL && window) ||
14446 (window.URL && URL.revokeObjectURL && URL) ||
14447 (window.webkitURL && webkitURL);
14452 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14454 this.selectorEl.on('change', this.onFileSelected, this);
14457 this.images.forEach(function(img) {
14460 this.images = false;
14462 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14468 onClick : function(e)
14470 e.preventDefault();
14472 this.selectorEl.dom.click();
14476 onFileSelected : function(e)
14478 e.preventDefault();
14480 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14484 Roo.each(this.selectorEl.dom.files, function(file){
14485 this.addFile(file);
14494 addFile : function(file)
14497 if(typeof(file) === 'string'){
14498 throw "Add file by name?"; // should not happen
14502 if(!file || !this.urlAPI){
14512 var url = _this.urlAPI.createObjectURL( file);
14515 id : Roo.bootstrap.form.CardUploader.ID--,
14516 is_uploaded : false,
14520 mimetype : file.type,
14528 * addCard - add an Attachment to the uploader
14529 * @param data - the data about the image to upload
14533 title : "Title of file",
14534 is_uploaded : false,
14535 src : "http://.....",
14536 srcfile : { the File upload object },
14537 mimetype : file.type,
14540 .. any other data...
14546 addCard : function (data)
14548 // hidden input element?
14549 // if the file is not an image...
14550 //then we need to use something other that and header_image
14555 xns : Roo.bootstrap,
14556 xtype : 'CardFooter',
14559 xns : Roo.bootstrap,
14565 xns : Roo.bootstrap,
14567 html : String.format("<small>{0}</small>", data.title),
14568 cls : 'col-10 text-left',
14573 click : function() {
14575 t.fireEvent( "download", t, data );
14581 xns : Roo.bootstrap,
14583 style: 'max-height: 28px; ',
14589 click : function() {
14590 t.removeCard(data.id)
14602 var cn = this.addxtype(
14605 xns : Roo.bootstrap,
14608 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14609 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14610 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14615 initEvents : function() {
14616 Roo.bootstrap.Card.prototype.initEvents.call(this);
14618 this.imgEl = this.el.select('.card-img-top').first();
14620 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14621 this.imgEl.set({ 'pointer' : 'cursor' });
14624 this.getCardFooter().addClass('p-1');
14631 // dont' really need ot update items.
14632 // this.items.push(cn);
14633 this.fileCollection.add(cn);
14635 if (!data.srcfile) {
14636 this.updateInput();
14641 var reader = new FileReader();
14642 reader.addEventListener("load", function() {
14643 data.srcdata = reader.result;
14646 reader.readAsDataURL(data.srcfile);
14651 removeCard : function(id)
14654 var card = this.fileCollection.get(id);
14655 card.data.is_deleted = 1;
14656 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14657 //this.fileCollection.remove(card);
14658 //this.items = this.items.filter(function(e) { return e != card });
14659 // dont' really need ot update items.
14660 card.el.dom.parentNode.removeChild(card.el.dom);
14661 this.updateInput();
14667 this.fileCollection.each(function(card) {
14668 if (card.el.dom && card.el.dom.parentNode) {
14669 card.el.dom.parentNode.removeChild(card.el.dom);
14672 this.fileCollection.clear();
14673 this.updateInput();
14676 updateInput : function()
14679 this.fileCollection.each(function(e) {
14683 this.inputEl().dom.value = JSON.stringify(data);
14693 Roo.bootstrap.form.CardUploader.ID = -1;/*
14695 * Ext JS Library 1.1.1
14696 * Copyright(c) 2006-2007, Ext JS, LLC.
14698 * Originally Released Under LGPL - original licence link has changed is not relivant.
14701 * <script type="text/javascript">
14706 * @class Roo.data.SortTypes
14708 * Defines the default sorting (casting?) comparison functions used when sorting data.
14710 Roo.data.SortTypes = {
14712 * Default sort that does nothing
14713 * @param {Mixed} s The value being converted
14714 * @return {Mixed} The comparison value
14716 none : function(s){
14721 * The regular expression used to strip tags
14725 stripTagsRE : /<\/?[^>]+>/gi,
14728 * Strips all HTML tags to sort on text only
14729 * @param {Mixed} s The value being converted
14730 * @return {String} The comparison value
14732 asText : function(s){
14733 return String(s).replace(this.stripTagsRE, "");
14737 * Strips all HTML tags to sort on text only - Case insensitive
14738 * @param {Mixed} s The value being converted
14739 * @return {String} The comparison value
14741 asUCText : function(s){
14742 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14746 * Case insensitive string
14747 * @param {Mixed} s The value being converted
14748 * @return {String} The comparison value
14750 asUCString : function(s) {
14751 return String(s).toUpperCase();
14756 * @param {Mixed} s The value being converted
14757 * @return {Number} The comparison value
14759 asDate : function(s) {
14763 if(s instanceof Date){
14764 return s.getTime();
14766 return Date.parse(String(s));
14771 * @param {Mixed} s The value being converted
14772 * @return {Float} The comparison value
14774 asFloat : function(s) {
14775 var val = parseFloat(String(s).replace(/,/g, ""));
14784 * @param {Mixed} s The value being converted
14785 * @return {Number} The comparison value
14787 asInt : function(s) {
14788 var val = parseInt(String(s).replace(/,/g, ""));
14796 * Ext JS Library 1.1.1
14797 * Copyright(c) 2006-2007, Ext JS, LLC.
14799 * Originally Released Under LGPL - original licence link has changed is not relivant.
14802 * <script type="text/javascript">
14806 * @class Roo.data.Record
14807 * Instances of this class encapsulate both record <em>definition</em> information, and record
14808 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14809 * to access Records cached in an {@link Roo.data.Store} object.<br>
14811 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14812 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14815 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14817 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14818 * {@link #create}. The parameters are the same.
14819 * @param {Array} data An associative Array of data values keyed by the field name.
14820 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14821 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14822 * not specified an integer id is generated.
14824 Roo.data.Record = function(data, id){
14825 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14830 * Generate a constructor for a specific record layout.
14831 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14832 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14833 * Each field definition object may contain the following properties: <ul>
14834 * <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,
14835 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14836 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14837 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14838 * is being used, then this is a string containing the javascript expression to reference the data relative to
14839 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14840 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14841 * this may be omitted.</p></li>
14842 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14843 * <ul><li>auto (Default, implies no conversion)</li>
14848 * <li>date</li></ul></p></li>
14849 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14850 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14851 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14852 * by the Reader into an object that will be stored in the Record. It is passed the
14853 * following parameters:<ul>
14854 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14856 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14858 * <br>usage:<br><pre><code>
14859 var TopicRecord = Roo.data.Record.create(
14860 {name: 'title', mapping: 'topic_title'},
14861 {name: 'author', mapping: 'username'},
14862 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14863 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14864 {name: 'lastPoster', mapping: 'user2'},
14865 {name: 'excerpt', mapping: 'post_text'}
14868 var myNewRecord = new TopicRecord({
14869 title: 'Do my job please',
14872 lastPost: new Date(),
14873 lastPoster: 'Animal',
14874 excerpt: 'No way dude!'
14876 myStore.add(myNewRecord);
14881 Roo.data.Record.create = function(o){
14882 var f = function(){
14883 f.superclass.constructor.apply(this, arguments);
14885 Roo.extend(f, Roo.data.Record);
14886 var p = f.prototype;
14887 p.fields = new Roo.util.MixedCollection(false, function(field){
14890 for(var i = 0, len = o.length; i < len; i++){
14891 p.fields.add(new Roo.data.Field(o[i]));
14893 f.getField = function(name){
14894 return p.fields.get(name);
14899 Roo.data.Record.AUTO_ID = 1000;
14900 Roo.data.Record.EDIT = 'edit';
14901 Roo.data.Record.REJECT = 'reject';
14902 Roo.data.Record.COMMIT = 'commit';
14904 Roo.data.Record.prototype = {
14906 * Readonly flag - true if this record has been modified.
14915 join : function(store){
14916 this.store = store;
14920 * Set the named field to the specified value.
14921 * @param {String} name The name of the field to set.
14922 * @param {Object} value The value to set the field to.
14924 set : function(name, value){
14925 if(this.data[name] == value){
14929 if(!this.modified){
14930 this.modified = {};
14932 if(typeof this.modified[name] == 'undefined'){
14933 this.modified[name] = this.data[name];
14935 this.data[name] = value;
14936 if(!this.editing && this.store){
14937 this.store.afterEdit(this);
14942 * Get the value of the named field.
14943 * @param {String} name The name of the field to get the value of.
14944 * @return {Object} The value of the field.
14946 get : function(name){
14947 return this.data[name];
14951 beginEdit : function(){
14952 this.editing = true;
14953 this.modified = {};
14957 cancelEdit : function(){
14958 this.editing = false;
14959 delete this.modified;
14963 endEdit : function(){
14964 this.editing = false;
14965 if(this.dirty && this.store){
14966 this.store.afterEdit(this);
14971 * Usually called by the {@link Roo.data.Store} which owns the Record.
14972 * Rejects all changes made to the Record since either creation, or the last commit operation.
14973 * Modified fields are reverted to their original values.
14975 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14976 * of reject operations.
14978 reject : function(){
14979 var m = this.modified;
14981 if(typeof m[n] != "function"){
14982 this.data[n] = m[n];
14985 this.dirty = false;
14986 delete this.modified;
14987 this.editing = false;
14989 this.store.afterReject(this);
14994 * Usually called by the {@link Roo.data.Store} which owns the Record.
14995 * Commits all changes made to the Record since either creation, or the last commit operation.
14997 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14998 * of commit operations.
15000 commit : function(){
15001 this.dirty = false;
15002 delete this.modified;
15003 this.editing = false;
15005 this.store.afterCommit(this);
15010 hasError : function(){
15011 return this.error != null;
15015 clearError : function(){
15020 * Creates a copy of this record.
15021 * @param {String} id (optional) A new record id if you don't want to use this record's id
15024 copy : function(newId) {
15025 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15029 * Ext JS Library 1.1.1
15030 * Copyright(c) 2006-2007, Ext JS, LLC.
15032 * Originally Released Under LGPL - original licence link has changed is not relivant.
15035 * <script type="text/javascript">
15041 * @class Roo.data.Store
15042 * @extends Roo.util.Observable
15043 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15044 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15046 * 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
15047 * has no knowledge of the format of the data returned by the Proxy.<br>
15049 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15050 * instances from the data object. These records are cached and made available through accessor functions.
15052 * Creates a new Store.
15053 * @param {Object} config A config object containing the objects needed for the Store to access data,
15054 * and read the data into Records.
15056 Roo.data.Store = function(config){
15057 this.data = new Roo.util.MixedCollection(false);
15058 this.data.getKey = function(o){
15061 this.baseParams = {};
15063 this.paramNames = {
15068 "multisort" : "_multisort"
15071 if(config && config.data){
15072 this.inlineData = config.data;
15073 delete config.data;
15076 Roo.apply(this, config);
15078 if(this.reader){ // reader passed
15079 this.reader = Roo.factory(this.reader, Roo.data);
15080 this.reader.xmodule = this.xmodule || false;
15081 if(!this.recordType){
15082 this.recordType = this.reader.recordType;
15084 if(this.reader.onMetaChange){
15085 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15089 if(this.recordType){
15090 this.fields = this.recordType.prototype.fields;
15092 this.modified = [];
15096 * @event datachanged
15097 * Fires when the data cache has changed, and a widget which is using this Store
15098 * as a Record cache should refresh its view.
15099 * @param {Store} this
15101 datachanged : true,
15103 * @event metachange
15104 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15105 * @param {Store} this
15106 * @param {Object} meta The JSON metadata
15111 * Fires when Records have been added to the Store
15112 * @param {Store} this
15113 * @param {Roo.data.Record[]} records The array of Records added
15114 * @param {Number} index The index at which the record(s) were added
15119 * Fires when a Record has been removed from the Store
15120 * @param {Store} this
15121 * @param {Roo.data.Record} record The Record that was removed
15122 * @param {Number} index The index at which the record was removed
15127 * Fires when a Record has been updated
15128 * @param {Store} this
15129 * @param {Roo.data.Record} record The Record that was updated
15130 * @param {String} operation The update operation being performed. Value may be one of:
15132 Roo.data.Record.EDIT
15133 Roo.data.Record.REJECT
15134 Roo.data.Record.COMMIT
15140 * Fires when the data cache has been cleared.
15141 * @param {Store} this
15145 * @event beforeload
15146 * Fires before a request is made for a new data object. If the beforeload handler returns false
15147 * the load action will be canceled.
15148 * @param {Store} this
15149 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15153 * @event beforeloadadd
15154 * Fires after a new set of Records has been loaded.
15155 * @param {Store} this
15156 * @param {Roo.data.Record[]} records The Records that were loaded
15157 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15159 beforeloadadd : true,
15162 * Fires after a new set of Records has been loaded, before they are added to the store.
15163 * @param {Store} this
15164 * @param {Roo.data.Record[]} records The Records that were loaded
15165 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15166 * @params {Object} return from reader
15170 * @event loadexception
15171 * Fires if an exception occurs in the Proxy during loading.
15172 * Called with the signature of the Proxy's "loadexception" event.
15173 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15176 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15177 * @param {Object} load options
15178 * @param {Object} jsonData from your request (normally this contains the Exception)
15180 loadexception : true
15184 this.proxy = Roo.factory(this.proxy, Roo.data);
15185 this.proxy.xmodule = this.xmodule || false;
15186 this.relayEvents(this.proxy, ["loadexception"]);
15188 this.sortToggle = {};
15189 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15191 Roo.data.Store.superclass.constructor.call(this);
15193 if(this.inlineData){
15194 this.loadData(this.inlineData);
15195 delete this.inlineData;
15199 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15201 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15202 * without a remote query - used by combo/forms at present.
15206 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15209 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15212 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15213 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15216 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15217 * on any HTTP request
15220 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15223 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15227 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15228 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15230 remoteSort : false,
15233 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15234 * loaded or when a record is removed. (defaults to false).
15236 pruneModifiedRecords : false,
15239 lastOptions : null,
15242 * Add Records to the Store and fires the add event.
15243 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15245 add : function(records){
15246 records = [].concat(records);
15247 for(var i = 0, len = records.length; i < len; i++){
15248 records[i].join(this);
15250 var index = this.data.length;
15251 this.data.addAll(records);
15252 this.fireEvent("add", this, records, index);
15256 * Remove a Record from the Store and fires the remove event.
15257 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15259 remove : function(record){
15260 var index = this.data.indexOf(record);
15261 this.data.removeAt(index);
15263 if(this.pruneModifiedRecords){
15264 this.modified.remove(record);
15266 this.fireEvent("remove", this, record, index);
15270 * Remove all Records from the Store and fires the clear event.
15272 removeAll : function(){
15274 if(this.pruneModifiedRecords){
15275 this.modified = [];
15277 this.fireEvent("clear", this);
15281 * Inserts Records to the Store at the given index and fires the add event.
15282 * @param {Number} index The start index at which to insert the passed Records.
15283 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15285 insert : function(index, records){
15286 records = [].concat(records);
15287 for(var i = 0, len = records.length; i < len; i++){
15288 this.data.insert(index, records[i]);
15289 records[i].join(this);
15291 this.fireEvent("add", this, records, index);
15295 * Get the index within the cache of the passed Record.
15296 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15297 * @return {Number} The index of the passed Record. Returns -1 if not found.
15299 indexOf : function(record){
15300 return this.data.indexOf(record);
15304 * Get the index within the cache of the Record with the passed id.
15305 * @param {String} id The id of the Record to find.
15306 * @return {Number} The index of the Record. Returns -1 if not found.
15308 indexOfId : function(id){
15309 return this.data.indexOfKey(id);
15313 * Get the Record with the specified id.
15314 * @param {String} id The id of the Record to find.
15315 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15317 getById : function(id){
15318 return this.data.key(id);
15322 * Get the Record at the specified index.
15323 * @param {Number} index The index of the Record to find.
15324 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15326 getAt : function(index){
15327 return this.data.itemAt(index);
15331 * Returns a range of Records between specified indices.
15332 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15333 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15334 * @return {Roo.data.Record[]} An array of Records
15336 getRange : function(start, end){
15337 return this.data.getRange(start, end);
15341 storeOptions : function(o){
15342 o = Roo.apply({}, o);
15345 this.lastOptions = o;
15349 * Loads the Record cache from the configured Proxy using the configured Reader.
15351 * If using remote paging, then the first load call must specify the <em>start</em>
15352 * and <em>limit</em> properties in the options.params property to establish the initial
15353 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15355 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15356 * and this call will return before the new data has been loaded. Perform any post-processing
15357 * in a callback function, or in a "load" event handler.</strong>
15359 * @param {Object} options An object containing properties which control loading options:<ul>
15360 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15361 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15364 data : data, // array of key=>value data like JsonReader
15365 total : data.length,
15371 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15372 * passed the following arguments:<ul>
15373 * <li>r : Roo.data.Record[]</li>
15374 * <li>options: Options object from the load call</li>
15375 * <li>success: Boolean success indicator</li></ul></li>
15376 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15377 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15380 load : function(options){
15381 options = options || {};
15382 if(this.fireEvent("beforeload", this, options) !== false){
15383 this.storeOptions(options);
15384 var p = Roo.apply(options.params || {}, this.baseParams);
15385 // if meta was not loaded from remote source.. try requesting it.
15386 if (!this.reader.metaFromRemote) {
15387 p._requestMeta = 1;
15389 if(this.sortInfo && this.remoteSort){
15390 var pn = this.paramNames;
15391 p[pn["sort"]] = this.sortInfo.field;
15392 p[pn["dir"]] = this.sortInfo.direction;
15394 if (this.multiSort) {
15395 var pn = this.paramNames;
15396 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15399 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15404 * Reloads the Record cache from the configured Proxy using the configured Reader and
15405 * the options from the last load operation performed.
15406 * @param {Object} options (optional) An object containing properties which may override the options
15407 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15408 * the most recently used options are reused).
15410 reload : function(options){
15411 this.load(Roo.applyIf(options||{}, this.lastOptions));
15415 // Called as a callback by the Reader during a load operation.
15416 loadRecords : function(o, options, success){
15419 if(success !== false){
15420 this.fireEvent("load", this, [], options, o);
15422 if(options.callback){
15423 options.callback.call(options.scope || this, [], options, false);
15427 // if data returned failure - throw an exception.
15428 if (o.success === false) {
15429 // show a message if no listener is registered.
15430 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15431 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15433 // loadmask wil be hooked into this..
15434 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15437 var r = o.records, t = o.totalRecords || r.length;
15439 this.fireEvent("beforeloadadd", this, r, options, o);
15441 if(!options || options.add !== true){
15442 if(this.pruneModifiedRecords){
15443 this.modified = [];
15445 for(var i = 0, len = r.length; i < len; i++){
15449 this.data = this.snapshot;
15450 delete this.snapshot;
15453 this.data.addAll(r);
15454 this.totalLength = t;
15456 this.fireEvent("datachanged", this);
15458 this.totalLength = Math.max(t, this.data.length+r.length);
15462 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15464 var e = new Roo.data.Record({});
15466 e.set(this.parent.displayField, this.parent.emptyTitle);
15467 e.set(this.parent.valueField, '');
15472 this.fireEvent("load", this, r, options, o);
15473 if(options.callback){
15474 options.callback.call(options.scope || this, r, options, true);
15480 * Loads data from a passed data block. A Reader which understands the format of the data
15481 * must have been configured in the constructor.
15482 * @param {Object} data The data block from which to read the Records. The format of the data expected
15483 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15484 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15486 loadData : function(o, append){
15487 var r = this.reader.readRecords(o);
15488 this.loadRecords(r, {add: append}, true);
15492 * using 'cn' the nested child reader read the child array into it's child stores.
15493 * @param {Object} rec The record with a 'children array
15495 loadDataFromChildren : function(rec)
15497 this.loadData(this.reader.toLoadData(rec));
15502 * Gets the number of cached records.
15504 * <em>If using paging, this may not be the total size of the dataset. If the data object
15505 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15506 * the data set size</em>
15508 getCount : function(){
15509 return this.data.length || 0;
15513 * Gets the total number of records in the dataset as returned by the server.
15515 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15516 * the dataset size</em>
15518 getTotalCount : function(){
15519 return this.totalLength || 0;
15523 * Returns the sort state of the Store as an object with two properties:
15525 field {String} The name of the field by which the Records are sorted
15526 direction {String} The sort order, "ASC" or "DESC"
15529 getSortState : function(){
15530 return this.sortInfo;
15534 applySort : function(){
15535 if(this.sortInfo && !this.remoteSort){
15536 var s = this.sortInfo, f = s.field;
15537 var st = this.fields.get(f).sortType;
15538 var fn = function(r1, r2){
15539 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15540 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15542 this.data.sort(s.direction, fn);
15543 if(this.snapshot && this.snapshot != this.data){
15544 this.snapshot.sort(s.direction, fn);
15550 * Sets the default sort column and order to be used by the next load operation.
15551 * @param {String} fieldName The name of the field to sort by.
15552 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15554 setDefaultSort : function(field, dir){
15555 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15559 * Sort the Records.
15560 * If remote sorting is used, the sort is performed on the server, and the cache is
15561 * reloaded. If local sorting is used, the cache is sorted internally.
15562 * @param {String} fieldName The name of the field to sort by.
15563 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15565 sort : function(fieldName, dir){
15566 var f = this.fields.get(fieldName);
15568 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15570 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15571 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15576 this.sortToggle[f.name] = dir;
15577 this.sortInfo = {field: f.name, direction: dir};
15578 if(!this.remoteSort){
15580 this.fireEvent("datachanged", this);
15582 this.load(this.lastOptions);
15587 * Calls the specified function for each of the Records in the cache.
15588 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15589 * Returning <em>false</em> aborts and exits the iteration.
15590 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15592 each : function(fn, scope){
15593 this.data.each(fn, scope);
15597 * Gets all records modified since the last commit. Modified records are persisted across load operations
15598 * (e.g., during paging).
15599 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15601 getModifiedRecords : function(){
15602 return this.modified;
15606 createFilterFn : function(property, value, anyMatch){
15607 if(!value.exec){ // not a regex
15608 value = String(value);
15609 if(value.length == 0){
15612 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15614 return function(r){
15615 return value.test(r.data[property]);
15620 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15621 * @param {String} property A field on your records
15622 * @param {Number} start The record index to start at (defaults to 0)
15623 * @param {Number} end The last record index to include (defaults to length - 1)
15624 * @return {Number} The sum
15626 sum : function(property, start, end){
15627 var rs = this.data.items, v = 0;
15628 start = start || 0;
15629 end = (end || end === 0) ? end : rs.length-1;
15631 for(var i = start; i <= end; i++){
15632 v += (rs[i].data[property] || 0);
15638 * Filter the records by a specified property.
15639 * @param {String} field A field on your records
15640 * @param {String/RegExp} value Either a string that the field
15641 * should start with or a RegExp to test against the field
15642 * @param {Boolean} anyMatch True to match any part not just the beginning
15644 filter : function(property, value, anyMatch){
15645 var fn = this.createFilterFn(property, value, anyMatch);
15646 return fn ? this.filterBy(fn) : this.clearFilter();
15650 * Filter by a function. The specified function will be called with each
15651 * record in this data source. If the function returns true the record is included,
15652 * otherwise it is filtered.
15653 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15654 * @param {Object} scope (optional) The scope of the function (defaults to this)
15656 filterBy : function(fn, scope){
15657 this.snapshot = this.snapshot || this.data;
15658 this.data = this.queryBy(fn, scope||this);
15659 this.fireEvent("datachanged", this);
15663 * Query the records by a specified property.
15664 * @param {String} field A field on your records
15665 * @param {String/RegExp} value Either a string that the field
15666 * should start with or a RegExp to test against the field
15667 * @param {Boolean} anyMatch True to match any part not just the beginning
15668 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15670 query : function(property, value, anyMatch){
15671 var fn = this.createFilterFn(property, value, anyMatch);
15672 return fn ? this.queryBy(fn) : this.data.clone();
15676 * Query by a function. The specified function will be called with each
15677 * record in this data source. If the function returns true the record is included
15679 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15680 * @param {Object} scope (optional) The scope of the function (defaults to this)
15681 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15683 queryBy : function(fn, scope){
15684 var data = this.snapshot || this.data;
15685 return data.filterBy(fn, scope||this);
15689 * Collects unique values for a particular dataIndex from this store.
15690 * @param {String} dataIndex The property to collect
15691 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15692 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15693 * @return {Array} An array of the unique values
15695 collect : function(dataIndex, allowNull, bypassFilter){
15696 var d = (bypassFilter === true && this.snapshot) ?
15697 this.snapshot.items : this.data.items;
15698 var v, sv, r = [], l = {};
15699 for(var i = 0, len = d.length; i < len; i++){
15700 v = d[i].data[dataIndex];
15702 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15711 * Revert to a view of the Record cache with no filtering applied.
15712 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15714 clearFilter : function(suppressEvent){
15715 if(this.snapshot && this.snapshot != this.data){
15716 this.data = this.snapshot;
15717 delete this.snapshot;
15718 if(suppressEvent !== true){
15719 this.fireEvent("datachanged", this);
15725 afterEdit : function(record){
15726 if(this.modified.indexOf(record) == -1){
15727 this.modified.push(record);
15729 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15733 afterReject : function(record){
15734 this.modified.remove(record);
15735 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15739 afterCommit : function(record){
15740 this.modified.remove(record);
15741 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15745 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15746 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15748 commitChanges : function(){
15749 var m = this.modified.slice(0);
15750 this.modified = [];
15751 for(var i = 0, len = m.length; i < len; i++){
15757 * Cancel outstanding changes on all changed records.
15759 rejectChanges : function(){
15760 var m = this.modified.slice(0);
15761 this.modified = [];
15762 for(var i = 0, len = m.length; i < len; i++){
15767 onMetaChange : function(meta, rtype, o){
15768 this.recordType = rtype;
15769 this.fields = rtype.prototype.fields;
15770 delete this.snapshot;
15771 this.sortInfo = meta.sortInfo || this.sortInfo;
15772 this.modified = [];
15773 this.fireEvent('metachange', this, this.reader.meta);
15776 moveIndex : function(data, type)
15778 var index = this.indexOf(data);
15780 var newIndex = index + type;
15784 this.insert(newIndex, data);
15789 * Ext JS Library 1.1.1
15790 * Copyright(c) 2006-2007, Ext JS, LLC.
15792 * Originally Released Under LGPL - original licence link has changed is not relivant.
15795 * <script type="text/javascript">
15799 * @class Roo.data.SimpleStore
15800 * @extends Roo.data.Store
15801 * Small helper class to make creating Stores from Array data easier.
15802 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15803 * @cfg {Array} fields An array of field definition objects, or field name strings.
15804 * @cfg {Object} an existing reader (eg. copied from another store)
15805 * @cfg {Array} data The multi-dimensional array of data
15806 * @cfg {Roo.data.DataProxy} proxy [not-required]
15807 * @cfg {Roo.data.Reader} reader [not-required]
15809 * @param {Object} config
15811 Roo.data.SimpleStore = function(config)
15813 Roo.data.SimpleStore.superclass.constructor.call(this, {
15815 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15818 Roo.data.Record.create(config.fields)
15820 proxy : new Roo.data.MemoryProxy(config.data)
15824 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15826 * Ext JS Library 1.1.1
15827 * Copyright(c) 2006-2007, Ext JS, LLC.
15829 * Originally Released Under LGPL - original licence link has changed is not relivant.
15832 * <script type="text/javascript">
15837 * @extends Roo.data.Store
15838 * @class Roo.data.JsonStore
15839 * Small helper class to make creating Stores for JSON data easier. <br/>
15841 var store = new Roo.data.JsonStore({
15842 url: 'get-images.php',
15844 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15847 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15848 * JsonReader and HttpProxy (unless inline data is provided).</b>
15849 * @cfg {Array} fields An array of field definition objects, or field name strings.
15851 * @param {Object} config
15853 Roo.data.JsonStore = function(c){
15854 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15855 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15856 reader: new Roo.data.JsonReader(c, c.fields)
15859 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15861 * Ext JS Library 1.1.1
15862 * Copyright(c) 2006-2007, Ext JS, LLC.
15864 * Originally Released Under LGPL - original licence link has changed is not relivant.
15867 * <script type="text/javascript">
15871 Roo.data.Field = function(config){
15872 if(typeof config == "string"){
15873 config = {name: config};
15875 Roo.apply(this, config);
15878 this.type = "auto";
15881 var st = Roo.data.SortTypes;
15882 // named sortTypes are supported, here we look them up
15883 if(typeof this.sortType == "string"){
15884 this.sortType = st[this.sortType];
15887 // set default sortType for strings and dates
15888 if(!this.sortType){
15891 this.sortType = st.asUCString;
15894 this.sortType = st.asDate;
15897 this.sortType = st.none;
15902 var stripRe = /[\$,%]/g;
15904 // prebuilt conversion function for this field, instead of
15905 // switching every time we're reading a value
15907 var cv, dateFormat = this.dateFormat;
15912 cv = function(v){ return v; };
15915 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15919 return v !== undefined && v !== null && v !== '' ?
15920 parseInt(String(v).replace(stripRe, ""), 10) : '';
15925 return v !== undefined && v !== null && v !== '' ?
15926 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15931 cv = function(v){ return v === true || v === "true" || v == 1; };
15938 if(v instanceof Date){
15942 if(dateFormat == "timestamp"){
15943 return new Date(v*1000);
15945 return Date.parseDate(v, dateFormat);
15947 var parsed = Date.parse(v);
15948 return parsed ? new Date(parsed) : null;
15957 Roo.data.Field.prototype = {
15965 * Ext JS Library 1.1.1
15966 * Copyright(c) 2006-2007, Ext JS, LLC.
15968 * Originally Released Under LGPL - original licence link has changed is not relivant.
15971 * <script type="text/javascript">
15974 // Base class for reading structured data from a data source. This class is intended to be
15975 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15978 * @class Roo.data.DataReader
15980 * Base class for reading structured data from a data source. This class is intended to be
15981 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15984 Roo.data.DataReader = function(meta, recordType){
15988 this.recordType = recordType instanceof Array ?
15989 Roo.data.Record.create(recordType) : recordType;
15992 Roo.data.DataReader.prototype = {
15995 readerType : 'Data',
15997 * Create an empty record
15998 * @param {Object} data (optional) - overlay some values
15999 * @return {Roo.data.Record} record created.
16001 newRow : function(d) {
16003 this.recordType.prototype.fields.each(function(c) {
16005 case 'int' : da[c.name] = 0; break;
16006 case 'date' : da[c.name] = new Date(); break;
16007 case 'float' : da[c.name] = 0.0; break;
16008 case 'boolean' : da[c.name] = false; break;
16009 default : da[c.name] = ""; break;
16013 return new this.recordType(Roo.apply(da, d));
16019 * Ext JS Library 1.1.1
16020 * Copyright(c) 2006-2007, Ext JS, LLC.
16022 * Originally Released Under LGPL - original licence link has changed is not relivant.
16025 * <script type="text/javascript">
16029 * @class Roo.data.DataProxy
16030 * @extends Roo.util.Observable
16032 * This class is an abstract base class for implementations which provide retrieval of
16033 * unformatted data objects.<br>
16035 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16036 * (of the appropriate type which knows how to parse the data object) to provide a block of
16037 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16039 * Custom implementations must implement the load method as described in
16040 * {@link Roo.data.HttpProxy#load}.
16042 Roo.data.DataProxy = function(){
16045 * @event beforeload
16046 * Fires before a network request is made to retrieve a data object.
16047 * @param {Object} This DataProxy object.
16048 * @param {Object} params The params parameter to the load function.
16053 * Fires before the load method's callback is called.
16054 * @param {Object} This DataProxy object.
16055 * @param {Object} o The data object.
16056 * @param {Object} arg The callback argument object passed to the load function.
16060 * @event loadexception
16061 * Fires if an Exception occurs during data retrieval.
16062 * @param {Object} This DataProxy object.
16063 * @param {Object} o The data object.
16064 * @param {Object} arg The callback argument object passed to the load function.
16065 * @param {Object} e The Exception.
16067 loadexception : true
16069 Roo.data.DataProxy.superclass.constructor.call(this);
16072 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16075 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16079 * Ext JS Library 1.1.1
16080 * Copyright(c) 2006-2007, Ext JS, LLC.
16082 * Originally Released Under LGPL - original licence link has changed is not relivant.
16085 * <script type="text/javascript">
16088 * @class Roo.data.MemoryProxy
16089 * @extends Roo.data.DataProxy
16090 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16091 * to the Reader when its load method is called.
16093 * @param {Object} config A config object containing the objects needed for the Store to access data,
16095 Roo.data.MemoryProxy = function(config){
16097 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16098 data = config.data;
16100 Roo.data.MemoryProxy.superclass.constructor.call(this);
16104 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16107 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16110 * Load data from the requested source (in this case an in-memory
16111 * data object passed to the constructor), read the data object into
16112 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16113 * process that block using the passed callback.
16114 * @param {Object} params This parameter is not used by the MemoryProxy class.
16115 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16116 * object into a block of Roo.data.Records.
16117 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16118 * The function must be passed <ul>
16119 * <li>The Record block object</li>
16120 * <li>The "arg" argument from the load function</li>
16121 * <li>A boolean success indicator</li>
16123 * @param {Object} scope The scope in which to call the callback
16124 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16126 load : function(params, reader, callback, scope, arg){
16127 params = params || {};
16130 result = reader.readRecords(params.data ? params.data :this.data);
16132 this.fireEvent("loadexception", this, arg, null, e);
16133 callback.call(scope, null, arg, false);
16136 callback.call(scope, result, arg, true);
16140 update : function(params, records){
16145 * Ext JS Library 1.1.1
16146 * Copyright(c) 2006-2007, Ext JS, LLC.
16148 * Originally Released Under LGPL - original licence link has changed is not relivant.
16151 * <script type="text/javascript">
16154 * @class Roo.data.HttpProxy
16155 * @extends Roo.data.DataProxy
16156 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16157 * configured to reference a certain URL.<br><br>
16159 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16160 * from which the running page was served.<br><br>
16162 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16164 * Be aware that to enable the browser to parse an XML document, the server must set
16165 * the Content-Type header in the HTTP response to "text/xml".
16167 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16168 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16169 * will be used to make the request.
16171 Roo.data.HttpProxy = function(conn){
16172 Roo.data.HttpProxy.superclass.constructor.call(this);
16173 // is conn a conn config or a real conn?
16175 this.useAjax = !conn || !conn.events;
16179 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16180 // thse are take from connection...
16183 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16186 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16187 * extra parameters to each request made by this object. (defaults to undefined)
16190 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16191 * to each request made by this object. (defaults to undefined)
16194 * @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)
16197 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16200 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16206 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16210 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16211 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16212 * a finer-grained basis than the DataProxy events.
16214 getConnection : function(){
16215 return this.useAjax ? Roo.Ajax : this.conn;
16219 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16220 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16221 * process that block using the passed callback.
16222 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16223 * for the request to the remote server.
16224 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16225 * object into a block of Roo.data.Records.
16226 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16227 * The function must be passed <ul>
16228 * <li>The Record block object</li>
16229 * <li>The "arg" argument from the load function</li>
16230 * <li>A boolean success indicator</li>
16232 * @param {Object} scope The scope in which to call the callback
16233 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16235 load : function(params, reader, callback, scope, arg){
16236 if(this.fireEvent("beforeload", this, params) !== false){
16238 params : params || {},
16240 callback : callback,
16245 callback : this.loadResponse,
16249 Roo.applyIf(o, this.conn);
16250 if(this.activeRequest){
16251 Roo.Ajax.abort(this.activeRequest);
16253 this.activeRequest = Roo.Ajax.request(o);
16255 this.conn.request(o);
16258 callback.call(scope||this, null, arg, false);
16263 loadResponse : function(o, success, response){
16264 delete this.activeRequest;
16266 this.fireEvent("loadexception", this, o, response);
16267 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16272 result = o.reader.read(response);
16275 o.raw = { errorMsg : response.responseText };
16276 this.fireEvent("loadexception", this, o, response, e);
16277 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16281 this.fireEvent("load", this, o, o.request.arg);
16282 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16286 update : function(dataSet){
16291 updateResponse : function(dataSet){
16296 * Ext JS Library 1.1.1
16297 * Copyright(c) 2006-2007, Ext JS, LLC.
16299 * Originally Released Under LGPL - original licence link has changed is not relivant.
16302 * <script type="text/javascript">
16306 * @class Roo.data.ScriptTagProxy
16307 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16308 * other than the originating domain of the running page.<br><br>
16310 * <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
16311 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16313 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16314 * source code that is used as the source inside a <script> tag.<br><br>
16316 * In order for the browser to process the returned data, the server must wrap the data object
16317 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16318 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16319 * depending on whether the callback name was passed:
16322 boolean scriptTag = false;
16323 String cb = request.getParameter("callback");
16326 response.setContentType("text/javascript");
16328 response.setContentType("application/x-json");
16330 Writer out = response.getWriter();
16332 out.write(cb + "(");
16334 out.print(dataBlock.toJsonString());
16341 * @param {Object} config A configuration object.
16343 Roo.data.ScriptTagProxy = function(config){
16344 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16345 Roo.apply(this, config);
16346 this.head = document.getElementsByTagName("head")[0];
16349 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16351 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16353 * @cfg {String} url The URL from which to request the data object.
16356 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16360 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16361 * the server the name of the callback function set up by the load call to process the returned data object.
16362 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16363 * javascript output which calls this named function passing the data object as its only parameter.
16365 callbackParam : "callback",
16367 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16368 * name to the request.
16373 * Load data from the configured URL, read the data object into
16374 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16375 * process that block using the passed callback.
16376 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16377 * for the request to the remote server.
16378 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16379 * object into a block of Roo.data.Records.
16380 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16381 * The function must be passed <ul>
16382 * <li>The Record block object</li>
16383 * <li>The "arg" argument from the load function</li>
16384 * <li>A boolean success indicator</li>
16386 * @param {Object} scope The scope in which to call the callback
16387 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16389 load : function(params, reader, callback, scope, arg){
16390 if(this.fireEvent("beforeload", this, params) !== false){
16392 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16394 var url = this.url;
16395 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16397 url += "&_dc=" + (new Date().getTime());
16399 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16402 cb : "stcCallback"+transId,
16403 scriptId : "stcScript"+transId,
16407 callback : callback,
16413 window[trans.cb] = function(o){
16414 conn.handleResponse(o, trans);
16417 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16419 if(this.autoAbort !== false){
16423 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16425 var script = document.createElement("script");
16426 script.setAttribute("src", url);
16427 script.setAttribute("type", "text/javascript");
16428 script.setAttribute("id", trans.scriptId);
16429 this.head.appendChild(script);
16431 this.trans = trans;
16433 callback.call(scope||this, null, arg, false);
16438 isLoading : function(){
16439 return this.trans ? true : false;
16443 * Abort the current server request.
16445 abort : function(){
16446 if(this.isLoading()){
16447 this.destroyTrans(this.trans);
16452 destroyTrans : function(trans, isLoaded){
16453 this.head.removeChild(document.getElementById(trans.scriptId));
16454 clearTimeout(trans.timeoutId);
16456 window[trans.cb] = undefined;
16458 delete window[trans.cb];
16461 // if hasn't been loaded, wait for load to remove it to prevent script error
16462 window[trans.cb] = function(){
16463 window[trans.cb] = undefined;
16465 delete window[trans.cb];
16472 handleResponse : function(o, trans){
16473 this.trans = false;
16474 this.destroyTrans(trans, true);
16477 result = trans.reader.readRecords(o);
16479 this.fireEvent("loadexception", this, o, trans.arg, e);
16480 trans.callback.call(trans.scope||window, null, trans.arg, false);
16483 this.fireEvent("load", this, o, trans.arg);
16484 trans.callback.call(trans.scope||window, result, trans.arg, true);
16488 handleFailure : function(trans){
16489 this.trans = false;
16490 this.destroyTrans(trans, false);
16491 this.fireEvent("loadexception", this, null, trans.arg);
16492 trans.callback.call(trans.scope||window, null, trans.arg, false);
16496 * Ext JS Library 1.1.1
16497 * Copyright(c) 2006-2007, Ext JS, LLC.
16499 * Originally Released Under LGPL - original licence link has changed is not relivant.
16502 * <script type="text/javascript">
16506 * @class Roo.data.JsonReader
16507 * @extends Roo.data.DataReader
16508 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16509 * based on mappings in a provided Roo.data.Record constructor.
16511 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16512 * in the reply previously.
16517 var RecordDef = Roo.data.Record.create([
16518 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16519 {name: 'occupation'} // This field will use "occupation" as the mapping.
16521 var myReader = new Roo.data.JsonReader({
16522 totalProperty: "results", // The property which contains the total dataset size (optional)
16523 root: "rows", // The property which contains an Array of row objects
16524 id: "id" // The property within each row object that provides an ID for the record (optional)
16528 * This would consume a JSON file like this:
16530 { 'results': 2, 'rows': [
16531 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16532 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16535 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16536 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16537 * paged from the remote server.
16538 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16539 * @cfg {String} root name of the property which contains the Array of row objects.
16540 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16541 * @cfg {Array} fields Array of field definition objects
16543 * Create a new JsonReader
16544 * @param {Object} meta Metadata configuration options
16545 * @param {Object} recordType Either an Array of field definition objects,
16546 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16548 Roo.data.JsonReader = function(meta, recordType){
16551 // set some defaults:
16552 Roo.applyIf(meta, {
16553 totalProperty: 'total',
16554 successProperty : 'success',
16559 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16561 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16563 readerType : 'Json',
16566 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16567 * Used by Store query builder to append _requestMeta to params.
16570 metaFromRemote : false,
16572 * This method is only used by a DataProxy which has retrieved data from a remote server.
16573 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16574 * @return {Object} data A data block which is used by an Roo.data.Store object as
16575 * a cache of Roo.data.Records.
16577 read : function(response){
16578 var json = response.responseText;
16580 var o = /* eval:var:o */ eval("("+json+")");
16582 throw {message: "JsonReader.read: Json object not found"};
16588 this.metaFromRemote = true;
16589 this.meta = o.metaData;
16590 this.recordType = Roo.data.Record.create(o.metaData.fields);
16591 this.onMetaChange(this.meta, this.recordType, o);
16593 return this.readRecords(o);
16596 // private function a store will implement
16597 onMetaChange : function(meta, recordType, o){
16604 simpleAccess: function(obj, subsc) {
16611 getJsonAccessor: function(){
16613 return function(expr) {
16615 return(re.test(expr))
16616 ? new Function("obj", "return obj." + expr)
16621 return Roo.emptyFn;
16626 * Create a data block containing Roo.data.Records from an XML document.
16627 * @param {Object} o An object which contains an Array of row objects in the property specified
16628 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16629 * which contains the total size of the dataset.
16630 * @return {Object} data A data block which is used by an Roo.data.Store object as
16631 * a cache of Roo.data.Records.
16633 readRecords : function(o){
16635 * After any data loads, the raw JSON data is available for further custom processing.
16639 var s = this.meta, Record = this.recordType,
16640 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16642 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16644 if(s.totalProperty) {
16645 this.getTotal = this.getJsonAccessor(s.totalProperty);
16647 if(s.successProperty) {
16648 this.getSuccess = this.getJsonAccessor(s.successProperty);
16650 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16652 var g = this.getJsonAccessor(s.id);
16653 this.getId = function(rec) {
16655 return (r === undefined || r === "") ? null : r;
16658 this.getId = function(){return null;};
16661 for(var jj = 0; jj < fl; jj++){
16663 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16664 this.ef[jj] = this.getJsonAccessor(map);
16668 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16669 if(s.totalProperty){
16670 var vt = parseInt(this.getTotal(o), 10);
16675 if(s.successProperty){
16676 var vs = this.getSuccess(o);
16677 if(vs === false || vs === 'false'){
16682 for(var i = 0; i < c; i++){
16685 var id = this.getId(n);
16686 for(var j = 0; j < fl; j++){
16688 var v = this.ef[j](n);
16690 Roo.log('missing convert for ' + f.name);
16694 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16698 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16704 var record = new Record(values, id);
16706 records[i] = record;
16712 totalRecords : totalRecords
16715 // used when loading children.. @see loadDataFromChildren
16716 toLoadData: function(rec)
16718 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16719 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16720 return { data : data, total : data.length };
16725 * Ext JS Library 1.1.1
16726 * Copyright(c) 2006-2007, Ext JS, LLC.
16728 * Originally Released Under LGPL - original licence link has changed is not relivant.
16731 * <script type="text/javascript">
16735 * @class Roo.data.ArrayReader
16736 * @extends Roo.data.DataReader
16737 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16738 * Each element of that Array represents a row of data fields. The
16739 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16740 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16744 var RecordDef = Roo.data.Record.create([
16745 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16746 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16748 var myReader = new Roo.data.ArrayReader({
16749 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16753 * This would consume an Array like this:
16755 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16759 * Create a new JsonReader
16760 * @param {Object} meta Metadata configuration options.
16761 * @param {Object|Array} recordType Either an Array of field definition objects
16763 * @cfg {Array} fields Array of field definition objects
16764 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16765 * as specified to {@link Roo.data.Record#create},
16766 * or an {@link Roo.data.Record} object
16769 * created using {@link Roo.data.Record#create}.
16771 Roo.data.ArrayReader = function(meta, recordType)
16773 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16776 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16779 * Create a data block containing Roo.data.Records from an XML document.
16780 * @param {Object} o An Array of row objects which represents the dataset.
16781 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16782 * a cache of Roo.data.Records.
16784 readRecords : function(o)
16786 var sid = this.meta ? this.meta.id : null;
16787 var recordType = this.recordType, fields = recordType.prototype.fields;
16790 for(var i = 0; i < root.length; i++){
16793 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16794 for(var j = 0, jlen = fields.length; j < jlen; j++){
16795 var f = fields.items[j];
16796 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16797 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16799 values[f.name] = v;
16801 var record = new recordType(values, id);
16803 records[records.length] = record;
16807 totalRecords : records.length
16810 // used when loading children.. @see loadDataFromChildren
16811 toLoadData: function(rec)
16813 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16814 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16825 * @class Roo.bootstrap.form.ComboBox
16826 * @extends Roo.bootstrap.form.TriggerField
16827 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16828 * @cfg {Boolean} append (true|false) default false
16829 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16830 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16831 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16832 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16833 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16834 * @cfg {Boolean} animate default true
16835 * @cfg {Boolean} emptyResultText only for touch device
16836 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16837 * @cfg {String} emptyTitle default ''
16838 * @cfg {Number} width fixed with? experimental
16840 * Create a new ComboBox.
16841 * @param {Object} config Configuration options
16843 Roo.bootstrap.form.ComboBox = function(config){
16844 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16848 * Fires when the dropdown list is expanded
16849 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16854 * Fires when the dropdown list is collapsed
16855 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16859 * @event beforeselect
16860 * Fires before a list item is selected. Return false to cancel the selection.
16861 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16862 * @param {Roo.data.Record} record The data record returned from the underlying store
16863 * @param {Number} index The index of the selected item in the dropdown list
16865 'beforeselect' : true,
16868 * Fires when a list item is selected
16869 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16870 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16871 * @param {Number} index The index of the selected item in the dropdown list
16875 * @event beforequery
16876 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16877 * The event object passed has these properties:
16878 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16879 * @param {String} query The query
16880 * @param {Boolean} forceAll true to force "all" query
16881 * @param {Boolean} cancel true to cancel the query
16882 * @param {Object} e The query event object
16884 'beforequery': true,
16887 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16888 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16893 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16894 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16895 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16900 * Fires when the remove value from the combobox array
16901 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16905 * @event afterremove
16906 * Fires when the remove value from the combobox array
16907 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16909 'afterremove' : true,
16911 * @event specialfilter
16912 * Fires when specialfilter
16913 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16915 'specialfilter' : true,
16918 * Fires when tick the element
16919 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16923 * @event touchviewdisplay
16924 * Fires when touch view require special display (default is using displayField)
16925 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16926 * @param {Object} cfg set html .
16928 'touchviewdisplay' : true
16933 this.tickItems = [];
16935 this.selectedIndex = -1;
16936 if(this.mode == 'local'){
16937 if(config.queryDelay === undefined){
16938 this.queryDelay = 10;
16940 if(config.minChars === undefined){
16946 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16949 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16950 * rendering into an Roo.Editor, defaults to false)
16953 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16954 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16957 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16960 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16961 * the dropdown list (defaults to undefined, with no header element)
16965 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16969 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16971 listWidth: undefined,
16973 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16974 * mode = 'remote' or 'text' if mode = 'local')
16976 displayField: undefined,
16979 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16980 * mode = 'remote' or 'value' if mode = 'local').
16981 * Note: use of a valueField requires the user make a selection
16982 * in order for a value to be mapped.
16984 valueField: undefined,
16986 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16991 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16992 * field's data value (defaults to the underlying DOM element's name)
16994 hiddenName: undefined,
16996 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17000 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17002 selectedClass: 'active',
17005 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17009 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17010 * anchor positions (defaults to 'tl-bl')
17012 listAlign: 'tl-bl?',
17014 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17018 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17019 * query specified by the allQuery config option (defaults to 'query')
17021 triggerAction: 'query',
17023 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17024 * (defaults to 4, does not apply if editable = false)
17028 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17029 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17033 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17034 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17038 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17039 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17043 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17044 * when editable = true (defaults to false)
17046 selectOnFocus:false,
17048 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17050 queryParam: 'query',
17052 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17053 * when mode = 'remote' (defaults to 'Loading...')
17055 loadingText: 'Loading...',
17057 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17061 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17065 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17066 * traditional select (defaults to true)
17070 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17074 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17078 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17079 * listWidth has a higher value)
17083 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17084 * allow the user to set arbitrary text into the field (defaults to false)
17086 forceSelection:false,
17088 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17089 * if typeAhead = true (defaults to 250)
17091 typeAheadDelay : 250,
17093 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17094 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17096 valueNotFoundText : undefined,
17098 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17100 blockFocus : false,
17103 * @cfg {Boolean} disableClear Disable showing of clear button.
17105 disableClear : false,
17107 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17109 alwaysQuery : false,
17112 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17117 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17119 invalidClass : "has-warning",
17122 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17124 validClass : "has-success",
17127 * @cfg {Boolean} specialFilter (true|false) special filter default false
17129 specialFilter : false,
17132 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17134 mobileTouchView : true,
17137 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17139 useNativeIOS : false,
17142 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17144 mobile_restrict_height : false,
17146 ios_options : false,
17158 btnPosition : 'right',
17159 triggerList : true,
17160 showToggleBtn : true,
17162 emptyResultText: 'Empty',
17163 triggerText : 'Select',
17167 // element that contains real text value.. (when hidden is used..)
17169 getAutoCreate : function()
17174 * Render classic select for iso
17177 if(Roo.isIOS && this.useNativeIOS){
17178 cfg = this.getAutoCreateNativeIOS();
17186 if(Roo.isTouch && this.mobileTouchView){
17187 cfg = this.getAutoCreateTouchView();
17194 if(!this.tickable){
17195 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17200 * ComboBox with tickable selections
17203 var align = this.labelAlign || this.parentLabelAlign();
17206 cls : 'form-group roo-combobox-tickable' //input-group
17209 var btn_text_select = '';
17210 var btn_text_done = '';
17211 var btn_text_cancel = '';
17213 if (this.btn_text_show) {
17214 btn_text_select = 'Select';
17215 btn_text_done = 'Done';
17216 btn_text_cancel = 'Cancel';
17221 cls : 'tickable-buttons',
17226 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17227 //html : this.triggerText
17228 html: btn_text_select
17234 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17236 html: btn_text_done
17242 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17244 html: btn_text_cancel
17250 buttons.cn.unshift({
17252 cls: 'roo-select2-search-field-input'
17258 Roo.each(buttons.cn, function(c){
17260 c.cls += ' btn-' + _this.size;
17263 if (_this.disabled) {
17270 style : 'display: contents',
17275 cls: 'form-hidden-field'
17279 cls: 'roo-select2-choices',
17283 cls: 'roo-select2-search-field',
17294 cls: 'roo-select2-container input-group roo-select2-container-multi',
17300 // cls: 'typeahead typeahead-long dropdown-menu',
17301 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17306 if(this.hasFeedback && !this.allowBlank){
17310 cls: 'glyphicon form-control-feedback'
17313 combobox.cn.push(feedback);
17320 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17321 tooltip : 'This field is required'
17324 if (this.allowBlank) {
17327 style : 'display:none'
17330 if (align ==='left' && this.fieldLabel.length) {
17332 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
17339 cls : 'control-label col-form-label',
17340 html : this.fieldLabel
17352 var labelCfg = cfg.cn[1];
17353 var contentCfg = cfg.cn[2];
17356 if(this.indicatorpos == 'right'){
17362 cls : 'control-label col-form-label',
17366 html : this.fieldLabel
17382 labelCfg = cfg.cn[0];
17383 contentCfg = cfg.cn[1];
17387 if(this.labelWidth > 12){
17388 labelCfg.style = "width: " + this.labelWidth + 'px';
17390 if(this.width * 1 > 0){
17391 contentCfg.style = "width: " + this.width + 'px';
17393 if(this.labelWidth < 13 && this.labelmd == 0){
17394 this.labelmd = this.labelWidth;
17397 if(this.labellg > 0){
17398 labelCfg.cls += ' col-lg-' + this.labellg;
17399 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17402 if(this.labelmd > 0){
17403 labelCfg.cls += ' col-md-' + this.labelmd;
17404 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17407 if(this.labelsm > 0){
17408 labelCfg.cls += ' col-sm-' + this.labelsm;
17409 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17412 if(this.labelxs > 0){
17413 labelCfg.cls += ' col-xs-' + this.labelxs;
17414 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17418 } else if ( this.fieldLabel.length) {
17419 // Roo.log(" label");
17424 //cls : 'input-group-addon',
17425 html : this.fieldLabel
17430 if(this.indicatorpos == 'right'){
17434 //cls : 'input-group-addon',
17435 html : this.fieldLabel
17445 // Roo.log(" no label && no align");
17452 ['xs','sm','md','lg'].map(function(size){
17453 if (settings[size]) {
17454 cfg.cls += ' col-' + size + '-' + settings[size];
17462 _initEventsCalled : false,
17465 initEvents: function()
17467 if (this._initEventsCalled) { // as we call render... prevent looping...
17470 this._initEventsCalled = true;
17473 throw "can not find store for combo";
17476 this.indicator = this.indicatorEl();
17478 this.store = Roo.factory(this.store, Roo.data);
17479 this.store.parent = this;
17481 // if we are building from html. then this element is so complex, that we can not really
17482 // use the rendered HTML.
17483 // so we have to trash and replace the previous code.
17484 if (Roo.XComponent.build_from_html) {
17485 // remove this element....
17486 var e = this.el.dom, k=0;
17487 while (e ) { e = e.previousSibling; ++k;}
17492 this.rendered = false;
17494 this.render(this.parent().getChildContainer(true), k);
17497 if(Roo.isIOS && this.useNativeIOS){
17498 this.initIOSView();
17506 if(Roo.isTouch && this.mobileTouchView){
17507 this.initTouchView();
17512 this.initTickableEvents();
17516 Roo.bootstrap.form.ComboBox.superclass.initEvents.call(this);
17518 if(this.hiddenName){
17520 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17522 this.hiddenField.dom.value =
17523 this.hiddenValue !== undefined ? this.hiddenValue :
17524 this.value !== undefined ? this.value : '';
17526 // prevent input submission
17527 this.el.dom.removeAttribute('name');
17528 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17533 // this.el.dom.setAttribute('autocomplete', 'off');
17536 var cls = 'x-combo-list';
17538 //this.list = new Roo.Layer({
17539 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17545 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17546 _this.list.setWidth(lw);
17549 this.list.on('mouseover', this.onViewOver, this);
17550 this.list.on('mousemove', this.onViewMove, this);
17551 this.list.on('scroll', this.onViewScroll, this);
17554 this.list.swallowEvent('mousewheel');
17555 this.assetHeight = 0;
17558 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17559 this.assetHeight += this.header.getHeight();
17562 this.innerList = this.list.createChild({cls:cls+'-inner'});
17563 this.innerList.on('mouseover', this.onViewOver, this);
17564 this.innerList.on('mousemove', this.onViewMove, this);
17565 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17567 if(this.allowBlank && !this.pageSize && !this.disableClear){
17568 this.footer = this.list.createChild({cls:cls+'-ft'});
17569 this.pageTb = new Roo.Toolbar(this.footer);
17573 this.footer = this.list.createChild({cls:cls+'-ft'});
17574 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17575 {pageSize: this.pageSize});
17579 if (this.pageTb && this.allowBlank && !this.disableClear) {
17581 this.pageTb.add(new Roo.Toolbar.Fill(), {
17582 cls: 'x-btn-icon x-btn-clear',
17584 handler: function()
17587 _this.clearValue();
17588 _this.onSelect(false, -1);
17593 this.assetHeight += this.footer.getHeight();
17598 this.tpl = Roo.bootstrap.version == 4 ?
17599 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
17600 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17603 this.view = new Roo.View(this.list, this.tpl, {
17604 singleSelect:true, store: this.store, selectedClass: this.selectedClass
17606 //this.view.wrapEl.setDisplayed(false);
17607 this.view.on('click', this.onViewClick, this);
17610 this.store.on('beforeload', this.onBeforeLoad, this);
17611 this.store.on('load', this.onLoad, this);
17612 this.store.on('loadexception', this.onLoadException, this);
17614 if(this.resizable){
17615 this.resizer = new Roo.Resizable(this.list, {
17616 pinned:true, handles:'se'
17618 this.resizer.on('resize', function(r, w, h){
17619 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17620 this.listWidth = w;
17621 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17622 this.restrictHeight();
17624 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17627 if(!this.editable){
17628 this.editable = true;
17629 this.setEditable(false);
17634 if (typeof(this.events.add.listeners) != 'undefined') {
17636 this.addicon = this.wrap.createChild(
17637 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
17639 this.addicon.on('click', function(e) {
17640 this.fireEvent('add', this);
17643 if (typeof(this.events.edit.listeners) != 'undefined') {
17645 this.editicon = this.wrap.createChild(
17646 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
17647 if (this.addicon) {
17648 this.editicon.setStyle('margin-left', '40px');
17650 this.editicon.on('click', function(e) {
17652 // we fire even if inothing is selected..
17653 this.fireEvent('edit', this, this.lastData );
17659 this.keyNav = new Roo.KeyNav(this.inputEl(), {
17660 "up" : function(e){
17661 this.inKeyMode = true;
17665 "down" : function(e){
17666 if(!this.isExpanded()){
17667 this.onTriggerClick();
17669 this.inKeyMode = true;
17674 "enter" : function(e){
17675 // this.onViewClick();
17679 if(this.fireEvent("specialkey", this, e)){
17680 this.onViewClick(false);
17686 "esc" : function(e){
17690 "tab" : function(e){
17693 if(this.fireEvent("specialkey", this, e)){
17694 this.onViewClick(false);
17702 doRelay : function(foo, bar, hname){
17703 if(hname == 'down' || this.scope.isExpanded()){
17704 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17713 this.queryDelay = Math.max(this.queryDelay || 10,
17714 this.mode == 'local' ? 10 : 250);
17717 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17719 if(this.typeAhead){
17720 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17722 if(this.editable !== false){
17723 this.inputEl().on("keyup", this.onKeyUp, this);
17725 if(this.forceSelection){
17726 this.inputEl().on('blur', this.doForce, this);
17730 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17731 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17735 initTickableEvents: function()
17739 if(this.hiddenName){
17741 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17743 this.hiddenField.dom.value =
17744 this.hiddenValue !== undefined ? this.hiddenValue :
17745 this.value !== undefined ? this.value : '';
17747 // prevent input submission
17748 this.el.dom.removeAttribute('name');
17749 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17754 // this.list = this.el.select('ul.dropdown-menu',true).first();
17756 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17757 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17758 if(this.triggerList){
17759 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17762 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17763 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17765 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17766 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17768 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17769 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17771 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17772 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17773 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17776 this.cancelBtn.hide();
17781 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17782 _this.list.setWidth(lw);
17785 this.list.on('mouseover', this.onViewOver, this);
17786 this.list.on('mousemove', this.onViewMove, this);
17788 this.list.on('scroll', this.onViewScroll, this);
17791 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
17792 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17795 this.view = new Roo.View(this.list, this.tpl, {
17800 selectedClass: this.selectedClass
17803 //this.view.wrapEl.setDisplayed(false);
17804 this.view.on('click', this.onViewClick, this);
17808 this.store.on('beforeload', this.onBeforeLoad, this);
17809 this.store.on('load', this.onLoad, this);
17810 this.store.on('loadexception', this.onLoadException, this);
17813 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17814 "up" : function(e){
17815 this.inKeyMode = true;
17819 "down" : function(e){
17820 this.inKeyMode = true;
17824 "enter" : function(e){
17825 if(this.fireEvent("specialkey", this, e)){
17826 this.onViewClick(false);
17832 "esc" : function(e){
17833 this.onTickableFooterButtonClick(e, false, false);
17836 "tab" : function(e){
17837 this.fireEvent("specialkey", this, e);
17839 this.onTickableFooterButtonClick(e, false, false);
17846 doRelay : function(e, fn, key){
17847 if(this.scope.isExpanded()){
17848 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17857 this.queryDelay = Math.max(this.queryDelay || 10,
17858 this.mode == 'local' ? 10 : 250);
17861 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17863 if(this.typeAhead){
17864 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17867 if(this.editable !== false){
17868 this.tickableInputEl().on("keyup", this.onKeyUp, this);
17871 this.indicator = this.indicatorEl();
17873 if(this.indicator){
17874 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17875 this.indicator.hide();
17880 onDestroy : function(){
17882 this.view.setStore(null);
17883 this.view.el.removeAllListeners();
17884 this.view.el.remove();
17885 this.view.purgeListeners();
17888 this.list.dom.innerHTML = '';
17892 this.store.un('beforeload', this.onBeforeLoad, this);
17893 this.store.un('load', this.onLoad, this);
17894 this.store.un('loadexception', this.onLoadException, this);
17896 Roo.bootstrap.form.ComboBox.superclass.onDestroy.call(this);
17900 fireKey : function(e){
17901 if(e.isNavKeyPress() && !this.list.isVisible()){
17902 this.fireEvent("specialkey", this, e);
17907 onResize: function(w, h)
17911 // Roo.bootstrap.form.ComboBox.superclass.onResize.apply(this, arguments);
17913 // if(typeof w != 'number'){
17914 // // we do not handle it!?!?
17917 // var tw = this.trigger.getWidth();
17918 // // tw += this.addicon ? this.addicon.getWidth() : 0;
17919 // // tw += this.editicon ? this.editicon.getWidth() : 0;
17921 // this.inputEl().setWidth( this.adjustWidth('input', x));
17923 // //this.trigger.setStyle('left', x+'px');
17925 // if(this.list && this.listWidth === undefined){
17926 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17927 // this.list.setWidth(lw);
17928 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17936 * Allow or prevent the user from directly editing the field text. If false is passed,
17937 * the user will only be able to select from the items defined in the dropdown list. This method
17938 * is the runtime equivalent of setting the 'editable' config option at config time.
17939 * @param {Boolean} value True to allow the user to directly edit the field text
17941 setEditable : function(value){
17942 if(value == this.editable){
17945 this.editable = value;
17947 this.inputEl().dom.setAttribute('readOnly', true);
17948 this.inputEl().on('mousedown', this.onTriggerClick, this);
17949 this.inputEl().addClass('x-combo-noedit');
17951 this.inputEl().dom.removeAttribute('readOnly');
17952 this.inputEl().un('mousedown', this.onTriggerClick, this);
17953 this.inputEl().removeClass('x-combo-noedit');
17959 onBeforeLoad : function(combo,opts){
17960 if(!this.hasFocus){
17964 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17966 this.restrictHeight();
17967 this.selectedIndex = -1;
17971 onLoad : function(){
17973 this.hasQuery = false;
17975 if(!this.hasFocus){
17979 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17980 this.loading.hide();
17983 if(this.store.getCount() > 0){
17986 this.restrictHeight();
17987 if(this.lastQuery == this.allQuery){
17988 if(this.editable && !this.tickable){
17989 this.inputEl().dom.select();
17993 !this.selectByValue(this.value, true) &&
17996 !this.store.lastOptions ||
17997 typeof(this.store.lastOptions.add) == 'undefined' ||
17998 this.store.lastOptions.add != true
18001 this.select(0, true);
18004 if(this.autoFocus){
18007 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
18008 this.taTask.delay(this.typeAheadDelay);
18012 this.onEmptyResults();
18018 onLoadException : function()
18020 this.hasQuery = false;
18022 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
18023 this.loading.hide();
18026 if(this.tickable && this.editable){
18031 // only causes errors at present
18032 //Roo.log(this.store.reader.jsonData);
18033 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
18035 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
18041 onTypeAhead : function(){
18042 if(this.store.getCount() > 0){
18043 var r = this.store.getAt(0);
18044 var newValue = r.data[this.displayField];
18045 var len = newValue.length;
18046 var selStart = this.getRawValue().length;
18048 if(selStart != len){
18049 this.setRawValue(newValue);
18050 this.selectText(selStart, newValue.length);
18056 onSelect : function(record, index){
18058 if(this.fireEvent('beforeselect', this, record, index) !== false){
18060 this.setFromData(index > -1 ? record.data : false);
18063 this.fireEvent('select', this, record, index);
18068 * Returns the currently selected field value or empty string if no value is set.
18069 * @return {String} value The selected value
18071 getValue : function()
18073 if(Roo.isIOS && this.useNativeIOS){
18074 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
18078 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
18081 if(this.valueField){
18082 return typeof this.value != 'undefined' ? this.value : '';
18084 return Roo.bootstrap.form.ComboBox.superclass.getValue.call(this);
18088 getRawValue : function()
18090 if(Roo.isIOS && this.useNativeIOS){
18091 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
18094 var v = this.inputEl().getValue();
18100 * Clears any text/value currently set in the field
18102 clearValue : function(){
18104 if(this.hiddenField){
18105 this.hiddenField.dom.value = '';
18108 this.setRawValue('');
18109 this.lastSelectionText = '';
18110 this.lastData = false;
18112 var close = this.closeTriggerEl();
18123 * Sets the specified value into the field. If the value finds a match, the corresponding record text
18124 * will be displayed in the field. If the value does not match the data value of an existing item,
18125 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
18126 * Otherwise the field will be blank (although the value will still be set).
18127 * @param {String} value The value to match
18129 setValue : function(v)
18131 if(Roo.isIOS && this.useNativeIOS){
18132 this.setIOSValue(v);
18142 if(this.valueField){
18143 var r = this.findRecord(this.valueField, v);
18145 text = r.data[this.displayField];
18146 }else if(this.valueNotFoundText !== undefined){
18147 text = this.valueNotFoundText;
18150 this.lastSelectionText = text;
18151 if(this.hiddenField){
18152 this.hiddenField.dom.value = v;
18154 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, text);
18157 var close = this.closeTriggerEl();
18160 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
18166 * @property {Object} the last set data for the element
18171 * Sets the value of the field based on a object which is related to the record format for the store.
18172 * @param {Object} value the value to set as. or false on reset?
18174 setFromData : function(o){
18181 var dv = ''; // display value
18182 var vv = ''; // value value..
18184 if (this.displayField) {
18185 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18187 // this is an error condition!!!
18188 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18191 if(this.valueField){
18192 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
18195 var close = this.closeTriggerEl();
18198 if(dv.length || vv * 1 > 0){
18200 this.blockFocus=true;
18206 if(this.hiddenField){
18207 this.hiddenField.dom.value = vv;
18209 this.lastSelectionText = dv;
18210 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18214 // no hidden field.. - we store the value in 'value', but still display
18215 // display field!!!!
18216 this.lastSelectionText = dv;
18217 Roo.bootstrap.form.ComboBox.superclass.setValue.call(this, dv);
18224 reset : function(){
18225 // overridden so that last data is reset..
18232 this.setValue(this.originalValue);
18233 //this.clearInvalid();
18234 this.lastData = false;
18236 this.view.clearSelections();
18242 findRecord : function(prop, value){
18244 if(this.store.getCount() > 0){
18245 this.store.each(function(r){
18246 if(r.data[prop] == value){
18256 getName: function()
18258 // returns hidden if it's set..
18259 if (!this.rendered) {return ''};
18260 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
18264 onViewMove : function(e, t){
18265 this.inKeyMode = false;
18269 onViewOver : function(e, t){
18270 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
18273 var item = this.view.findItemFromChild(t);
18276 var index = this.view.indexOf(item);
18277 this.select(index, false);
18282 onViewClick : function(view, doFocus, el, e)
18284 var index = this.view.getSelectedIndexes()[0];
18286 var r = this.store.getAt(index);
18290 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
18297 Roo.each(this.tickItems, function(v,k){
18299 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
18301 _this.tickItems.splice(k, 1);
18303 if(typeof(e) == 'undefined' && view == false){
18304 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
18316 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
18317 this.tickItems.push(r.data);
18320 if(typeof(e) == 'undefined' && view == false){
18321 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
18328 this.onSelect(r, index);
18330 if(doFocus !== false && !this.blockFocus){
18331 this.inputEl().focus();
18336 restrictHeight : function(){
18337 //this.innerList.dom.style.height = '';
18338 //var inner = this.innerList.dom;
18339 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
18340 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
18341 //this.list.beginUpdate();
18342 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
18343 this.list.alignTo(this.inputEl(), this.listAlign);
18344 this.list.alignTo(this.inputEl(), this.listAlign);
18345 //this.list.endUpdate();
18349 onEmptyResults : function(){
18351 if(this.tickable && this.editable){
18352 this.hasFocus = false;
18353 this.restrictHeight();
18361 * Returns true if the dropdown list is expanded, else false.
18363 isExpanded : function(){
18364 return this.list.isVisible();
18368 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
18369 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18370 * @param {String} value The data value of the item to select
18371 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18372 * selected item if it is not currently in view (defaults to true)
18373 * @return {Boolean} True if the value matched an item in the list, else false
18375 selectByValue : function(v, scrollIntoView){
18376 if(v !== undefined && v !== null){
18377 var r = this.findRecord(this.valueField || this.displayField, v);
18379 this.select(this.store.indexOf(r), scrollIntoView);
18387 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
18388 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
18389 * @param {Number} index The zero-based index of the list item to select
18390 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
18391 * selected item if it is not currently in view (defaults to true)
18393 select : function(index, scrollIntoView){
18394 this.selectedIndex = index;
18395 this.view.select(index);
18396 if(scrollIntoView !== false){
18397 var el = this.view.getNode(index);
18399 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
18402 this.list.scrollChildIntoView(el, false);
18408 selectNext : function(){
18409 var ct = this.store.getCount();
18411 if(this.selectedIndex == -1){
18413 }else if(this.selectedIndex < ct-1){
18414 this.select(this.selectedIndex+1);
18420 selectPrev : function(){
18421 var ct = this.store.getCount();
18423 if(this.selectedIndex == -1){
18425 }else if(this.selectedIndex != 0){
18426 this.select(this.selectedIndex-1);
18432 onKeyUp : function(e){
18433 if(this.editable !== false && !e.isSpecialKey()){
18434 this.lastKey = e.getKey();
18435 this.dqTask.delay(this.queryDelay);
18440 validateBlur : function(){
18441 return !this.list || !this.list.isVisible();
18445 initQuery : function(){
18447 var v = this.getRawValue();
18449 if(this.tickable && this.editable){
18450 v = this.tickableInputEl().getValue();
18457 doForce : function(){
18458 if(this.inputEl().dom.value.length > 0){
18459 this.inputEl().dom.value =
18460 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
18466 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
18467 * query allowing the query action to be canceled if needed.
18468 * @param {String} query The SQL query to execute
18469 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
18470 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
18471 * saved in the current store (defaults to false)
18473 doQuery : function(q, forceAll){
18475 if(q === undefined || q === null){
18480 forceAll: forceAll,
18484 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18489 forceAll = qe.forceAll;
18490 if(forceAll === true || (q.length >= this.minChars)){
18492 this.hasQuery = true;
18494 if(this.lastQuery != q || this.alwaysQuery){
18495 this.lastQuery = q;
18496 if(this.mode == 'local'){
18497 this.selectedIndex = -1;
18499 this.store.clearFilter();
18502 if(this.specialFilter){
18503 this.fireEvent('specialfilter', this);
18508 this.store.filter(this.displayField, q);
18511 this.store.fireEvent("datachanged", this.store);
18518 this.store.baseParams[this.queryParam] = q;
18520 var options = {params : this.getParams(q)};
18523 options.add = true;
18524 options.params.start = this.page * this.pageSize;
18527 this.store.load(options);
18530 * this code will make the page width larger, at the beginning, the list not align correctly,
18531 * we should expand the list on onLoad
18532 * so command out it
18537 this.selectedIndex = -1;
18542 this.loadNext = false;
18546 getParams : function(q){
18548 //p[this.queryParam] = q;
18552 p.limit = this.pageSize;
18558 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18560 collapse : function(){
18561 if(!this.isExpanded()){
18567 this.hasFocus = false;
18571 this.cancelBtn.hide();
18572 this.trigger.show();
18575 this.tickableInputEl().dom.value = '';
18576 this.tickableInputEl().blur();
18581 Roo.get(document).un('mousedown', this.collapseIf, this);
18582 Roo.get(document).un('mousewheel', this.collapseIf, this);
18583 if (!this.editable) {
18584 Roo.get(document).un('keydown', this.listKeyPress, this);
18586 this.fireEvent('collapse', this);
18592 collapseIf : function(e){
18593 var in_combo = e.within(this.el);
18594 var in_list = e.within(this.list);
18595 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18597 if (in_combo || in_list || is_list) {
18598 //e.stopPropagation();
18603 this.onTickableFooterButtonClick(e, false, false);
18611 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18613 expand : function(){
18615 if(this.isExpanded() || !this.hasFocus){
18619 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18620 this.list.setWidth(lw);
18626 this.restrictHeight();
18630 this.tickItems = Roo.apply([], this.item);
18633 this.cancelBtn.show();
18634 this.trigger.hide();
18637 this.tickableInputEl().focus();
18642 Roo.get(document).on('mousedown', this.collapseIf, this);
18643 Roo.get(document).on('mousewheel', this.collapseIf, this);
18644 if (!this.editable) {
18645 Roo.get(document).on('keydown', this.listKeyPress, this);
18648 this.fireEvent('expand', this);
18652 // Implements the default empty TriggerField.onTriggerClick function
18653 onTriggerClick : function(e)
18655 Roo.log('trigger click');
18657 if(this.disabled || !this.triggerList){
18662 this.loadNext = false;
18664 if(this.isExpanded()){
18666 if (!this.blockFocus) {
18667 this.inputEl().focus();
18671 this.hasFocus = true;
18672 if(this.triggerAction == 'all') {
18673 this.doQuery(this.allQuery, true);
18675 this.doQuery(this.getRawValue());
18677 if (!this.blockFocus) {
18678 this.inputEl().focus();
18683 onTickableTriggerClick : function(e)
18690 this.loadNext = false;
18691 this.hasFocus = true;
18693 if(this.triggerAction == 'all') {
18694 this.doQuery(this.allQuery, true);
18696 this.doQuery(this.getRawValue());
18700 onSearchFieldClick : function(e)
18702 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18703 this.onTickableFooterButtonClick(e, false, false);
18707 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18712 this.loadNext = false;
18713 this.hasFocus = true;
18715 if(this.triggerAction == 'all') {
18716 this.doQuery(this.allQuery, true);
18718 this.doQuery(this.getRawValue());
18722 listKeyPress : function(e)
18724 //Roo.log('listkeypress');
18725 // scroll to first matching element based on key pres..
18726 if (e.isSpecialKey()) {
18729 var k = String.fromCharCode(e.getKey()).toUpperCase();
18732 var csel = this.view.getSelectedNodes();
18733 var cselitem = false;
18735 var ix = this.view.indexOf(csel[0]);
18736 cselitem = this.store.getAt(ix);
18737 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18743 this.store.each(function(v) {
18745 // start at existing selection.
18746 if (cselitem.id == v.id) {
18752 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18753 match = this.store.indexOf(v);
18759 if (match === false) {
18760 return true; // no more action?
18763 this.view.select(match);
18764 var sn = Roo.get(this.view.getSelectedNodes()[0]);
18765 sn.scrollIntoView(sn.dom.parentNode, false);
18768 onViewScroll : function(e, t){
18770 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){
18774 this.hasQuery = true;
18776 this.loading = this.list.select('.loading', true).first();
18778 if(this.loading === null){
18779 this.list.createChild({
18781 cls: 'loading roo-select2-more-results roo-select2-active',
18782 html: 'Loading more results...'
18785 this.loading = this.list.select('.loading', true).first();
18787 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18789 this.loading.hide();
18792 this.loading.show();
18797 this.loadNext = true;
18799 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18804 addItem : function(o)
18806 var dv = ''; // display value
18808 if (this.displayField) {
18809 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18811 // this is an error condition!!!
18812 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
18819 var choice = this.choices.createChild({
18821 cls: 'roo-select2-search-choice',
18830 cls: 'roo-select2-search-choice-close fa fa-times',
18835 }, this.searchField);
18837 var close = choice.select('a.roo-select2-search-choice-close', true).first();
18839 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18847 this.inputEl().dom.value = '';
18852 onRemoveItem : function(e, _self, o)
18854 e.preventDefault();
18856 this.lastItem = Roo.apply([], this.item);
18858 var index = this.item.indexOf(o.data) * 1;
18861 Roo.log('not this item?!');
18865 this.item.splice(index, 1);
18870 this.fireEvent('remove', this, e);
18876 syncValue : function()
18878 if(!this.item.length){
18885 Roo.each(this.item, function(i){
18886 if(_this.valueField){
18887 value.push(i[_this.valueField]);
18894 this.value = value.join(',');
18896 if(this.hiddenField){
18897 this.hiddenField.dom.value = this.value;
18900 this.store.fireEvent("datachanged", this.store);
18905 clearItem : function()
18907 if(!this.multiple){
18913 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18921 if(this.tickable && !Roo.isTouch){
18922 this.view.refresh();
18926 inputEl: function ()
18928 if(Roo.isIOS && this.useNativeIOS){
18929 return this.el.select('select.roo-ios-select', true).first();
18932 if(Roo.isTouch && this.mobileTouchView){
18933 return this.el.select('input.form-control',true).first();
18937 return this.searchField;
18940 return this.el.select('input.form-control',true).first();
18943 onTickableFooterButtonClick : function(e, btn, el)
18945 e.preventDefault();
18947 this.lastItem = Roo.apply([], this.item);
18949 if(btn && btn.name == 'cancel'){
18950 this.tickItems = Roo.apply([], this.item);
18959 Roo.each(this.tickItems, function(o){
18967 validate : function()
18969 if(this.getVisibilityEl().hasClass('hidden')){
18973 var v = this.getRawValue();
18976 v = this.getValue();
18979 if(this.disabled || this.allowBlank || v.length){
18984 this.markInvalid();
18988 tickableInputEl : function()
18990 if(!this.tickable || !this.editable){
18991 return this.inputEl();
18994 return this.inputEl().select('.roo-select2-search-field-input', true).first();
18998 getAutoCreateTouchView : function()
19003 cls: 'form-group' //input-group
19009 type : this.inputType,
19010 cls : 'form-control x-combo-noedit',
19011 autocomplete: 'new-password',
19012 placeholder : this.placeholder || '',
19017 input.name = this.name;
19021 input.cls += ' input-' + this.size;
19024 if (this.disabled) {
19025 input.disabled = true;
19029 cls : 'roo-combobox-wrap',
19036 inputblock.cls += ' input-group';
19038 inputblock.cn.unshift({
19040 cls : 'input-group-addon input-group-prepend input-group-text',
19045 if(this.removable && !this.multiple){
19046 inputblock.cls += ' roo-removable';
19048 inputblock.cn.push({
19051 cls : 'roo-combo-removable-btn close'
19055 if(this.hasFeedback && !this.allowBlank){
19057 inputblock.cls += ' has-feedback';
19059 inputblock.cn.push({
19061 cls: 'glyphicon form-control-feedback'
19068 inputblock.cls += (this.before) ? '' : ' input-group';
19070 inputblock.cn.push({
19072 cls : 'input-group-addon input-group-append input-group-text',
19078 var ibwrap = inputblock;
19083 cls: 'roo-select2-choices',
19087 cls: 'roo-select2-search-field',
19100 cls: 'roo-select2-container input-group roo-touchview-combobox ',
19105 cls: 'form-hidden-field'
19111 if(!this.multiple && this.showToggleBtn){
19117 if (this.caret != false) {
19120 cls: 'fa fa-' + this.caret
19127 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
19129 Roo.bootstrap.version == 3 ? caret : '',
19132 cls: 'combobox-clear',
19146 combobox.cls += ' roo-select2-container-multi';
19149 var required = this.allowBlank ? {
19151 style: 'display: none'
19154 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
19155 tooltip : 'This field is required'
19158 var align = this.labelAlign || this.parentLabelAlign();
19160 if (align ==='left' && this.fieldLabel.length) {
19166 cls : 'control-label col-form-label',
19167 html : this.fieldLabel
19171 cls : 'roo-combobox-wrap ',
19178 var labelCfg = cfg.cn[1];
19179 var contentCfg = cfg.cn[2];
19182 if(this.indicatorpos == 'right'){
19187 cls : 'control-label col-form-label',
19191 html : this.fieldLabel
19197 cls : "roo-combobox-wrap ",
19205 labelCfg = cfg.cn[0];
19206 contentCfg = cfg.cn[1];
19211 if(this.labelWidth > 12){
19212 labelCfg.style = "width: " + this.labelWidth + 'px';
19215 if(this.labelWidth < 13 && this.labelmd == 0){
19216 this.labelmd = this.labelWidth;
19219 if(this.labellg > 0){
19220 labelCfg.cls += ' col-lg-' + this.labellg;
19221 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
19224 if(this.labelmd > 0){
19225 labelCfg.cls += ' col-md-' + this.labelmd;
19226 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
19229 if(this.labelsm > 0){
19230 labelCfg.cls += ' col-sm-' + this.labelsm;
19231 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
19234 if(this.labelxs > 0){
19235 labelCfg.cls += ' col-xs-' + this.labelxs;
19236 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
19240 } else if ( this.fieldLabel.length) {
19245 cls : 'control-label',
19246 html : this.fieldLabel
19257 if(this.indicatorpos == 'right'){
19261 cls : 'control-label',
19262 html : this.fieldLabel,
19280 var settings = this;
19282 ['xs','sm','md','lg'].map(function(size){
19283 if (settings[size]) {
19284 cfg.cls += ' col-' + size + '-' + settings[size];
19291 initTouchView : function()
19293 this.renderTouchView();
19295 this.touchViewEl.on('scroll', function(){
19296 this.el.dom.scrollTop = 0;
19299 this.originalValue = this.getValue();
19301 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
19303 this.inputEl().on("click", this.showTouchView, this);
19304 if (this.triggerEl) {
19305 this.triggerEl.on("click", this.showTouchView, this);
19309 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
19310 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
19312 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
19314 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
19315 this.store.on('load', this.onTouchViewLoad, this);
19316 this.store.on('loadexception', this.onTouchViewLoadException, this);
19318 if(this.hiddenName){
19320 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
19322 this.hiddenField.dom.value =
19323 this.hiddenValue !== undefined ? this.hiddenValue :
19324 this.value !== undefined ? this.value : '';
19326 this.el.dom.removeAttribute('name');
19327 this.hiddenField.dom.setAttribute('name', this.hiddenName);
19331 this.choices = this.el.select('ul.roo-select2-choices', true).first();
19332 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
19335 if(this.removable && !this.multiple){
19336 var close = this.closeTriggerEl();
19338 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
19339 close.on('click', this.removeBtnClick, this, close);
19343 * fix the bug in Safari iOS8
19345 this.inputEl().on("focus", function(e){
19346 document.activeElement.blur();
19349 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19356 renderTouchView : function()
19358 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.form.ComboBox.touchViewTemplate);
19359 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19361 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
19362 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19364 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
19365 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19366 this.touchViewBodyEl.setStyle('overflow', 'auto');
19368 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
19369 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19371 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
19372 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19376 showTouchView : function()
19382 this.touchViewHeaderEl.hide();
19384 if(this.modalTitle.length){
19385 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
19386 this.touchViewHeaderEl.show();
19389 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
19390 this.touchViewEl.show();
19392 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
19394 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
19395 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19397 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19399 if(this.modalTitle.length){
19400 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19403 this.touchViewBodyEl.setHeight(bodyHeight);
19407 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
19409 this.touchViewEl.addClass(['in','show']);
19412 if(this._touchViewMask){
19413 Roo.get(document.body).addClass("x-body-masked");
19414 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
19415 this._touchViewMask.setStyle('z-index', 10000);
19416 this._touchViewMask.addClass('show');
19419 this.doTouchViewQuery();
19423 hideTouchView : function()
19425 this.touchViewEl.removeClass(['in','show']);
19429 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
19431 this.touchViewEl.setStyle('display', 'none');
19434 if(this._touchViewMask){
19435 this._touchViewMask.removeClass('show');
19436 Roo.get(document.body).removeClass("x-body-masked");
19440 setTouchViewValue : function()
19447 Roo.each(this.tickItems, function(o){
19452 this.hideTouchView();
19455 doTouchViewQuery : function()
19464 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
19468 if(!this.alwaysQuery || this.mode == 'local'){
19469 this.onTouchViewLoad();
19476 onTouchViewBeforeLoad : function(combo,opts)
19482 onTouchViewLoad : function()
19484 if(this.store.getCount() < 1){
19485 this.onTouchViewEmptyResults();
19489 this.clearTouchView();
19491 var rawValue = this.getRawValue();
19493 var template = (this.multiple) ? Roo.bootstrap.form.ComboBox.listItemCheckbox : Roo.bootstrap.form.ComboBox.listItemRadio;
19495 this.tickItems = [];
19497 this.store.data.each(function(d, rowIndex){
19498 var row = this.touchViewListGroup.createChild(template);
19500 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19501 row.addClass(d.data.cls);
19504 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19507 html : d.data[this.displayField]
19510 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19511 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19514 row.removeClass('selected');
19515 if(!this.multiple && this.valueField &&
19516 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19519 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19520 row.addClass('selected');
19523 if(this.multiple && this.valueField &&
19524 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19528 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19529 this.tickItems.push(d.data);
19532 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19536 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19538 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19540 if(this.modalTitle.length){
19541 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19544 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19546 if(this.mobile_restrict_height && listHeight < bodyHeight){
19547 this.touchViewBodyEl.setHeight(listHeight);
19552 if(firstChecked && listHeight > bodyHeight){
19553 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19558 onTouchViewLoadException : function()
19560 this.hideTouchView();
19563 onTouchViewEmptyResults : function()
19565 this.clearTouchView();
19567 this.touchViewListGroup.createChild(Roo.bootstrap.form.ComboBox.emptyResult);
19569 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19573 clearTouchView : function()
19575 this.touchViewListGroup.dom.innerHTML = '';
19578 onTouchViewClick : function(e, el, o)
19580 e.preventDefault();
19583 var rowIndex = o.rowIndex;
19585 var r = this.store.getAt(rowIndex);
19587 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19589 if(!this.multiple){
19590 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19591 c.dom.removeAttribute('checked');
19594 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19596 this.setFromData(r.data);
19598 var close = this.closeTriggerEl();
19604 this.hideTouchView();
19606 this.fireEvent('select', this, r, rowIndex);
19611 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19612 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19613 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19617 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19618 this.addItem(r.data);
19619 this.tickItems.push(r.data);
19623 getAutoCreateNativeIOS : function()
19626 cls: 'form-group' //input-group,
19631 cls : 'roo-ios-select'
19635 combobox.name = this.name;
19638 if (this.disabled) {
19639 combobox.disabled = true;
19642 var settings = this;
19644 ['xs','sm','md','lg'].map(function(size){
19645 if (settings[size]) {
19646 cfg.cls += ' col-' + size + '-' + settings[size];
19656 initIOSView : function()
19658 this.store.on('load', this.onIOSViewLoad, this);
19663 onIOSViewLoad : function()
19665 if(this.store.getCount() < 1){
19669 this.clearIOSView();
19671 if(this.allowBlank) {
19673 var default_text = '-- SELECT --';
19675 if(this.placeholder.length){
19676 default_text = this.placeholder;
19679 if(this.emptyTitle.length){
19680 default_text += ' - ' + this.emptyTitle + ' -';
19683 var opt = this.inputEl().createChild({
19686 html : default_text
19690 o[this.valueField] = 0;
19691 o[this.displayField] = default_text;
19693 this.ios_options.push({
19700 this.store.data.each(function(d, rowIndex){
19704 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19705 html = d.data[this.displayField];
19710 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19711 value = d.data[this.valueField];
19720 if(this.value == d.data[this.valueField]){
19721 option['selected'] = true;
19724 var opt = this.inputEl().createChild(option);
19726 this.ios_options.push({
19733 this.inputEl().on('change', function(){
19734 this.fireEvent('select', this);
19739 clearIOSView: function()
19741 this.inputEl().dom.innerHTML = '';
19743 this.ios_options = [];
19746 setIOSValue: function(v)
19750 if(!this.ios_options){
19754 Roo.each(this.ios_options, function(opts){
19756 opts.el.dom.removeAttribute('selected');
19758 if(opts.data[this.valueField] != v){
19762 opts.el.dom.setAttribute('selected', true);
19768 * @cfg {Boolean} grow
19772 * @cfg {Number} growMin
19776 * @cfg {Number} growMax
19785 Roo.apply(Roo.bootstrap.form.ComboBox, {
19789 cls: 'modal-header',
19811 cls: 'list-group-item',
19815 cls: 'roo-combobox-list-group-item-value'
19819 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19833 listItemCheckbox : {
19835 cls: 'list-group-item',
19839 cls: 'roo-combobox-list-group-item-value'
19843 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19859 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19864 cls: 'modal-footer',
19872 cls: 'col-xs-6 text-left',
19875 cls: 'btn btn-danger roo-touch-view-cancel',
19881 cls: 'col-xs-6 text-right',
19884 cls: 'btn btn-success roo-touch-view-ok',
19895 Roo.apply(Roo.bootstrap.form.ComboBox, {
19897 touchViewTemplate : {
19899 cls: 'modal fade roo-combobox-touch-view',
19903 cls: 'modal-dialog',
19904 style : 'position:fixed', // we have to fix position....
19908 cls: 'modal-content',
19910 Roo.bootstrap.form.ComboBox.header,
19911 Roo.bootstrap.form.ComboBox.body,
19912 Roo.bootstrap.form.ComboBox.footer
19921 * Ext JS Library 1.1.1
19922 * Copyright(c) 2006-2007, Ext JS, LLC.
19924 * Originally Released Under LGPL - original licence link has changed is not relivant.
19927 * <script type="text/javascript">
19932 * @extends Roo.util.Observable
19933 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
19934 * This class also supports single and multi selection modes. <br>
19935 * Create a data model bound view:
19937 var store = new Roo.data.Store(...);
19939 var view = new Roo.View({
19941 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
19943 singleSelect: true,
19944 selectedClass: "ydataview-selected",
19948 // listen for node click?
19949 view.on("click", function(vw, index, node, e){
19950 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19954 dataModel.load("foobar.xml");
19956 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19958 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19959 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19961 * Note: old style constructor is still suported (container, template, config)
19964 * Create a new View
19965 * @param {Object} config The config object
19968 Roo.View = function(config, depreciated_tpl, depreciated_config){
19970 this.parent = false;
19972 if (typeof(depreciated_tpl) == 'undefined') {
19973 // new way.. - universal constructor.
19974 Roo.apply(this, config);
19975 this.el = Roo.get(this.el);
19978 this.el = Roo.get(config);
19979 this.tpl = depreciated_tpl;
19980 Roo.apply(this, depreciated_config);
19982 this.wrapEl = this.el.wrap().wrap();
19983 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19986 if(typeof(this.tpl) == "string"){
19987 this.tpl = new Roo.Template(this.tpl);
19989 // support xtype ctors..
19990 this.tpl = new Roo.factory(this.tpl, Roo);
19994 this.tpl.compile();
19999 * @event beforeclick
20000 * Fires before a click is processed. Returns false to cancel the default action.
20001 * @param {Roo.View} this
20002 * @param {Number} index The index of the target node
20003 * @param {HTMLElement} node The target node
20004 * @param {Roo.EventObject} e The raw event object
20006 "beforeclick" : true,
20009 * Fires when a template node is clicked.
20010 * @param {Roo.View} this
20011 * @param {Number} index The index of the target node
20012 * @param {HTMLElement} node The target node
20013 * @param {Roo.EventObject} e The raw event object
20018 * Fires when a template node is double clicked.
20019 * @param {Roo.View} this
20020 * @param {Number} index The index of the target node
20021 * @param {HTMLElement} node The target node
20022 * @param {Roo.EventObject} e The raw event object
20026 * @event contextmenu
20027 * Fires when a template node is right clicked.
20028 * @param {Roo.View} this
20029 * @param {Number} index The index of the target node
20030 * @param {HTMLElement} node The target node
20031 * @param {Roo.EventObject} e The raw event object
20033 "contextmenu" : true,
20035 * @event selectionchange
20036 * Fires when the selected nodes change.
20037 * @param {Roo.View} this
20038 * @param {Array} selections Array of the selected nodes
20040 "selectionchange" : true,
20043 * @event beforeselect
20044 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
20045 * @param {Roo.View} this
20046 * @param {HTMLElement} node The node to be selected
20047 * @param {Array} selections Array of currently selected nodes
20049 "beforeselect" : true,
20051 * @event preparedata
20052 * Fires on every row to render, to allow you to change the data.
20053 * @param {Roo.View} this
20054 * @param {Object} data to be rendered (change this)
20056 "preparedata" : true
20064 "click": this.onClick,
20065 "dblclick": this.onDblClick,
20066 "contextmenu": this.onContextMenu,
20070 this.selections = [];
20072 this.cmp = new Roo.CompositeElementLite([]);
20074 this.store = Roo.factory(this.store, Roo.data);
20075 this.setStore(this.store, true);
20078 if ( this.footer && this.footer.xtype) {
20080 var fctr = this.wrapEl.appendChild(document.createElement("div"));
20082 this.footer.dataSource = this.store;
20083 this.footer.container = fctr;
20084 this.footer = Roo.factory(this.footer, Roo);
20085 fctr.insertFirst(this.el);
20087 // this is a bit insane - as the paging toolbar seems to detach the el..
20088 // dom.parentNode.parentNode.parentNode
20089 // they get detached?
20093 Roo.View.superclass.constructor.call(this);
20098 Roo.extend(Roo.View, Roo.util.Observable, {
20101 * @cfg {Roo.data.Store} store Data store to load data from.
20106 * @cfg {String|Roo.Element} el The container element.
20111 * @cfg {String|Roo.Template} tpl The template used by this View
20115 * @cfg {String} dataName the named area of the template to use as the data area
20116 * Works with domtemplates roo-name="name"
20120 * @cfg {String} selectedClass The css class to add to selected nodes
20122 selectedClass : "x-view-selected",
20124 * @cfg {String} emptyText The empty text to show when nothing is loaded.
20129 * @cfg {String} text to display on mask (default Loading)
20133 * @cfg {Boolean} multiSelect Allow multiple selection
20135 multiSelect : false,
20137 * @cfg {Boolean} singleSelect Allow single selection
20139 singleSelect: false,
20142 * @cfg {Boolean} toggleSelect - selecting
20144 toggleSelect : false,
20147 * @cfg {Boolean} tickable - selecting
20152 * Returns the element this view is bound to.
20153 * @return {Roo.Element}
20155 getEl : function(){
20156 return this.wrapEl;
20162 * Refreshes the view. - called by datachanged on the store. - do not call directly.
20164 refresh : function(){
20165 //Roo.log('refresh');
20168 // if we are using something like 'domtemplate', then
20169 // the what gets used is:
20170 // t.applySubtemplate(NAME, data, wrapping data..)
20171 // the outer template then get' applied with
20172 // the store 'extra data'
20173 // and the body get's added to the
20174 // roo-name="data" node?
20175 // <span class='roo-tpl-{name}'></span> ?????
20179 this.clearSelections();
20180 this.el.update("");
20182 var records = this.store.getRange();
20183 if(records.length < 1) {
20185 // is this valid?? = should it render a template??
20187 this.el.update(this.emptyText);
20191 if (this.dataName) {
20192 this.el.update(t.apply(this.store.meta)); //????
20193 el = this.el.child('.roo-tpl-' + this.dataName);
20196 for(var i = 0, len = records.length; i < len; i++){
20197 var data = this.prepareData(records[i].data, i, records[i]);
20198 this.fireEvent("preparedata", this, data, i, records[i]);
20200 var d = Roo.apply({}, data);
20203 Roo.apply(d, {'roo-id' : Roo.id()});
20207 Roo.each(this.parent.item, function(item){
20208 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
20211 Roo.apply(d, {'roo-data-checked' : 'checked'});
20215 html[html.length] = Roo.util.Format.trim(
20217 t.applySubtemplate(this.dataName, d, this.store.meta) :
20224 el.update(html.join(""));
20225 this.nodes = el.dom.childNodes;
20226 this.updateIndexes(0);
20231 * Function to override to reformat the data that is sent to
20232 * the template for each node.
20233 * DEPRICATED - use the preparedata event handler.
20234 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
20235 * a JSON object for an UpdateManager bound view).
20237 prepareData : function(data, index, record)
20239 this.fireEvent("preparedata", this, data, index, record);
20243 onUpdate : function(ds, record){
20244 // Roo.log('on update');
20245 this.clearSelections();
20246 var index = this.store.indexOf(record);
20247 var n = this.nodes[index];
20248 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
20249 n.parentNode.removeChild(n);
20250 this.updateIndexes(index, index);
20256 onAdd : function(ds, records, index)
20258 //Roo.log(['on Add', ds, records, index] );
20259 this.clearSelections();
20260 if(this.nodes.length == 0){
20264 var n = this.nodes[index];
20265 for(var i = 0, len = records.length; i < len; i++){
20266 var d = this.prepareData(records[i].data, i, records[i]);
20268 this.tpl.insertBefore(n, d);
20271 this.tpl.append(this.el, d);
20274 this.updateIndexes(index);
20277 onRemove : function(ds, record, index){
20278 // Roo.log('onRemove');
20279 this.clearSelections();
20280 var el = this.dataName ?
20281 this.el.child('.roo-tpl-' + this.dataName) :
20284 el.dom.removeChild(this.nodes[index]);
20285 this.updateIndexes(index);
20289 * Refresh an individual node.
20290 * @param {Number} index
20292 refreshNode : function(index){
20293 this.onUpdate(this.store, this.store.getAt(index));
20296 updateIndexes : function(startIndex, endIndex){
20297 var ns = this.nodes;
20298 startIndex = startIndex || 0;
20299 endIndex = endIndex || ns.length - 1;
20300 for(var i = startIndex; i <= endIndex; i++){
20301 ns[i].nodeIndex = i;
20306 * Changes the data store this view uses and refresh the view.
20307 * @param {Store} store
20309 setStore : function(store, initial){
20310 if(!initial && this.store){
20311 this.store.un("datachanged", this.refresh);
20312 this.store.un("add", this.onAdd);
20313 this.store.un("remove", this.onRemove);
20314 this.store.un("update", this.onUpdate);
20315 this.store.un("clear", this.refresh);
20316 this.store.un("beforeload", this.onBeforeLoad);
20317 this.store.un("load", this.onLoad);
20318 this.store.un("loadexception", this.onLoad);
20322 store.on("datachanged", this.refresh, this);
20323 store.on("add", this.onAdd, this);
20324 store.on("remove", this.onRemove, this);
20325 store.on("update", this.onUpdate, this);
20326 store.on("clear", this.refresh, this);
20327 store.on("beforeload", this.onBeforeLoad, this);
20328 store.on("load", this.onLoad, this);
20329 store.on("loadexception", this.onLoad, this);
20337 * onbeforeLoad - masks the loading area.
20340 onBeforeLoad : function(store,opts)
20342 //Roo.log('onBeforeLoad');
20344 this.el.update("");
20346 this.el.mask(this.mask ? this.mask : "Loading" );
20348 onLoad : function ()
20355 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
20356 * @param {HTMLElement} node
20357 * @return {HTMLElement} The template node
20359 findItemFromChild : function(node){
20360 var el = this.dataName ?
20361 this.el.child('.roo-tpl-' + this.dataName,true) :
20364 if(!node || node.parentNode == el){
20367 var p = node.parentNode;
20368 while(p && p != el){
20369 if(p.parentNode == el){
20378 onClick : function(e){
20379 var item = this.findItemFromChild(e.getTarget());
20381 var index = this.indexOf(item);
20382 if(this.onItemClick(item, index, e) !== false){
20383 this.fireEvent("click", this, index, item, e);
20386 this.clearSelections();
20391 onContextMenu : function(e){
20392 var item = this.findItemFromChild(e.getTarget());
20394 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
20399 onDblClick : function(e){
20400 var item = this.findItemFromChild(e.getTarget());
20402 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
20406 onItemClick : function(item, index, e)
20408 if(this.fireEvent("beforeclick", this, index, item, e) === false){
20411 if (this.toggleSelect) {
20412 var m = this.isSelected(item) ? 'unselect' : 'select';
20415 _t[m](item, true, false);
20418 if(this.multiSelect || this.singleSelect){
20419 if(this.multiSelect && e.shiftKey && this.lastSelection){
20420 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
20422 this.select(item, this.multiSelect && e.ctrlKey);
20423 this.lastSelection = item;
20426 if(!this.tickable){
20427 e.preventDefault();
20435 * Get the number of selected nodes.
20438 getSelectionCount : function(){
20439 return this.selections.length;
20443 * Get the currently selected nodes.
20444 * @return {Array} An array of HTMLElements
20446 getSelectedNodes : function(){
20447 return this.selections;
20451 * Get the indexes of the selected nodes.
20454 getSelectedIndexes : function(){
20455 var indexes = [], s = this.selections;
20456 for(var i = 0, len = s.length; i < len; i++){
20457 indexes.push(s[i].nodeIndex);
20463 * Clear all selections
20464 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
20466 clearSelections : function(suppressEvent){
20467 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
20468 this.cmp.elements = this.selections;
20469 this.cmp.removeClass(this.selectedClass);
20470 this.selections = [];
20471 if(!suppressEvent){
20472 this.fireEvent("selectionchange", this, this.selections);
20478 * Returns true if the passed node is selected
20479 * @param {HTMLElement/Number} node The node or node index
20480 * @return {Boolean}
20482 isSelected : function(node){
20483 var s = this.selections;
20487 node = this.getNode(node);
20488 return s.indexOf(node) !== -1;
20493 * @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
20494 * @param {Boolean} keepExisting (optional) true to keep existing selections
20495 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20497 select : function(nodeInfo, keepExisting, suppressEvent){
20498 if(nodeInfo instanceof Array){
20500 this.clearSelections(true);
20502 for(var i = 0, len = nodeInfo.length; i < len; i++){
20503 this.select(nodeInfo[i], true, true);
20507 var node = this.getNode(nodeInfo);
20508 if(!node || this.isSelected(node)){
20509 return; // already selected.
20512 this.clearSelections(true);
20515 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20516 Roo.fly(node).addClass(this.selectedClass);
20517 this.selections.push(node);
20518 if(!suppressEvent){
20519 this.fireEvent("selectionchange", this, this.selections);
20527 * @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
20528 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20529 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20531 unselect : function(nodeInfo, keepExisting, suppressEvent)
20533 if(nodeInfo instanceof Array){
20534 Roo.each(this.selections, function(s) {
20535 this.unselect(s, nodeInfo);
20539 var node = this.getNode(nodeInfo);
20540 if(!node || !this.isSelected(node)){
20541 //Roo.log("not selected");
20542 return; // not selected.
20546 Roo.each(this.selections, function(s) {
20548 Roo.fly(node).removeClass(this.selectedClass);
20555 this.selections= ns;
20556 this.fireEvent("selectionchange", this, this.selections);
20560 * Gets a template node.
20561 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20562 * @return {HTMLElement} The node or null if it wasn't found
20564 getNode : function(nodeInfo){
20565 if(typeof nodeInfo == "string"){
20566 return document.getElementById(nodeInfo);
20567 }else if(typeof nodeInfo == "number"){
20568 return this.nodes[nodeInfo];
20574 * Gets a range template nodes.
20575 * @param {Number} startIndex
20576 * @param {Number} endIndex
20577 * @return {Array} An array of nodes
20579 getNodes : function(start, end){
20580 var ns = this.nodes;
20581 start = start || 0;
20582 end = typeof end == "undefined" ? ns.length - 1 : end;
20585 for(var i = start; i <= end; i++){
20589 for(var i = start; i >= end; i--){
20597 * Finds the index of the passed node
20598 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20599 * @return {Number} The index of the node or -1
20601 indexOf : function(node){
20602 node = this.getNode(node);
20603 if(typeof node.nodeIndex == "number"){
20604 return node.nodeIndex;
20606 var ns = this.nodes;
20607 for(var i = 0, len = ns.length; i < len; i++){
20618 * based on jquery fullcalendar
20622 Roo.bootstrap = Roo.bootstrap || {};
20624 * @class Roo.bootstrap.Calendar
20625 * @extends Roo.bootstrap.Component
20626 * Bootstrap Calendar class
20627 * @cfg {Boolean} loadMask (true|false) default false
20628 * @cfg {Object} header generate the user specific header of the calendar, default false
20631 * Create a new Container
20632 * @param {Object} config The config object
20637 Roo.bootstrap.Calendar = function(config){
20638 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20642 * Fires when a date is selected
20643 * @param {DatePicker} this
20644 * @param {Date} date The selected date
20648 * @event monthchange
20649 * Fires when the displayed month changes
20650 * @param {DatePicker} this
20651 * @param {Date} date The selected month
20653 'monthchange': true,
20655 * @event evententer
20656 * Fires when mouse over an event
20657 * @param {Calendar} this
20658 * @param {event} Event
20660 'evententer': true,
20662 * @event eventleave
20663 * Fires when the mouse leaves an
20664 * @param {Calendar} this
20667 'eventleave': true,
20669 * @event eventclick
20670 * Fires when the mouse click an
20671 * @param {Calendar} this
20680 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
20683 * @cfg {Roo.data.Store} store
20684 * The data source for the calendar
20688 * @cfg {Number} startDay
20689 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20697 getAutoCreate : function(){
20700 var fc_button = function(name, corner, style, content ) {
20701 return Roo.apply({},{
20703 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
20705 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20708 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20719 style : 'width:100%',
20726 cls : 'fc-header-left',
20728 fc_button('prev', 'left', 'arrow', '‹' ),
20729 fc_button('next', 'right', 'arrow', '›' ),
20730 { tag: 'span', cls: 'fc-header-space' },
20731 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
20739 cls : 'fc-header-center',
20743 cls: 'fc-header-title',
20746 html : 'month / year'
20754 cls : 'fc-header-right',
20756 /* fc_button('month', 'left', '', 'month' ),
20757 fc_button('week', '', '', 'week' ),
20758 fc_button('day', 'right', '', 'day' )
20770 header = this.header;
20773 var cal_heads = function() {
20775 // fixme - handle this.
20777 for (var i =0; i < Date.dayNames.length; i++) {
20778 var d = Date.dayNames[i];
20781 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20782 html : d.substring(0,3)
20786 ret[0].cls += ' fc-first';
20787 ret[6].cls += ' fc-last';
20790 var cal_cell = function(n) {
20793 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20798 cls: 'fc-day-number',
20802 cls: 'fc-day-content',
20806 style: 'position: relative;' // height: 17px;
20818 var cal_rows = function() {
20821 for (var r = 0; r < 6; r++) {
20828 for (var i =0; i < Date.dayNames.length; i++) {
20829 var d = Date.dayNames[i];
20830 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20833 row.cn[0].cls+=' fc-first';
20834 row.cn[0].cn[0].style = 'min-height:90px';
20835 row.cn[6].cls+=' fc-last';
20839 ret[0].cls += ' fc-first';
20840 ret[4].cls += ' fc-prev-last';
20841 ret[5].cls += ' fc-last';
20848 cls: 'fc-border-separate',
20849 style : 'width:100%',
20857 cls : 'fc-first fc-last',
20875 cls : 'fc-content',
20876 style : "position: relative;",
20879 cls : 'fc-view fc-view-month fc-grid',
20880 style : 'position: relative',
20881 unselectable : 'on',
20884 cls : 'fc-event-container',
20885 style : 'position:absolute;z-index:8;top:0;left:0;'
20903 initEvents : function()
20906 throw "can not find store for calendar";
20912 style: "text-align:center",
20916 style: "background-color:white;width:50%;margin:250 auto",
20920 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
20931 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20933 var size = this.el.select('.fc-content', true).first().getSize();
20934 this.maskEl.setSize(size.width, size.height);
20935 this.maskEl.enableDisplayMode("block");
20936 if(!this.loadMask){
20937 this.maskEl.hide();
20940 this.store = Roo.factory(this.store, Roo.data);
20941 this.store.on('load', this.onLoad, this);
20942 this.store.on('beforeload', this.onBeforeLoad, this);
20946 this.cells = this.el.select('.fc-day',true);
20947 //Roo.log(this.cells);
20948 this.textNodes = this.el.query('.fc-day-number');
20949 this.cells.addClassOnOver('fc-state-hover');
20951 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20952 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20953 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20954 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20956 this.on('monthchange', this.onMonthChange, this);
20958 this.update(new Date().clearTime());
20961 resize : function() {
20962 var sz = this.el.getSize();
20964 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20965 this.el.select('.fc-day-content div',true).setHeight(34);
20970 showPrevMonth : function(e){
20971 this.update(this.activeDate.add("mo", -1));
20973 showToday : function(e){
20974 this.update(new Date().clearTime());
20977 showNextMonth : function(e){
20978 this.update(this.activeDate.add("mo", 1));
20982 showPrevYear : function(){
20983 this.update(this.activeDate.add("y", -1));
20987 showNextYear : function(){
20988 this.update(this.activeDate.add("y", 1));
20993 update : function(date)
20995 var vd = this.activeDate;
20996 this.activeDate = date;
20997 // if(vd && this.el){
20998 // var t = date.getTime();
20999 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
21000 // Roo.log('using add remove');
21002 // this.fireEvent('monthchange', this, date);
21004 // this.cells.removeClass("fc-state-highlight");
21005 // this.cells.each(function(c){
21006 // if(c.dateValue == t){
21007 // c.addClass("fc-state-highlight");
21008 // setTimeout(function(){
21009 // try{c.dom.firstChild.focus();}catch(e){}
21019 var days = date.getDaysInMonth();
21021 var firstOfMonth = date.getFirstDateOfMonth();
21022 var startingPos = firstOfMonth.getDay()-this.startDay;
21024 if(startingPos < this.startDay){
21028 var pm = date.add(Date.MONTH, -1);
21029 var prevStart = pm.getDaysInMonth()-startingPos;
21031 this.cells = this.el.select('.fc-day',true);
21032 this.textNodes = this.el.query('.fc-day-number');
21033 this.cells.addClassOnOver('fc-state-hover');
21035 var cells = this.cells.elements;
21036 var textEls = this.textNodes;
21038 Roo.each(cells, function(cell){
21039 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
21042 days += startingPos;
21044 // convert everything to numbers so it's fast
21045 var day = 86400000;
21046 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
21049 //Roo.log(prevStart);
21051 var today = new Date().clearTime().getTime();
21052 var sel = date.clearTime().getTime();
21053 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
21054 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
21055 var ddMatch = this.disabledDatesRE;
21056 var ddText = this.disabledDatesText;
21057 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
21058 var ddaysText = this.disabledDaysText;
21059 var format = this.format;
21061 var setCellClass = function(cal, cell){
21065 //Roo.log('set Cell Class');
21067 var t = d.getTime();
21071 cell.dateValue = t;
21073 cell.className += " fc-today";
21074 cell.className += " fc-state-highlight";
21075 cell.title = cal.todayText;
21078 // disable highlight in other month..
21079 //cell.className += " fc-state-highlight";
21084 cell.className = " fc-state-disabled";
21085 cell.title = cal.minText;
21089 cell.className = " fc-state-disabled";
21090 cell.title = cal.maxText;
21094 if(ddays.indexOf(d.getDay()) != -1){
21095 cell.title = ddaysText;
21096 cell.className = " fc-state-disabled";
21099 if(ddMatch && format){
21100 var fvalue = d.dateFormat(format);
21101 if(ddMatch.test(fvalue)){
21102 cell.title = ddText.replace("%0", fvalue);
21103 cell.className = " fc-state-disabled";
21107 if (!cell.initialClassName) {
21108 cell.initialClassName = cell.dom.className;
21111 cell.dom.className = cell.initialClassName + ' ' + cell.className;
21116 for(; i < startingPos; i++) {
21117 textEls[i].innerHTML = (++prevStart);
21118 d.setDate(d.getDate()+1);
21120 cells[i].className = "fc-past fc-other-month";
21121 setCellClass(this, cells[i]);
21126 for(; i < days; i++){
21127 intDay = i - startingPos + 1;
21128 textEls[i].innerHTML = (intDay);
21129 d.setDate(d.getDate()+1);
21131 cells[i].className = ''; // "x-date-active";
21132 setCellClass(this, cells[i]);
21136 for(; i < 42; i++) {
21137 textEls[i].innerHTML = (++extraDays);
21138 d.setDate(d.getDate()+1);
21140 cells[i].className = "fc-future fc-other-month";
21141 setCellClass(this, cells[i]);
21144 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
21146 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
21148 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
21149 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
21151 if(totalRows != 6){
21152 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
21153 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
21156 this.fireEvent('monthchange', this, date);
21160 if(!this.internalRender){
21161 var main = this.el.dom.firstChild;
21162 var w = main.offsetWidth;
21163 this.el.setWidth(w + this.el.getBorderWidth("lr"));
21164 Roo.fly(main).setWidth(w);
21165 this.internalRender = true;
21166 // opera does not respect the auto grow header center column
21167 // then, after it gets a width opera refuses to recalculate
21168 // without a second pass
21169 if(Roo.isOpera && !this.secondPass){
21170 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
21171 this.secondPass = true;
21172 this.update.defer(10, this, [date]);
21179 findCell : function(dt) {
21180 dt = dt.clearTime().getTime();
21182 this.cells.each(function(c){
21183 //Roo.log("check " +c.dateValue + '?=' + dt);
21184 if(c.dateValue == dt){
21194 findCells : function(ev) {
21195 var s = ev.start.clone().clearTime().getTime();
21197 var e= ev.end.clone().clearTime().getTime();
21200 this.cells.each(function(c){
21201 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
21203 if(c.dateValue > e){
21206 if(c.dateValue < s){
21215 // findBestRow: function(cells)
21219 // for (var i =0 ; i < cells.length;i++) {
21220 // ret = Math.max(cells[i].rows || 0,ret);
21227 addItem : function(ev)
21229 // look for vertical location slot in
21230 var cells = this.findCells(ev);
21232 // ev.row = this.findBestRow(cells);
21234 // work out the location.
21238 for(var i =0; i < cells.length; i++) {
21240 cells[i].row = cells[0].row;
21243 cells[i].row = cells[i].row + 1;
21253 if (crow.start.getY() == cells[i].getY()) {
21255 crow.end = cells[i];
21272 cells[0].events.push(ev);
21274 this.calevents.push(ev);
21277 clearEvents: function() {
21279 if(!this.calevents){
21283 Roo.each(this.cells.elements, function(c){
21289 Roo.each(this.calevents, function(e) {
21290 Roo.each(e.els, function(el) {
21291 el.un('mouseenter' ,this.onEventEnter, this);
21292 el.un('mouseleave' ,this.onEventLeave, this);
21297 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
21303 renderEvents: function()
21307 this.cells.each(function(c) {
21316 if(c.row != c.events.length){
21317 r = 4 - (4 - (c.row - c.events.length));
21320 c.events = ev.slice(0, r);
21321 c.more = ev.slice(r);
21323 if(c.more.length && c.more.length == 1){
21324 c.events.push(c.more.pop());
21327 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
21331 this.cells.each(function(c) {
21333 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
21336 for (var e = 0; e < c.events.length; e++){
21337 var ev = c.events[e];
21338 var rows = ev.rows;
21340 for(var i = 0; i < rows.length; i++) {
21342 // how many rows should it span..
21345 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
21346 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
21348 unselectable : "on",
21351 cls: 'fc-event-inner',
21355 // cls: 'fc-event-time',
21356 // html : cells.length > 1 ? '' : ev.time
21360 cls: 'fc-event-title',
21361 html : String.format('{0}', ev.title)
21368 cls: 'ui-resizable-handle ui-resizable-e',
21369 html : '  '
21376 cfg.cls += ' fc-event-start';
21378 if ((i+1) == rows.length) {
21379 cfg.cls += ' fc-event-end';
21382 var ctr = _this.el.select('.fc-event-container',true).first();
21383 var cg = ctr.createChild(cfg);
21385 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
21386 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
21388 var r = (c.more.length) ? 1 : 0;
21389 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
21390 cg.setWidth(ebox.right - sbox.x -2);
21392 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
21393 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
21394 cg.on('click', _this.onEventClick, _this, ev);
21405 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
21406 style : 'position: absolute',
21407 unselectable : "on",
21410 cls: 'fc-event-inner',
21414 cls: 'fc-event-title',
21422 cls: 'ui-resizable-handle ui-resizable-e',
21423 html : '  '
21429 var ctr = _this.el.select('.fc-event-container',true).first();
21430 var cg = ctr.createChild(cfg);
21432 var sbox = c.select('.fc-day-content',true).first().getBox();
21433 var ebox = c.select('.fc-day-content',true).first().getBox();
21435 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
21436 cg.setWidth(ebox.right - sbox.x -2);
21438 cg.on('click', _this.onMoreEventClick, _this, c.more);
21448 onEventEnter: function (e, el,event,d) {
21449 this.fireEvent('evententer', this, el, event);
21452 onEventLeave: function (e, el,event,d) {
21453 this.fireEvent('eventleave', this, el, event);
21456 onEventClick: function (e, el,event,d) {
21457 this.fireEvent('eventclick', this, el, event);
21460 onMonthChange: function () {
21464 onMoreEventClick: function(e, el, more)
21468 this.calpopover.placement = 'right';
21469 this.calpopover.setTitle('More');
21471 this.calpopover.setContent('');
21473 var ctr = this.calpopover.el.select('.popover-content', true).first();
21475 Roo.each(more, function(m){
21477 cls : 'fc-event-hori fc-event-draggable',
21480 var cg = ctr.createChild(cfg);
21482 cg.on('click', _this.onEventClick, _this, m);
21485 this.calpopover.show(el);
21490 onLoad: function ()
21492 this.calevents = [];
21495 if(this.store.getCount() > 0){
21496 this.store.data.each(function(d){
21499 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21500 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21501 time : d.data.start_time,
21502 title : d.data.title,
21503 description : d.data.description,
21504 venue : d.data.venue
21509 this.renderEvents();
21511 if(this.calevents.length && this.loadMask){
21512 this.maskEl.hide();
21516 onBeforeLoad: function()
21518 this.clearEvents();
21520 this.maskEl.show();
21534 * @class Roo.bootstrap.Popover
21535 * @extends Roo.bootstrap.Component
21536 * @parent none builder
21537 * @children Roo.bootstrap.Component
21538 * Bootstrap Popover class
21539 * @cfg {String} html contents of the popover (or false to use children..)
21540 * @cfg {String} title of popover (or false to hide)
21541 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21542 * @cfg {String} trigger click || hover (or false to trigger manually)
21543 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21544 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21545 * - if false and it has a 'parent' then it will be automatically added to that element
21546 * - if string - Roo.get will be called
21547 * @cfg {Number} delay - delay before showing
21550 * Create a new Popover
21551 * @param {Object} config The config object
21554 Roo.bootstrap.Popover = function(config){
21555 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21561 * After the popover show
21563 * @param {Roo.bootstrap.Popover} this
21568 * After the popover hide
21570 * @param {Roo.bootstrap.Popover} this
21576 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
21581 placement : 'right',
21582 trigger : 'hover', // hover
21588 can_build_overlaid : false,
21590 maskEl : false, // the mask element
21593 alignEl : false, // when show is called with an element - this get's stored.
21595 getChildContainer : function()
21597 return this.contentEl;
21600 getPopoverHeader : function()
21602 this.title = true; // flag not to hide it..
21603 this.headerEl.addClass('p-0');
21604 return this.headerEl
21608 getAutoCreate : function(){
21611 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21612 style: 'display:block',
21618 cls : 'popover-inner ',
21622 cls: 'popover-title popover-header',
21623 html : this.title === false ? '' : this.title
21626 cls : 'popover-content popover-body ' + (this.cls || ''),
21627 html : this.html || ''
21638 * @param {string} the title
21640 setTitle: function(str)
21644 this.headerEl.dom.innerHTML = str;
21649 * @param {string} the body content
21651 setContent: function(str)
21654 if (this.contentEl) {
21655 this.contentEl.dom.innerHTML = str;
21659 // as it get's added to the bottom of the page.
21660 onRender : function(ct, position)
21662 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21667 var cfg = Roo.apply({}, this.getAutoCreate());
21671 cfg.cls += ' ' + this.cls;
21674 cfg.style = this.style;
21676 //Roo.log("adding to ");
21677 this.el = Roo.get(document.body).createChild(cfg, position);
21678 // Roo.log(this.el);
21681 this.contentEl = this.el.select('.popover-content',true).first();
21682 this.headerEl = this.el.select('.popover-title',true).first();
21685 if(typeof(this.items) != 'undefined'){
21686 var items = this.items;
21689 for(var i =0;i < items.length;i++) {
21690 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21694 this.items = nitems;
21696 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21697 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21704 resizeMask : function()
21706 this.maskEl.setSize(
21707 Roo.lib.Dom.getViewWidth(true),
21708 Roo.lib.Dom.getViewHeight(true)
21712 initEvents : function()
21716 Roo.bootstrap.Popover.register(this);
21719 this.arrowEl = this.el.select('.arrow',true).first();
21720 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21721 this.el.enableDisplayMode('block');
21725 if (this.over === false && !this.parent()) {
21728 if (this.triggers === false) {
21733 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21734 var triggers = this.trigger ? this.trigger.split(' ') : [];
21735 Roo.each(triggers, function(trigger) {
21737 if (trigger == 'click') {
21738 on_el.on('click', this.toggle, this);
21739 } else if (trigger != 'manual') {
21740 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
21741 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21743 on_el.on(eventIn ,this.enter, this);
21744 on_el.on(eventOut, this.leave, this);
21754 toggle : function () {
21755 this.hoverState == 'in' ? this.leave() : this.enter();
21758 enter : function () {
21760 clearTimeout(this.timeout);
21762 this.hoverState = 'in';
21764 if (!this.delay || !this.delay.show) {
21769 this.timeout = setTimeout(function () {
21770 if (_t.hoverState == 'in') {
21773 }, this.delay.show)
21776 leave : function() {
21777 clearTimeout(this.timeout);
21779 this.hoverState = 'out';
21781 if (!this.delay || !this.delay.hide) {
21786 this.timeout = setTimeout(function () {
21787 if (_t.hoverState == 'out') {
21790 }, this.delay.hide)
21794 * update the position of the dialog
21795 * normally this is needed if the popover get's bigger - due to a Table reload etc..
21800 doAlign : function()
21803 if (this.alignEl) {
21804 this.updatePosition(this.placement, true);
21807 // this is usually just done by the builder = to show the popoup in the middle of the scren.
21808 var es = this.el.getSize();
21809 var x = Roo.lib.Dom.getViewWidth()/2;
21810 var y = Roo.lib.Dom.getViewHeight()/2;
21811 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
21823 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21824 * @param {string} (left|right|top|bottom) position
21826 show : function (on_el, placement)
21828 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
21829 on_el = on_el || false; // default to false
21832 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21833 on_el = this.parent().el;
21834 } else if (this.over) {
21835 on_el = Roo.get(this.over);
21840 this.alignEl = Roo.get( on_el );
21843 this.render(document.body);
21849 if (this.title === false) {
21850 this.headerEl.hide();
21855 this.el.dom.style.display = 'block';
21859 //var arrow = this.el.select('.arrow',true).first();
21860 //arrow.set(align[2],
21862 this.el.addClass('in');
21866 this.hoverState = 'in';
21869 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
21870 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21871 this.maskEl.dom.style.display = 'block';
21872 this.maskEl.addClass('show');
21874 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21876 this.fireEvent('show', this);
21880 * fire this manually after loading a grid in the table for example
21881 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21882 * @param {Boolean} try and move it if we cant get right position.
21884 updatePosition : function(placement, try_move)
21886 // allow for calling with no parameters
21887 placement = placement ? placement : this.placement;
21888 try_move = typeof(try_move) == 'undefined' ? true : try_move;
21890 this.el.removeClass([
21891 'fade','top','bottom', 'left', 'right','in',
21892 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21894 this.el.addClass(placement + ' bs-popover-' + placement);
21896 if (!this.alignEl ) {
21900 switch (placement) {
21902 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21903 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21904 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21905 //normal display... or moved up/down.
21906 this.el.setXY(offset);
21907 var xy = this.alignEl.getAnchorXY('tr', false);
21909 this.arrowEl.setXY(xy);
21912 // continue through...
21913 return this.updatePosition('left', false);
21917 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21918 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21919 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21920 //normal display... or moved up/down.
21921 this.el.setXY(offset);
21922 var xy = this.alignEl.getAnchorXY('tl', false);
21923 xy[0]-=10;xy[1]+=5; // << fix me
21924 this.arrowEl.setXY(xy);
21928 return this.updatePosition('right', false);
21931 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21932 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21933 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21934 //normal display... or moved up/down.
21935 this.el.setXY(offset);
21936 var xy = this.alignEl.getAnchorXY('t', false);
21937 xy[1]-=10; // << fix me
21938 this.arrowEl.setXY(xy);
21942 return this.updatePosition('bottom', false);
21945 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21946 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21947 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21948 //normal display... or moved up/down.
21949 this.el.setXY(offset);
21950 var xy = this.alignEl.getAnchorXY('b', false);
21951 xy[1]+=2; // << fix me
21952 this.arrowEl.setXY(xy);
21956 return this.updatePosition('top', false);
21967 this.el.setXY([0,0]);
21968 this.el.removeClass('in');
21970 this.hoverState = null;
21971 this.maskEl.hide(); // always..
21972 this.fireEvent('hide', this);
21978 Roo.apply(Roo.bootstrap.Popover, {
21981 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21982 'right' : ['l-br', [10,0], 'right bs-popover-right'],
21983 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21984 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21989 clickHander : false,
21993 onMouseDown : function(e)
21995 if (this.popups.length && !e.getTarget(".roo-popover")) {
21996 /// what is nothing is showing..
22005 register : function(popup)
22007 if (!Roo.bootstrap.Popover.clickHandler) {
22008 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
22010 // hide other popups.
22011 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
22012 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
22013 this.hideAll(); //<< why?
22014 //this.popups.push(popup);
22016 hideAll : function()
22018 this.popups.forEach(function(p) {
22022 onShow : function() {
22023 Roo.bootstrap.Popover.popups.push(this);
22025 onHide : function() {
22026 Roo.bootstrap.Popover.popups.remove(this);
22031 * @class Roo.bootstrap.PopoverNav
22032 * @extends Roo.bootstrap.nav.Simplebar
22033 * @parent Roo.bootstrap.Popover
22034 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container
22036 * Bootstrap Popover header navigation class
22037 * FIXME? should this go under nav?
22041 * Create a new Popover Header Navigation
22042 * @param {Object} config The config object
22045 Roo.bootstrap.PopoverNav = function(config){
22046 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
22049 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.nav.Simplebar, {
22052 container_method : 'getPopoverHeader'
22070 * @class Roo.bootstrap.Progress
22071 * @extends Roo.bootstrap.Component
22072 * @children Roo.bootstrap.ProgressBar
22073 * Bootstrap Progress class
22074 * @cfg {Boolean} striped striped of the progress bar
22075 * @cfg {Boolean} active animated of the progress bar
22079 * Create a new Progress
22080 * @param {Object} config The config object
22083 Roo.bootstrap.Progress = function(config){
22084 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
22087 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
22092 getAutoCreate : function(){
22100 cfg.cls += ' progress-striped';
22104 cfg.cls += ' active';
22123 * @class Roo.bootstrap.ProgressBar
22124 * @extends Roo.bootstrap.Component
22125 * Bootstrap ProgressBar class
22126 * @cfg {Number} aria_valuenow aria-value now
22127 * @cfg {Number} aria_valuemin aria-value min
22128 * @cfg {Number} aria_valuemax aria-value max
22129 * @cfg {String} label label for the progress bar
22130 * @cfg {String} panel (success | info | warning | danger )
22131 * @cfg {String} role role of the progress bar
22132 * @cfg {String} sr_only text
22136 * Create a new ProgressBar
22137 * @param {Object} config The config object
22140 Roo.bootstrap.ProgressBar = function(config){
22141 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
22144 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
22148 aria_valuemax : 100,
22154 getAutoCreate : function()
22159 cls: 'progress-bar',
22160 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
22172 cfg.role = this.role;
22175 if(this.aria_valuenow){
22176 cfg['aria-valuenow'] = this.aria_valuenow;
22179 if(this.aria_valuemin){
22180 cfg['aria-valuemin'] = this.aria_valuemin;
22183 if(this.aria_valuemax){
22184 cfg['aria-valuemax'] = this.aria_valuemax;
22187 if(this.label && !this.sr_only){
22188 cfg.html = this.label;
22192 cfg.cls += ' progress-bar-' + this.panel;
22198 update : function(aria_valuenow)
22200 this.aria_valuenow = aria_valuenow;
22202 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
22210 * @class Roo.bootstrap.TabGroup
22211 * @extends Roo.bootstrap.Column
22212 * @children Roo.bootstrap.TabPanel
22213 * Bootstrap Column class
22214 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
22215 * @cfg {Boolean} carousel true to make the group behave like a carousel
22216 * @cfg {Boolean} bullets show bullets for the panels
22217 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
22218 * @cfg {Number} timer auto slide timer .. default 0 millisecond
22219 * @cfg {Boolean} showarrow (true|false) show arrow default true
22222 * Create a new TabGroup
22223 * @param {Object} config The config object
22226 Roo.bootstrap.TabGroup = function(config){
22227 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
22229 this.navId = Roo.id();
22232 Roo.bootstrap.TabGroup.register(this);
22236 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
22239 transition : false,
22244 slideOnTouch : false,
22247 getAutoCreate : function()
22249 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
22251 cfg.cls += ' tab-content';
22253 if (this.carousel) {
22254 cfg.cls += ' carousel slide';
22257 cls : 'carousel-inner',
22261 if(this.bullets && !Roo.isTouch){
22264 cls : 'carousel-bullets',
22268 if(this.bullets_cls){
22269 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
22276 cfg.cn[0].cn.push(bullets);
22279 if(this.showarrow){
22280 cfg.cn[0].cn.push({
22282 class : 'carousel-arrow',
22286 class : 'carousel-prev',
22290 class : 'fa fa-chevron-left'
22296 class : 'carousel-next',
22300 class : 'fa fa-chevron-right'
22313 initEvents: function()
22315 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
22316 // this.el.on("touchstart", this.onTouchStart, this);
22319 if(this.autoslide){
22322 this.slideFn = window.setInterval(function() {
22323 _this.showPanelNext();
22327 if(this.showarrow){
22328 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
22329 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
22335 // onTouchStart : function(e, el, o)
22337 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
22341 // this.showPanelNext();
22345 getChildContainer : function()
22347 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
22351 * register a Navigation item
22352 * @param {Roo.bootstrap.nav.Item} the navitem to add
22354 register : function(item)
22356 this.tabs.push( item);
22357 item.navId = this.navId; // not really needed..
22362 getActivePanel : function()
22365 Roo.each(this.tabs, function(t) {
22375 getPanelByName : function(n)
22378 Roo.each(this.tabs, function(t) {
22379 if (t.tabId == n) {
22387 indexOfPanel : function(p)
22390 Roo.each(this.tabs, function(t,i) {
22391 if (t.tabId == p.tabId) {
22400 * show a specific panel
22401 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
22402 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
22404 showPanel : function (pan)
22406 if(this.transition || typeof(pan) == 'undefined'){
22407 Roo.log("waiting for the transitionend");
22411 if (typeof(pan) == 'number') {
22412 pan = this.tabs[pan];
22415 if (typeof(pan) == 'string') {
22416 pan = this.getPanelByName(pan);
22419 var cur = this.getActivePanel();
22422 Roo.log('pan or acitve pan is undefined');
22426 if (pan.tabId == this.getActivePanel().tabId) {
22430 if (false === cur.fireEvent('beforedeactivate')) {
22434 if(this.bullets > 0 && !Roo.isTouch){
22435 this.setActiveBullet(this.indexOfPanel(pan));
22438 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
22440 //class="carousel-item carousel-item-next carousel-item-left"
22442 this.transition = true;
22443 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
22444 var lr = dir == 'next' ? 'left' : 'right';
22445 pan.el.addClass(dir); // or prev
22446 pan.el.addClass('carousel-item-' + dir); // or prev
22447 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
22448 cur.el.addClass(lr); // or right
22449 pan.el.addClass(lr);
22450 cur.el.addClass('carousel-item-' +lr); // or right
22451 pan.el.addClass('carousel-item-' +lr);
22455 cur.el.on('transitionend', function() {
22456 Roo.log("trans end?");
22458 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
22459 pan.setActive(true);
22461 cur.el.removeClass([lr, 'carousel-item-' + lr]);
22462 cur.setActive(false);
22464 _this.transition = false;
22466 }, this, { single: true } );
22471 cur.setActive(false);
22472 pan.setActive(true);
22477 showPanelNext : function()
22479 var i = this.indexOfPanel(this.getActivePanel());
22481 if (i >= this.tabs.length - 1 && !this.autoslide) {
22485 if (i >= this.tabs.length - 1 && this.autoslide) {
22489 this.showPanel(this.tabs[i+1]);
22492 showPanelPrev : function()
22494 var i = this.indexOfPanel(this.getActivePanel());
22496 if (i < 1 && !this.autoslide) {
22500 if (i < 1 && this.autoslide) {
22501 i = this.tabs.length;
22504 this.showPanel(this.tabs[i-1]);
22508 addBullet: function()
22510 if(!this.bullets || Roo.isTouch){
22513 var ctr = this.el.select('.carousel-bullets',true).first();
22514 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22515 var bullet = ctr.createChild({
22516 cls : 'bullet bullet-' + i
22517 },ctr.dom.lastChild);
22522 bullet.on('click', (function(e, el, o, ii, t){
22524 e.preventDefault();
22526 this.showPanel(ii);
22528 if(this.autoslide && this.slideFn){
22529 clearInterval(this.slideFn);
22530 this.slideFn = window.setInterval(function() {
22531 _this.showPanelNext();
22535 }).createDelegate(this, [i, bullet], true));
22540 setActiveBullet : function(i)
22546 Roo.each(this.el.select('.bullet', true).elements, function(el){
22547 el.removeClass('selected');
22550 var bullet = this.el.select('.bullet-' + i, true).first();
22556 bullet.addClass('selected');
22567 Roo.apply(Roo.bootstrap.TabGroup, {
22571 * register a Navigation Group
22572 * @param {Roo.bootstrap.nav.Group} the navgroup to add
22574 register : function(navgrp)
22576 this.groups[navgrp.navId] = navgrp;
22580 * fetch a Navigation Group based on the navigation ID
22581 * if one does not exist , it will get created.
22582 * @param {string} the navgroup to add
22583 * @returns {Roo.bootstrap.nav.Group} the navgroup
22585 get: function(navId) {
22586 if (typeof(this.groups[navId]) == 'undefined') {
22587 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22589 return this.groups[navId] ;
22604 * @class Roo.bootstrap.TabPanel
22605 * @extends Roo.bootstrap.Component
22606 * @children Roo.bootstrap.Component
22607 * Bootstrap TabPanel class
22608 * @cfg {Boolean} active panel active
22609 * @cfg {String} html panel content
22610 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22611 * @cfg {String} navId The Roo.bootstrap.nav.Group which triggers show hide ()
22612 * @cfg {String} href click to link..
22613 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22617 * Create a new TabPanel
22618 * @param {Object} config The config object
22621 Roo.bootstrap.TabPanel = function(config){
22622 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22626 * Fires when the active status changes
22627 * @param {Roo.bootstrap.TabPanel} this
22628 * @param {Boolean} state the new state
22633 * @event beforedeactivate
22634 * Fires before a tab is de-activated - can be used to do validation on a form.
22635 * @param {Roo.bootstrap.TabPanel} this
22636 * @return {Boolean} false if there is an error
22639 'beforedeactivate': true
22642 this.tabId = this.tabId || Roo.id();
22646 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
22653 touchSlide : false,
22654 getAutoCreate : function(){
22659 // item is needed for carousel - not sure if it has any effect otherwise
22660 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22661 html: this.html || ''
22665 cfg.cls += ' active';
22669 cfg.tabId = this.tabId;
22677 initEvents: function()
22679 var p = this.parent();
22681 this.navId = this.navId || p.navId;
22683 if (typeof(this.navId) != 'undefined') {
22684 // not really needed.. but just in case.. parent should be a NavGroup.
22685 var tg = Roo.bootstrap.TabGroup.get(this.navId);
22689 var i = tg.tabs.length - 1;
22691 if(this.active && tg.bullets > 0 && i < tg.bullets){
22692 tg.setActiveBullet(i);
22696 this.el.on('click', this.onClick, this);
22698 if(Roo.isTouch && this.touchSlide){
22699 this.el.on("touchstart", this.onTouchStart, this);
22700 this.el.on("touchmove", this.onTouchMove, this);
22701 this.el.on("touchend", this.onTouchEnd, this);
22706 onRender : function(ct, position)
22708 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22711 setActive : function(state)
22713 Roo.log("panel - set active " + this.tabId + "=" + state);
22715 this.active = state;
22717 this.el.removeClass('active');
22719 } else if (!this.el.hasClass('active')) {
22720 this.el.addClass('active');
22723 this.fireEvent('changed', this, state);
22726 onClick : function(e)
22728 e.preventDefault();
22730 if(!this.href.length){
22734 window.location.href = this.href;
22743 onTouchStart : function(e)
22745 this.swiping = false;
22747 this.startX = e.browserEvent.touches[0].clientX;
22748 this.startY = e.browserEvent.touches[0].clientY;
22751 onTouchMove : function(e)
22753 this.swiping = true;
22755 this.endX = e.browserEvent.touches[0].clientX;
22756 this.endY = e.browserEvent.touches[0].clientY;
22759 onTouchEnd : function(e)
22766 var tabGroup = this.parent();
22768 if(this.endX > this.startX){ // swiping right
22769 tabGroup.showPanelPrev();
22773 if(this.startX > this.endX){ // swiping left
22774 tabGroup.showPanelNext();
22793 * @class Roo.bootstrap.form.DateField
22794 * @extends Roo.bootstrap.form.Input
22795 * Bootstrap DateField class
22796 * @cfg {Number} weekStart default 0
22797 * @cfg {String} viewMode default empty, (months|years)
22798 * @cfg {String} minViewMode default empty, (months|years)
22799 * @cfg {Number} startDate default -Infinity
22800 * @cfg {Number} endDate default Infinity
22801 * @cfg {Boolean} todayHighlight default false
22802 * @cfg {Boolean} todayBtn default false
22803 * @cfg {Boolean} calendarWeeks default false
22804 * @cfg {Object} daysOfWeekDisabled default empty
22805 * @cfg {Boolean} singleMode default false (true | false)
22807 * @cfg {Boolean} keyboardNavigation default true
22808 * @cfg {String} language default en
22811 * Create a new DateField
22812 * @param {Object} config The config object
22815 Roo.bootstrap.form.DateField = function(config){
22816 Roo.bootstrap.form.DateField.superclass.constructor.call(this, config);
22820 * Fires when this field show.
22821 * @param {Roo.bootstrap.form.DateField} this
22822 * @param {Mixed} date The date value
22827 * Fires when this field hide.
22828 * @param {Roo.bootstrap.form.DateField} this
22829 * @param {Mixed} date The date value
22834 * Fires when select a date.
22835 * @param {Roo.bootstrap.form.DateField} this
22836 * @param {Mixed} date The date value
22840 * @event beforeselect
22841 * Fires when before select a date.
22842 * @param {Roo.bootstrap.form.DateField} this
22843 * @param {Mixed} date The date value
22845 beforeselect : true
22849 Roo.extend(Roo.bootstrap.form.DateField, Roo.bootstrap.form.Input, {
22852 * @cfg {String} format
22853 * The default date format string which can be overriden for localization support. The format must be
22854 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22858 * @cfg {String} altFormats
22859 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22860 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22862 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22870 todayHighlight : false,
22876 keyboardNavigation: true,
22878 calendarWeeks: false,
22880 startDate: -Infinity,
22884 daysOfWeekDisabled: [],
22888 singleMode : false,
22890 UTCDate: function()
22892 return new Date(Date.UTC.apply(Date, arguments));
22895 UTCToday: function()
22897 var today = new Date();
22898 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22901 getDate: function() {
22902 var d = this.getUTCDate();
22903 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22906 getUTCDate: function() {
22910 setDate: function(d) {
22911 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22914 setUTCDate: function(d) {
22916 this.setValue(this.formatDate(this.date));
22919 onRender: function(ct, position)
22922 Roo.bootstrap.form.DateField.superclass.onRender.call(this, ct, position);
22924 this.language = this.language || 'en';
22925 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : this.language.split('-')[0];
22926 this.language = this.language in Roo.bootstrap.form.DateField.dates ? this.language : "en";
22928 this.isRTL = Roo.bootstrap.form.DateField.dates[this.language].rtl || false;
22929 this.format = this.format || 'm/d/y';
22930 this.isInline = false;
22931 this.isInput = true;
22932 this.component = this.el.select('.add-on', true).first() || false;
22933 this.component = (this.component && this.component.length === 0) ? false : this.component;
22934 this.hasInput = this.component && this.inputEl().length;
22936 if (typeof(this.minViewMode === 'string')) {
22937 switch (this.minViewMode) {
22939 this.minViewMode = 1;
22942 this.minViewMode = 2;
22945 this.minViewMode = 0;
22950 if (typeof(this.viewMode === 'string')) {
22951 switch (this.viewMode) {
22964 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.DateField.template);
22966 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.form.DateField.template);
22968 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22970 this.picker().on('mousedown', this.onMousedown, this);
22971 this.picker().on('click', this.onClick, this);
22973 this.picker().addClass('datepicker-dropdown');
22975 this.startViewMode = this.viewMode;
22977 if(this.singleMode){
22978 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22979 v.setVisibilityMode(Roo.Element.DISPLAY);
22983 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22984 v.setStyle('width', '189px');
22988 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22989 if(!this.calendarWeeks){
22994 v.dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
22995 v.attr('colspan', function(i, val){
22996 return parseInt(val) + 1;
23001 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
23003 this.setStartDate(this.startDate);
23004 this.setEndDate(this.endDate);
23006 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
23013 if(this.isInline) {
23018 picker : function()
23020 return this.pickerEl;
23021 // return this.el.select('.datepicker', true).first();
23024 fillDow: function()
23026 var dowCnt = this.weekStart;
23035 if(this.calendarWeeks){
23043 while (dowCnt < this.weekStart + 7) {
23047 html: Roo.bootstrap.form.DateField.dates[this.language].daysMin[(dowCnt++)%7]
23051 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
23054 fillMonths: function()
23057 var months = this.picker().select('>.datepicker-months td', true).first();
23059 months.dom.innerHTML = '';
23065 html: Roo.bootstrap.form.DateField.dates[this.language].monthsShort[i++]
23068 months.createChild(month);
23075 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;
23077 if (this.date < this.startDate) {
23078 this.viewDate = new Date(this.startDate);
23079 } else if (this.date > this.endDate) {
23080 this.viewDate = new Date(this.endDate);
23082 this.viewDate = new Date(this.date);
23090 var d = new Date(this.viewDate),
23091 year = d.getUTCFullYear(),
23092 month = d.getUTCMonth(),
23093 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
23094 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
23095 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
23096 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
23097 currentDate = this.date && this.date.valueOf(),
23098 today = this.UTCToday();
23100 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].months[month]+' '+year;
23102 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.form.DateField.dates[this.language].today;
23104 // this.picker.select('>tfoot th.today').
23105 // .text(dates[this.language].today)
23106 // .toggle(this.todayBtn !== false);
23108 this.updateNavArrows();
23111 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
23113 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
23115 prevMonth.setUTCDate(day);
23117 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
23119 var nextMonth = new Date(prevMonth);
23121 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
23123 nextMonth = nextMonth.valueOf();
23125 var fillMonths = false;
23127 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
23129 while(prevMonth.valueOf() <= nextMonth) {
23132 if (prevMonth.getUTCDay() === this.weekStart) {
23134 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
23142 if(this.calendarWeeks){
23143 // ISO 8601: First week contains first thursday.
23144 // ISO also states week starts on Monday, but we can be more abstract here.
23146 // Start of current week: based on weekstart/current date
23147 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
23148 // Thursday of this week
23149 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
23150 // First Thursday of year, year from thursday
23151 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
23152 // Calendar week: ms between thursdays, div ms per day, div 7 days
23153 calWeek = (th - yth) / 864e5 / 7 + 1;
23155 fillMonths.cn.push({
23163 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
23165 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
23168 if (this.todayHighlight &&
23169 prevMonth.getUTCFullYear() == today.getFullYear() &&
23170 prevMonth.getUTCMonth() == today.getMonth() &&
23171 prevMonth.getUTCDate() == today.getDate()) {
23172 clsName += ' today';
23175 if (currentDate && prevMonth.valueOf() === currentDate) {
23176 clsName += ' active';
23179 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
23180 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
23181 clsName += ' disabled';
23184 fillMonths.cn.push({
23186 cls: 'day ' + clsName,
23187 html: prevMonth.getDate()
23190 prevMonth.setDate(prevMonth.getDate()+1);
23193 var currentYear = this.date && this.date.getUTCFullYear();
23194 var currentMonth = this.date && this.date.getUTCMonth();
23196 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
23198 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
23199 v.removeClass('active');
23201 if(currentYear === year && k === currentMonth){
23202 v.addClass('active');
23205 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
23206 v.addClass('disabled');
23212 year = parseInt(year/10, 10) * 10;
23214 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
23216 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
23219 for (var i = -1; i < 11; i++) {
23220 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
23222 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
23230 showMode: function(dir)
23233 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
23236 Roo.each(this.picker().select('>div',true).elements, function(v){
23237 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23240 this.picker().select('>.datepicker-'+Roo.bootstrap.form.DateField.modes[this.viewMode].clsName, true).first().show();
23245 if(this.isInline) {
23249 this.picker().removeClass(['bottom', 'top']);
23251 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23253 * place to the top of element!
23257 this.picker().addClass('top');
23258 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23263 this.picker().addClass('bottom');
23265 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23268 parseDate : function(value)
23270 if(!value || value instanceof Date){
23273 var v = Date.parseDate(value, this.format);
23274 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
23275 v = Date.parseDate(value, 'Y-m-d');
23277 if(!v && this.altFormats){
23278 if(!this.altFormatsArray){
23279 this.altFormatsArray = this.altFormats.split("|");
23281 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
23282 v = Date.parseDate(value, this.altFormatsArray[i]);
23288 formatDate : function(date, fmt)
23290 return (!date || !(date instanceof Date)) ?
23291 date : date.dateFormat(fmt || this.format);
23294 onFocus : function()
23296 Roo.bootstrap.form.DateField.superclass.onFocus.call(this);
23300 onBlur : function()
23302 Roo.bootstrap.form.DateField.superclass.onBlur.call(this);
23304 var d = this.inputEl().getValue();
23311 showPopup : function()
23313 this.picker().show();
23317 this.fireEvent('showpopup', this, this.date);
23320 hidePopup : function()
23322 if(this.isInline) {
23325 this.picker().hide();
23326 this.viewMode = this.startViewMode;
23329 this.fireEvent('hidepopup', this, this.date);
23333 onMousedown: function(e)
23335 e.stopPropagation();
23336 e.preventDefault();
23341 Roo.bootstrap.form.DateField.superclass.keyup.call(this);
23345 setValue: function(v)
23347 if(this.fireEvent('beforeselect', this, v) !== false){
23348 var d = new Date(this.parseDate(v) ).clearTime();
23350 if(isNaN(d.getTime())){
23351 this.date = this.viewDate = '';
23352 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23356 v = this.formatDate(d);
23358 Roo.bootstrap.form.DateField.superclass.setValue.call(this, v);
23360 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
23364 this.fireEvent('select', this, this.date);
23368 getValue: function()
23370 return this.formatDate(this.date);
23373 fireKey: function(e)
23375 if (!this.picker().isVisible()){
23376 if (e.keyCode == 27) { // allow escape to hide and re-show picker
23382 var dateChanged = false,
23384 newDate, newViewDate;
23389 e.preventDefault();
23393 if (!this.keyboardNavigation) {
23396 dir = e.keyCode == 37 ? -1 : 1;
23399 newDate = this.moveYear(this.date, dir);
23400 newViewDate = this.moveYear(this.viewDate, dir);
23401 } else if (e.shiftKey){
23402 newDate = this.moveMonth(this.date, dir);
23403 newViewDate = this.moveMonth(this.viewDate, dir);
23405 newDate = new Date(this.date);
23406 newDate.setUTCDate(this.date.getUTCDate() + dir);
23407 newViewDate = new Date(this.viewDate);
23408 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
23410 if (this.dateWithinRange(newDate)){
23411 this.date = newDate;
23412 this.viewDate = newViewDate;
23413 this.setValue(this.formatDate(this.date));
23415 e.preventDefault();
23416 dateChanged = true;
23421 if (!this.keyboardNavigation) {
23424 dir = e.keyCode == 38 ? -1 : 1;
23426 newDate = this.moveYear(this.date, dir);
23427 newViewDate = this.moveYear(this.viewDate, dir);
23428 } else if (e.shiftKey){
23429 newDate = this.moveMonth(this.date, dir);
23430 newViewDate = this.moveMonth(this.viewDate, dir);
23432 newDate = new Date(this.date);
23433 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
23434 newViewDate = new Date(this.viewDate);
23435 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
23437 if (this.dateWithinRange(newDate)){
23438 this.date = newDate;
23439 this.viewDate = newViewDate;
23440 this.setValue(this.formatDate(this.date));
23442 e.preventDefault();
23443 dateChanged = true;
23447 this.setValue(this.formatDate(this.date));
23449 e.preventDefault();
23452 this.setValue(this.formatDate(this.date));
23466 onClick: function(e)
23468 e.stopPropagation();
23469 e.preventDefault();
23471 var target = e.getTarget();
23473 if(target.nodeName.toLowerCase() === 'i'){
23474 target = Roo.get(target).dom.parentNode;
23477 var nodeName = target.nodeName;
23478 var className = target.className;
23479 var html = target.innerHTML;
23480 //Roo.log(nodeName);
23482 switch(nodeName.toLowerCase()) {
23484 switch(className) {
23490 var dir = Roo.bootstrap.form.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
23491 switch(this.viewMode){
23493 this.viewDate = this.moveMonth(this.viewDate, dir);
23497 this.viewDate = this.moveYear(this.viewDate, dir);
23503 var date = new Date();
23504 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23506 this.setValue(this.formatDate(this.date));
23513 if (className.indexOf('disabled') < 0) {
23514 if (!this.viewDate) {
23515 this.viewDate = new Date();
23517 this.viewDate.setUTCDate(1);
23518 if (className.indexOf('month') > -1) {
23519 this.viewDate.setUTCMonth(Roo.bootstrap.form.DateField.dates[this.language].monthsShort.indexOf(html));
23521 var year = parseInt(html, 10) || 0;
23522 this.viewDate.setUTCFullYear(year);
23526 if(this.singleMode){
23527 this.setValue(this.formatDate(this.viewDate));
23538 //Roo.log(className);
23539 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23540 var day = parseInt(html, 10) || 1;
23541 var year = (this.viewDate || new Date()).getUTCFullYear(),
23542 month = (this.viewDate || new Date()).getUTCMonth();
23544 if (className.indexOf('old') > -1) {
23551 } else if (className.indexOf('new') > -1) {
23559 //Roo.log([year,month,day]);
23560 this.date = this.UTCDate(year, month, day,0,0,0,0);
23561 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23563 //Roo.log(this.formatDate(this.date));
23564 this.setValue(this.formatDate(this.date));
23571 setStartDate: function(startDate)
23573 this.startDate = startDate || -Infinity;
23574 if (this.startDate !== -Infinity) {
23575 this.startDate = this.parseDate(this.startDate);
23578 this.updateNavArrows();
23581 setEndDate: function(endDate)
23583 this.endDate = endDate || Infinity;
23584 if (this.endDate !== Infinity) {
23585 this.endDate = this.parseDate(this.endDate);
23588 this.updateNavArrows();
23591 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23593 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23594 if (typeof(this.daysOfWeekDisabled) !== 'object') {
23595 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23597 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23598 return parseInt(d, 10);
23601 this.updateNavArrows();
23604 updateNavArrows: function()
23606 if(this.singleMode){
23610 var d = new Date(this.viewDate),
23611 year = d.getUTCFullYear(),
23612 month = d.getUTCMonth();
23614 Roo.each(this.picker().select('.prev', true).elements, function(v){
23616 switch (this.viewMode) {
23619 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23625 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23632 Roo.each(this.picker().select('.next', true).elements, function(v){
23634 switch (this.viewMode) {
23637 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23643 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23651 moveMonth: function(date, dir)
23656 var new_date = new Date(date.valueOf()),
23657 day = new_date.getUTCDate(),
23658 month = new_date.getUTCMonth(),
23659 mag = Math.abs(dir),
23661 dir = dir > 0 ? 1 : -1;
23664 // If going back one month, make sure month is not current month
23665 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23667 return new_date.getUTCMonth() == month;
23669 // If going forward one month, make sure month is as expected
23670 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23672 return new_date.getUTCMonth() != new_month;
23674 new_month = month + dir;
23675 new_date.setUTCMonth(new_month);
23676 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23677 if (new_month < 0 || new_month > 11) {
23678 new_month = (new_month + 12) % 12;
23681 // For magnitudes >1, move one month at a time...
23682 for (var i=0; i<mag; i++) {
23683 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23684 new_date = this.moveMonth(new_date, dir);
23686 // ...then reset the day, keeping it in the new month
23687 new_month = new_date.getUTCMonth();
23688 new_date.setUTCDate(day);
23690 return new_month != new_date.getUTCMonth();
23693 // Common date-resetting loop -- if date is beyond end of month, make it
23696 new_date.setUTCDate(--day);
23697 new_date.setUTCMonth(new_month);
23702 moveYear: function(date, dir)
23704 return this.moveMonth(date, dir*12);
23707 dateWithinRange: function(date)
23709 return date >= this.startDate && date <= this.endDate;
23715 this.picker().remove();
23718 validateValue : function(value)
23720 if(this.getVisibilityEl().hasClass('hidden')){
23724 if(value.length < 1) {
23725 if(this.allowBlank){
23731 if(value.length < this.minLength){
23734 if(value.length > this.maxLength){
23738 var vt = Roo.form.VTypes;
23739 if(!vt[this.vtype](value, this)){
23743 if(typeof this.validator == "function"){
23744 var msg = this.validator(value);
23750 if(this.regex && !this.regex.test(value)){
23754 if(typeof(this.parseDate(value)) == 'undefined'){
23758 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23762 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23772 this.date = this.viewDate = '';
23774 Roo.bootstrap.form.DateField.superclass.setValue.call(this, '');
23779 Roo.apply(Roo.bootstrap.form.DateField, {
23790 html: '<i class="fa fa-arrow-left"/>'
23800 html: '<i class="fa fa-arrow-right"/>'
23842 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23843 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23844 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23845 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23846 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23859 navFnc: 'FullYear',
23864 navFnc: 'FullYear',
23869 Roo.apply(Roo.bootstrap.form.DateField, {
23873 cls: 'datepicker dropdown-menu roo-dynamic shadow',
23877 cls: 'datepicker-days',
23881 cls: 'table-condensed',
23883 Roo.bootstrap.form.DateField.head,
23887 Roo.bootstrap.form.DateField.footer
23894 cls: 'datepicker-months',
23898 cls: 'table-condensed',
23900 Roo.bootstrap.form.DateField.head,
23901 Roo.bootstrap.form.DateField.content,
23902 Roo.bootstrap.form.DateField.footer
23909 cls: 'datepicker-years',
23913 cls: 'table-condensed',
23915 Roo.bootstrap.form.DateField.head,
23916 Roo.bootstrap.form.DateField.content,
23917 Roo.bootstrap.form.DateField.footer
23936 * @class Roo.bootstrap.form.TimeField
23937 * @extends Roo.bootstrap.form.Input
23938 * Bootstrap DateField class
23939 * @cfg {Number} minuteStep the minutes is always the multiple of a fixed number, default 1
23943 * Create a new TimeField
23944 * @param {Object} config The config object
23947 Roo.bootstrap.form.TimeField = function(config){
23948 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23952 * Fires when this field show.
23953 * @param {Roo.bootstrap.form.DateField} thisthis
23954 * @param {Mixed} date The date value
23959 * Fires when this field hide.
23960 * @param {Roo.bootstrap.form.DateField} this
23961 * @param {Mixed} date The date value
23966 * Fires when select a date.
23967 * @param {Roo.bootstrap.form.DateField} this
23968 * @param {Mixed} date The date value
23974 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23977 * @cfg {String} format
23978 * The default time format string which can be overriden for localization support. The format must be
23979 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23984 getAutoCreate : function()
23986 this.after = '<i class="fa far fa-clock"></i>';
23987 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23991 onRender: function(ct, position)
23994 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23996 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23998 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24000 this.pop = this.picker().select('>.datepicker-time',true).first();
24001 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24003 this.picker().on('mousedown', this.onMousedown, this);
24004 this.picker().on('click', this.onClick, this);
24006 this.picker().addClass('datepicker-dropdown');
24011 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24012 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24013 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24014 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24015 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24016 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24020 fireKey: function(e){
24021 if (!this.picker().isVisible()){
24022 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24028 e.preventDefault();
24036 this.onTogglePeriod();
24039 this.onIncrementMinutes();
24042 this.onDecrementMinutes();
24051 onClick: function(e) {
24052 e.stopPropagation();
24053 e.preventDefault();
24056 picker : function()
24058 return this.pickerEl;
24061 fillTime: function()
24063 var time = this.pop.select('tbody', true).first();
24065 time.dom.innerHTML = '';
24080 cls: 'hours-up fa fas fa-chevron-up'
24100 cls: 'minutes-up fa fas fa-chevron-up'
24121 cls: 'timepicker-hour',
24136 cls: 'timepicker-minute',
24151 cls: 'btn btn-primary period',
24173 cls: 'hours-down fa fas fa-chevron-down'
24193 cls: 'minutes-down fa fas fa-chevron-down'
24211 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24218 var hours = this.time.getHours();
24219 var minutes = this.time.getMinutes();
24232 hours = hours - 12;
24236 hours = '0' + hours;
24240 minutes = '0' + minutes;
24243 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24244 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24245 this.pop.select('button', true).first().dom.innerHTML = period;
24251 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24253 var cls = ['bottom'];
24255 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24262 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24266 //this.picker().setXY(20000,20000);
24267 this.picker().addClass(cls.join('-'));
24271 Roo.each(cls, function(c){
24276 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24277 //_this.picker().setTop(_this.inputEl().getHeight());
24281 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24283 //_this.picker().setTop(0 - _this.picker().getHeight());
24288 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24292 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24300 onFocus : function()
24302 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24306 onBlur : function()
24308 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24314 this.picker().show();
24319 this.fireEvent('show', this, this.date);
24324 this.picker().hide();
24327 this.fireEvent('hide', this, this.date);
24330 setTime : function()
24333 this.setValue(this.time.format(this.format));
24335 this.fireEvent('select', this, this.date);
24340 onMousedown: function(e){
24341 e.stopPropagation();
24342 e.preventDefault();
24345 onIncrementHours: function()
24347 Roo.log('onIncrementHours');
24348 this.time = this.time.add(Date.HOUR, 1);
24353 onDecrementHours: function()
24355 Roo.log('onDecrementHours');
24356 this.time = this.time.add(Date.HOUR, -1);
24360 onIncrementMinutes: function()
24362 Roo.log('onIncrementMinutes');
24363 var minutesToAdd = Math.round((parseInt(this.time.format('i')) + this.minuteStep) / this.minuteStep) * this.minuteStep - parseInt(this.time.format('i'));
24364 this.time = this.time.add(Date.MINUTE, minutesToAdd);
24368 onDecrementMinutes: function()
24370 Roo.log('onDecrementMinutes');
24371 var minutesToSubtract = parseInt(this.time.format('i')) - Math.round((parseInt(this.time.format('i')) - this.minuteStep) / this.minuteStep) * this.minuteStep;
24372 this.time = this.time.add(Date.MINUTE, -1 * minutesToSubtract);
24376 onTogglePeriod: function()
24378 Roo.log('onTogglePeriod');
24379 this.time = this.time.add(Date.HOUR, 12);
24387 Roo.apply(Roo.bootstrap.form.TimeField, {
24391 cls: 'datepicker dropdown-menu',
24395 cls: 'datepicker-time',
24399 cls: 'table-condensed',
24428 cls: 'btn btn-info ok',
24456 * @class Roo.bootstrap.form.MonthField
24457 * @extends Roo.bootstrap.form.Input
24458 * Bootstrap MonthField class
24460 * @cfg {String} language default en
24463 * Create a new MonthField
24464 * @param {Object} config The config object
24467 Roo.bootstrap.form.MonthField = function(config){
24468 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24473 * Fires when this field show.
24474 * @param {Roo.bootstrap.form.MonthField} this
24475 * @param {Mixed} date The date value
24480 * Fires when this field hide.
24481 * @param {Roo.bootstrap.form.MonthField} this
24482 * @param {Mixed} date The date value
24487 * Fires when select a date.
24488 * @param {Roo.bootstrap.form.MonthField} this
24489 * @param {String} oldvalue The old value
24490 * @param {String} newvalue The new value
24496 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24498 onRender: function(ct, position)
24501 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24503 this.language = this.language || 'en';
24504 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24505 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24507 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24508 this.isInline = false;
24509 this.isInput = true;
24510 this.component = this.el.select('.add-on', true).first() || false;
24511 this.component = (this.component && this.component.length === 0) ? false : this.component;
24512 this.hasInput = this.component && this.inputEL().length;
24514 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24516 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24518 this.picker().on('mousedown', this.onMousedown, this);
24519 this.picker().on('click', this.onClick, this);
24521 this.picker().addClass('datepicker-dropdown');
24523 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24524 v.setStyle('width', '189px');
24531 if(this.isInline) {
24537 setValue: function(v, suppressEvent)
24539 var o = this.getValue();
24541 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24545 if(suppressEvent !== true){
24546 this.fireEvent('select', this, o, v);
24551 getValue: function()
24556 onClick: function(e)
24558 e.stopPropagation();
24559 e.preventDefault();
24561 var target = e.getTarget();
24563 if(target.nodeName.toLowerCase() === 'i'){
24564 target = Roo.get(target).dom.parentNode;
24567 var nodeName = target.nodeName;
24568 var className = target.className;
24569 var html = target.innerHTML;
24571 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24575 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24577 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24583 picker : function()
24585 return this.pickerEl;
24588 fillMonths: function()
24591 var months = this.picker().select('>.datepicker-months td', true).first();
24593 months.dom.innerHTML = '';
24599 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24602 months.createChild(month);
24611 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24612 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24615 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24616 e.removeClass('active');
24618 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24619 e.addClass('active');
24626 if(this.isInline) {
24630 this.picker().removeClass(['bottom', 'top']);
24632 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24634 * place to the top of element!
24638 this.picker().addClass('top');
24639 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24644 this.picker().addClass('bottom');
24646 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24649 onFocus : function()
24651 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24655 onBlur : function()
24657 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24659 var d = this.inputEl().getValue();
24668 this.picker().show();
24669 this.picker().select('>.datepicker-months', true).first().show();
24673 this.fireEvent('show', this, this.date);
24678 if(this.isInline) {
24681 this.picker().hide();
24682 this.fireEvent('hide', this, this.date);
24686 onMousedown: function(e)
24688 e.stopPropagation();
24689 e.preventDefault();
24694 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24698 fireKey: function(e)
24700 if (!this.picker().isVisible()){
24701 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24712 e.preventDefault();
24716 dir = e.keyCode == 37 ? -1 : 1;
24718 this.vIndex = this.vIndex + dir;
24720 if(this.vIndex < 0){
24724 if(this.vIndex > 11){
24728 if(isNaN(this.vIndex)){
24732 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24738 dir = e.keyCode == 38 ? -1 : 1;
24740 this.vIndex = this.vIndex + dir * 4;
24742 if(this.vIndex < 0){
24746 if(this.vIndex > 11){
24750 if(isNaN(this.vIndex)){
24754 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24759 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24760 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24764 e.preventDefault();
24767 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24768 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24784 this.picker().remove();
24789 Roo.apply(Roo.bootstrap.form.MonthField, {
24808 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24809 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24814 Roo.apply(Roo.bootstrap.form.MonthField, {
24818 cls: 'datepicker dropdown-menu roo-dynamic',
24822 cls: 'datepicker-months',
24826 cls: 'table-condensed',
24828 Roo.bootstrap.form.DateField.content
24848 * @class Roo.bootstrap.form.CheckBox
24849 * @extends Roo.bootstrap.form.Input
24850 * Bootstrap CheckBox class
24852 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24853 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24854 * @cfg {String} boxLabel The text that appears beside the checkbox
24855 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24856 * @cfg {Boolean} checked initnal the element
24857 * @cfg {Boolean} inline inline the element (default false)
24858 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24859 * @cfg {String} tooltip label tooltip
24862 * Create a new CheckBox
24863 * @param {Object} config The config object
24866 Roo.bootstrap.form.CheckBox = function(config){
24867 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24872 * Fires when the element is checked or unchecked.
24873 * @param {Roo.bootstrap.form.CheckBox} this This input
24874 * @param {Boolean} checked The new checked value
24879 * Fires when the element is click.
24880 * @param {Roo.bootstrap.form.CheckBox} this This input
24887 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24889 inputType: 'checkbox',
24898 // checkbox success does not make any sense really..
24903 getAutoCreate : function()
24905 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24911 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24914 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24920 type : this.inputType,
24921 value : this.inputValue,
24922 cls : 'roo-' + this.inputType, //'form-box',
24923 placeholder : this.placeholder || ''
24927 if(this.inputType != 'radio'){
24931 cls : 'roo-hidden-value',
24932 value : this.checked ? this.inputValue : this.valueOff
24937 if (this.weight) { // Validity check?
24938 cfg.cls += " " + this.inputType + "-" + this.weight;
24941 if (this.disabled) {
24942 input.disabled=true;
24946 input.checked = this.checked;
24951 input.name = this.name;
24953 if(this.inputType != 'radio'){
24954 hidden.name = this.name;
24955 input.name = '_hidden_' + this.name;
24960 input.cls += ' input-' + this.size;
24965 ['xs','sm','md','lg'].map(function(size){
24966 if (settings[size]) {
24967 cfg.cls += ' col-' + size + '-' + settings[size];
24971 var inputblock = input;
24973 if (this.before || this.after) {
24976 cls : 'input-group',
24981 inputblock.cn.push({
24983 cls : 'input-group-addon',
24988 inputblock.cn.push(input);
24990 if(this.inputType != 'radio'){
24991 inputblock.cn.push(hidden);
24995 inputblock.cn.push({
24997 cls : 'input-group-addon',
25003 var boxLabelCfg = false;
25009 //'for': id, // box label is handled by onclick - so no for...
25011 html: this.boxLabel
25014 boxLabelCfg.tooltip = this.tooltip;
25020 if (align ==='left' && this.fieldLabel.length) {
25021 // Roo.log("left and has label");
25026 cls : 'control-label',
25027 html : this.fieldLabel
25038 cfg.cn[1].cn.push(boxLabelCfg);
25041 if(this.labelWidth > 12){
25042 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25045 if(this.labelWidth < 13 && this.labelmd == 0){
25046 this.labelmd = this.labelWidth;
25049 if(this.labellg > 0){
25050 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25051 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25054 if(this.labelmd > 0){
25055 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25056 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25059 if(this.labelsm > 0){
25060 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25061 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25064 if(this.labelxs > 0){
25065 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25066 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25069 } else if ( this.fieldLabel.length) {
25070 // Roo.log(" label");
25074 tag: this.boxLabel ? 'span' : 'label',
25076 cls: 'control-label box-input-label',
25077 //cls : 'input-group-addon',
25078 html : this.fieldLabel
25085 cfg.cn.push(boxLabelCfg);
25090 // Roo.log(" no label && no align");
25091 cfg.cn = [ inputblock ] ;
25093 cfg.cn.push(boxLabelCfg);
25101 if(this.inputType != 'radio'){
25102 cfg.cn.push(hidden);
25110 * return the real input element.
25112 inputEl: function ()
25114 return this.el.select('input.roo-' + this.inputType,true).first();
25116 hiddenEl: function ()
25118 return this.el.select('input.roo-hidden-value',true).first();
25121 labelEl: function()
25123 return this.el.select('label.control-label',true).first();
25125 /* depricated... */
25129 return this.labelEl();
25132 boxLabelEl: function()
25134 return this.el.select('label.box-label',true).first();
25137 initEvents : function()
25139 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25141 this.inputEl().on('click', this.onClick, this);
25143 if (this.boxLabel) {
25144 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25147 this.startValue = this.getValue();
25150 Roo.bootstrap.form.CheckBox.register(this);
25154 onClick : function(e)
25156 if(this.fireEvent('click', this, e) !== false){
25157 this.setChecked(!this.checked);
25162 setChecked : function(state,suppressEvent)
25164 this.startValue = this.getValue();
25166 if(this.inputType == 'radio'){
25168 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25169 e.dom.checked = false;
25172 this.inputEl().dom.checked = true;
25174 this.inputEl().dom.value = this.inputValue;
25176 if(suppressEvent !== true){
25177 this.fireEvent('check', this, true);
25185 this.checked = state;
25187 this.inputEl().dom.checked = state;
25190 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25192 if(suppressEvent !== true){
25193 this.fireEvent('check', this, state);
25199 getValue : function()
25201 if(this.inputType == 'radio'){
25202 return this.getGroupValue();
25205 return this.hiddenEl().dom.value;
25209 getGroupValue : function()
25211 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25215 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25218 setValue : function(v,suppressEvent)
25220 if(this.inputType == 'radio'){
25221 this.setGroupValue(v, suppressEvent);
25225 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25230 setGroupValue : function(v, suppressEvent)
25232 this.startValue = this.getValue();
25234 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25235 e.dom.checked = false;
25237 if(e.dom.value == v){
25238 e.dom.checked = true;
25242 if(suppressEvent !== true){
25243 this.fireEvent('check', this, true);
25251 validate : function()
25253 if(this.getVisibilityEl().hasClass('hidden')){
25259 (this.inputType == 'radio' && this.validateRadio()) ||
25260 (this.inputType == 'checkbox' && this.validateCheckbox())
25266 this.markInvalid();
25270 validateRadio : function()
25272 if(this.getVisibilityEl().hasClass('hidden')){
25276 if(this.allowBlank){
25282 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25283 if(!e.dom.checked){
25295 validateCheckbox : function()
25298 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25299 //return (this.getValue() == this.inputValue) ? true : false;
25302 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25310 for(var i in group){
25311 if(group[i].el.isVisible(true)){
25319 for(var i in group){
25324 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25331 * Mark this field as valid
25333 markValid : function()
25337 this.fireEvent('valid', this);
25339 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25342 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25349 if(this.inputType == 'radio'){
25350 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25351 var fg = e.findParent('.form-group', false, true);
25352 if (Roo.bootstrap.version == 3) {
25353 fg.removeClass([_this.invalidClass, _this.validClass]);
25354 fg.addClass(_this.validClass);
25356 fg.removeClass(['is-valid', 'is-invalid']);
25357 fg.addClass('is-valid');
25365 var fg = this.el.findParent('.form-group', false, true);
25366 if (Roo.bootstrap.version == 3) {
25367 fg.removeClass([this.invalidClass, this.validClass]);
25368 fg.addClass(this.validClass);
25370 fg.removeClass(['is-valid', 'is-invalid']);
25371 fg.addClass('is-valid');
25376 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25382 for(var i in group){
25383 var fg = group[i].el.findParent('.form-group', false, true);
25384 if (Roo.bootstrap.version == 3) {
25385 fg.removeClass([this.invalidClass, this.validClass]);
25386 fg.addClass(this.validClass);
25388 fg.removeClass(['is-valid', 'is-invalid']);
25389 fg.addClass('is-valid');
25395 * Mark this field as invalid
25396 * @param {String} msg The validation message
25398 markInvalid : function(msg)
25400 if(this.allowBlank){
25406 this.fireEvent('invalid', this, msg);
25408 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25411 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25415 label.markInvalid();
25418 if(this.inputType == 'radio'){
25420 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25421 var fg = e.findParent('.form-group', false, true);
25422 if (Roo.bootstrap.version == 3) {
25423 fg.removeClass([_this.invalidClass, _this.validClass]);
25424 fg.addClass(_this.invalidClass);
25426 fg.removeClass(['is-invalid', 'is-valid']);
25427 fg.addClass('is-invalid');
25435 var fg = this.el.findParent('.form-group', false, true);
25436 if (Roo.bootstrap.version == 3) {
25437 fg.removeClass([_this.invalidClass, _this.validClass]);
25438 fg.addClass(_this.invalidClass);
25440 fg.removeClass(['is-invalid', 'is-valid']);
25441 fg.addClass('is-invalid');
25446 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25452 for(var i in group){
25453 var fg = group[i].el.findParent('.form-group', false, true);
25454 if (Roo.bootstrap.version == 3) {
25455 fg.removeClass([_this.invalidClass, _this.validClass]);
25456 fg.addClass(_this.invalidClass);
25458 fg.removeClass(['is-invalid', 'is-valid']);
25459 fg.addClass('is-invalid');
25465 clearInvalid : function()
25467 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25469 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25471 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25473 if (label && label.iconEl) {
25474 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25475 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25479 disable : function()
25481 if(this.inputType != 'radio'){
25482 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25489 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25490 _this.getActionEl().addClass(this.disabledClass);
25491 e.dom.disabled = true;
25495 this.disabled = true;
25496 this.fireEvent("disable", this);
25500 enable : function()
25502 if(this.inputType != 'radio'){
25503 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25510 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25511 _this.getActionEl().removeClass(this.disabledClass);
25512 e.dom.disabled = false;
25516 this.disabled = false;
25517 this.fireEvent("enable", this);
25521 setBoxLabel : function(v)
25526 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25532 Roo.apply(Roo.bootstrap.form.CheckBox, {
25537 * register a CheckBox Group
25538 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25540 register : function(checkbox)
25542 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25543 this.groups[checkbox.groupId] = {};
25546 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25550 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25554 * fetch a CheckBox Group based on the group ID
25555 * @param {string} the group ID
25556 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25558 get: function(groupId) {
25559 if (typeof(this.groups[groupId]) == 'undefined') {
25563 return this.groups[groupId] ;
25576 * @class Roo.bootstrap.form.Radio
25577 * @extends Roo.bootstrap.Component
25578 * Bootstrap Radio class
25579 * @cfg {String} boxLabel - the label associated
25580 * @cfg {String} value - the value of radio
25583 * Create a new Radio
25584 * @param {Object} config The config object
25586 Roo.bootstrap.form.Radio = function(config){
25587 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25591 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25597 getAutoCreate : function()
25601 cls : 'form-group radio',
25606 html : this.boxLabel
25614 initEvents : function()
25616 this.parent().register(this);
25618 this.el.on('click', this.onClick, this);
25622 onClick : function(e)
25624 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25625 this.setChecked(true);
25629 setChecked : function(state, suppressEvent)
25631 this.parent().setValue(this.value, suppressEvent);
25635 setBoxLabel : function(v)
25640 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25655 * @class Roo.bootstrap.form.SecurePass
25656 * @extends Roo.bootstrap.form.Input
25657 * Bootstrap SecurePass class
25661 * Create a new SecurePass
25662 * @param {Object} config The config object
25665 Roo.bootstrap.form.SecurePass = function (config) {
25666 // these go here, so the translation tool can replace them..
25668 PwdEmpty: "Please type a password, and then retype it to confirm.",
25669 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25670 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25671 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25672 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25673 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25674 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25675 TooWeak: "Your password is Too Weak."
25677 this.meterLabel = "Password strength:";
25678 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25679 this.meterClass = [
25680 "roo-password-meter-tooweak",
25681 "roo-password-meter-weak",
25682 "roo-password-meter-medium",
25683 "roo-password-meter-strong",
25684 "roo-password-meter-grey"
25689 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25692 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25694 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25696 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25697 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25698 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25699 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25700 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25701 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25702 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25712 * @cfg {String/Object} Label for the strength meter (defaults to
25713 * 'Password strength:')
25718 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25719 * ['Weak', 'Medium', 'Strong'])
25722 pwdStrengths: false,
25735 initEvents: function ()
25737 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25739 if (this.el.is('input[type=password]') && Roo.isSafari) {
25740 this.el.on('keydown', this.SafariOnKeyDown, this);
25743 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25746 onRender: function (ct, position)
25748 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25749 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25750 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25752 this.trigger.createChild({
25757 cls: 'roo-password-meter-grey col-xs-12',
25760 //width: this.meterWidth + 'px'
25764 cls: 'roo-password-meter-text'
25770 if (this.hideTrigger) {
25771 this.trigger.setDisplayed(false);
25773 this.setSize(this.width || '', this.height || '');
25776 onDestroy: function ()
25778 if (this.trigger) {
25779 this.trigger.removeAllListeners();
25780 this.trigger.remove();
25783 this.wrap.remove();
25785 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25788 checkStrength: function ()
25790 var pwd = this.inputEl().getValue();
25791 if (pwd == this._lastPwd) {
25796 if (this.ClientSideStrongPassword(pwd)) {
25798 } else if (this.ClientSideMediumPassword(pwd)) {
25800 } else if (this.ClientSideWeakPassword(pwd)) {
25806 Roo.log('strength1: ' + strength);
25808 //var pm = this.trigger.child('div/div/div').dom;
25809 var pm = this.trigger.child('div/div');
25810 pm.removeClass(this.meterClass);
25811 pm.addClass(this.meterClass[strength]);
25814 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25816 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25818 this._lastPwd = pwd;
25822 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25824 this._lastPwd = '';
25826 var pm = this.trigger.child('div/div');
25827 pm.removeClass(this.meterClass);
25828 pm.addClass('roo-password-meter-grey');
25831 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25834 this.inputEl().dom.type='password';
25837 validateValue: function (value)
25839 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25842 if (value.length == 0) {
25843 if (this.allowBlank) {
25844 this.clearInvalid();
25848 this.markInvalid(this.errors.PwdEmpty);
25849 this.errorMsg = this.errors.PwdEmpty;
25857 if (!value.match(/[\x21-\x7e]+/)) {
25858 this.markInvalid(this.errors.PwdBadChar);
25859 this.errorMsg = this.errors.PwdBadChar;
25862 if (value.length < 6) {
25863 this.markInvalid(this.errors.PwdShort);
25864 this.errorMsg = this.errors.PwdShort;
25867 if (value.length > 16) {
25868 this.markInvalid(this.errors.PwdLong);
25869 this.errorMsg = this.errors.PwdLong;
25873 if (this.ClientSideStrongPassword(value)) {
25875 } else if (this.ClientSideMediumPassword(value)) {
25877 } else if (this.ClientSideWeakPassword(value)) {
25884 if (strength < 2) {
25885 //this.markInvalid(this.errors.TooWeak);
25886 this.errorMsg = this.errors.TooWeak;
25891 console.log('strength2: ' + strength);
25893 //var pm = this.trigger.child('div/div/div').dom;
25895 var pm = this.trigger.child('div/div');
25896 pm.removeClass(this.meterClass);
25897 pm.addClass(this.meterClass[strength]);
25899 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25901 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25903 this.errorMsg = '';
25907 CharacterSetChecks: function (type)
25910 this.fResult = false;
25913 isctype: function (character, type)
25916 case this.kCapitalLetter:
25917 if (character >= 'A' && character <= 'Z') {
25922 case this.kSmallLetter:
25923 if (character >= 'a' && character <= 'z') {
25929 if (character >= '0' && character <= '9') {
25934 case this.kPunctuation:
25935 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25946 IsLongEnough: function (pwd, size)
25948 return !(pwd == null || isNaN(size) || pwd.length < size);
25951 SpansEnoughCharacterSets: function (word, nb)
25953 if (!this.IsLongEnough(word, nb))
25958 var characterSetChecks = new Array(
25959 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25960 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25963 for (var index = 0; index < word.length; ++index) {
25964 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25965 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25966 characterSetChecks[nCharSet].fResult = true;
25973 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25974 if (characterSetChecks[nCharSet].fResult) {
25979 if (nCharSets < nb) {
25985 ClientSideStrongPassword: function (pwd)
25987 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25990 ClientSideMediumPassword: function (pwd)
25992 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25995 ClientSideWeakPassword: function (pwd)
25997 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
26000 });Roo.rtf = {}; // namespace
26001 Roo.rtf.Hex = function(hex)
26005 Roo.rtf.Paragraph = function(opts)
26007 this.content = []; ///??? is that used?
26008 };Roo.rtf.Span = function(opts)
26010 this.value = opts.value;
26013 Roo.rtf.Group = function(parent)
26015 // we dont want to acutally store parent - it will make debug a nightmare..
26023 Roo.rtf.Group.prototype = {
26027 addContent : function(node) {
26028 // could set styles...
26029 this.content.push(node);
26031 addChild : function(cn)
26035 // only for images really...
26036 toDataURL : function()
26038 var mimetype = false;
26040 case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0:
26041 mimetype = "image/png";
26043 case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
26044 mimetype = "image/jpeg";
26047 return 'about:blank'; // ?? error?
26051 var hexstring = this.content[this.content.length-1].value;
26053 return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
26054 return String.fromCharCode(parseInt(a, 16));
26059 // this looks like it's normally the {rtf{ .... }}
26060 Roo.rtf.Document = function()
26062 // we dont want to acutally store parent - it will make debug a nightmare..
26068 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, {
26069 addChild : function(cn)
26073 case 'rtlch': // most content seems to be inside this??
26076 this.rtlch.push(cn);
26079 this[cn.type] = cn;
26084 getElementsByType : function(type)
26087 this._getElementsByType(type, ret, this.cn, 'rtf');
26090 _getElementsByType : function (type, ret, search_array, path)
26092 search_array.forEach(function(n,i) {
26093 if (n.type == type) {
26094 n.path = path + '/' + n.type + ':' + i;
26097 if (n.cn.length > 0) {
26098 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
26105 Roo.rtf.Ctrl = function(opts)
26107 this.value = opts.value;
26108 this.param = opts.param;
26113 * based on this https://github.com/iarna/rtf-parser
26114 * it's really only designed to extract pict from pasted RTF
26118 * var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
26127 Roo.rtf.Parser = function(text) {
26128 //super({objectMode: true})
26130 this.parserState = this.parseText;
26132 // these are for interpeter...
26134 ///this.parserState = this.parseTop
26135 this.groupStack = [];
26136 this.hexStore = [];
26139 this.groups = []; // where we put the return.
26141 for (var ii = 0; ii < text.length; ++ii) {
26144 if (text[ii] === '\n') {
26150 this.parserState(text[ii]);
26156 Roo.rtf.Parser.prototype = {
26157 text : '', // string being parsed..
26159 controlWordParam : '',
26163 groupStack : false,
26168 row : 1, // reportin?
26172 push : function (el)
26174 var m = 'cmd'+ el.type;
26175 if (typeof(this[m]) == 'undefined') {
26176 Roo.log('invalid cmd:' + el.type);
26182 flushHexStore : function()
26184 if (this.hexStore.length < 1) {
26187 var hexstr = this.hexStore.map(
26192 this.group.addContent( new Roo.rtf.Hex( hexstr ));
26195 this.hexStore.splice(0)
26199 cmdgroupstart : function()
26201 this.flushHexStore();
26203 this.groupStack.push(this.group);
26206 if (this.doc === false) {
26207 this.group = this.doc = new Roo.rtf.Document();
26211 this.group = new Roo.rtf.Group(this.group);
26213 cmdignorable : function()
26215 this.flushHexStore();
26216 this.group.ignorable = true;
26218 cmdendparagraph : function()
26220 this.flushHexStore();
26221 this.group.addContent(new Roo.rtf.Paragraph());
26223 cmdgroupend : function ()
26225 this.flushHexStore();
26226 var endingGroup = this.group;
26229 this.group = this.groupStack.pop();
26231 this.group.addChild(endingGroup);
26236 var doc = this.group || this.doc;
26237 //if (endingGroup instanceof FontTable) {
26238 // doc.fonts = endingGroup.table
26239 //} else if (endingGroup instanceof ColorTable) {
26240 // doc.colors = endingGroup.table
26241 //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
26242 if (endingGroup.ignorable === false) {
26244 this.groups.push(endingGroup);
26245 // Roo.log( endingGroup );
26247 //Roo.each(endingGroup.content, function(item)) {
26248 // doc.addContent(item);
26250 //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
26253 cmdtext : function (cmd)
26255 this.flushHexStore();
26256 if (!this.group) { // an RTF fragment, missing the {\rtf1 header
26257 //this.group = this.doc
26258 return; // we really don't care about stray text...
26260 this.group.addContent(new Roo.rtf.Span(cmd));
26262 cmdcontrolword : function (cmd)
26264 this.flushHexStore();
26265 if (!this.group.type) {
26266 this.group.type = cmd.value;
26269 this.group.addContent(new Roo.rtf.Ctrl(cmd));
26270 // we actually don't care about ctrl words...
26273 var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
26274 if (this[method]) {
26275 this[method](cmd.param)
26277 if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
26281 cmdhexchar : function(cmd) {
26282 this.hexStore.push(cmd);
26284 cmderror : function(cmd) {
26290 if (this.text !== '\u0000') this.emitText()
26296 parseText : function(c)
26299 this.parserState = this.parseEscapes;
26300 } else if (c === '{') {
26301 this.emitStartGroup();
26302 } else if (c === '}') {
26303 this.emitEndGroup();
26304 } else if (c === '\x0A' || c === '\x0D') {
26305 // cr/lf are noise chars
26311 parseEscapes: function (c)
26313 if (c === '\\' || c === '{' || c === '}') {
26315 this.parserState = this.parseText;
26317 this.parserState = this.parseControlSymbol;
26318 this.parseControlSymbol(c);
26321 parseControlSymbol: function(c)
26324 this.text += '\u00a0'; // nbsp
26325 this.parserState = this.parseText
26326 } else if (c === '-') {
26327 this.text += '\u00ad'; // soft hyphen
26328 } else if (c === '_') {
26329 this.text += '\u2011'; // non-breaking hyphen
26330 } else if (c === '*') {
26331 this.emitIgnorable();
26332 this.parserState = this.parseText;
26333 } else if (c === "'") {
26334 this.parserState = this.parseHexChar;
26335 } else if (c === '|') { // formula cacter
26336 this.emitFormula();
26337 this.parserState = this.parseText;
26338 } else if (c === ':') { // subentry in an index entry
26339 this.emitIndexSubEntry();
26340 this.parserState = this.parseText;
26341 } else if (c === '\x0a') {
26342 this.emitEndParagraph();
26343 this.parserState = this.parseText;
26344 } else if (c === '\x0d') {
26345 this.emitEndParagraph();
26346 this.parserState = this.parseText;
26348 this.parserState = this.parseControlWord;
26349 this.parseControlWord(c);
26352 parseHexChar: function (c)
26354 if (/^[A-Fa-f0-9]$/.test(c)) {
26356 if (this.hexChar.length >= 2) {
26357 this.emitHexChar();
26358 this.parserState = this.parseText;
26362 this.emitError("Invalid character \"" + c + "\" in hex literal.");
26363 this.parserState = this.parseText;
26366 parseControlWord : function(c)
26369 this.emitControlWord();
26370 this.parserState = this.parseText;
26371 } else if (/^[-\d]$/.test(c)) {
26372 this.parserState = this.parseControlWordParam;
26373 this.controlWordParam += c;
26374 } else if (/^[A-Za-z]$/.test(c)) {
26375 this.controlWord += c;
26377 this.emitControlWord();
26378 this.parserState = this.parseText;
26382 parseControlWordParam : function (c) {
26383 if (/^\d$/.test(c)) {
26384 this.controlWordParam += c;
26385 } else if (c === ' ') {
26386 this.emitControlWord();
26387 this.parserState = this.parseText;
26389 this.emitControlWord();
26390 this.parserState = this.parseText;
26398 emitText : function () {
26399 if (this.text === '') {
26411 emitControlWord : function ()
26414 if (this.controlWord === '') {
26415 // do we want to track this - it seems just to cause problems.
26416 //this.emitError('empty control word');
26419 type: 'controlword',
26420 value: this.controlWord,
26421 param: this.controlWordParam !== '' && Number(this.controlWordParam),
26427 this.controlWord = '';
26428 this.controlWordParam = '';
26430 emitStartGroup : function ()
26434 type: 'groupstart',
26440 emitEndGroup : function ()
26450 emitIgnorable : function ()
26460 emitHexChar : function ()
26465 value: this.hexChar,
26472 emitError : function (message)
26480 char: this.cpos //,
26481 //stack: new Error().stack
26484 emitEndParagraph : function () {
26487 type: 'endparagraph',
26495 Roo.htmleditor = {};
26498 * @class Roo.htmleditor.Filter
26499 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26500 * @cfg {DomElement} node The node to iterate and filter
26501 * @cfg {boolean|String|Array} tag Tags to replace
26503 * Create a new Filter.
26504 * @param {Object} config Configuration options
26509 Roo.htmleditor.Filter = function(cfg) {
26510 Roo.apply(this.cfg);
26511 // this does not actually call walk as it's really just a abstract class
26515 Roo.htmleditor.Filter.prototype = {
26521 // overrride to do replace comments.
26522 replaceComment : false,
26524 // overrride to do replace or do stuff with tags..
26525 replaceTag : false,
26527 walk : function(dom)
26529 Roo.each( Array.from(dom.childNodes), function( e ) {
26532 case e.nodeType == 8 && this.replaceComment !== false: // comment
26533 this.replaceComment(e);
26536 case e.nodeType != 1: //not a node.
26539 case this.tag === true: // everything
26540 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1:
26541 case e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":":
26542 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26543 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26544 if (this.replaceTag && false === this.replaceTag(e)) {
26547 if (e.hasChildNodes()) {
26552 default: // tags .. that do not match.
26553 if (e.hasChildNodes()) {
26563 removeNodeKeepChildren : function( node)
26566 ar = Array.from(node.childNodes);
26567 for (var i = 0; i < ar.length; i++) {
26569 node.removeChild(ar[i]);
26570 // what if we need to walk these???
26571 node.parentNode.insertBefore(ar[i], node);
26574 node.parentNode.removeChild(node);
26579 * @class Roo.htmleditor.FilterAttributes
26580 * clean attributes and styles including http:// etc.. in attribute
26582 * Run a new Attribute Filter
26583 * @param {Object} config Configuration options
26585 Roo.htmleditor.FilterAttributes = function(cfg)
26587 Roo.apply(this, cfg);
26588 this.attrib_black = this.attrib_black || [];
26589 this.attrib_white = this.attrib_white || [];
26591 this.attrib_clean = this.attrib_clean || [];
26592 this.style_white = this.style_white || [];
26593 this.style_black = this.style_black || [];
26594 this.walk(cfg.node);
26597 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26599 tag: true, // all tags
26601 attrib_black : false, // array
26602 attrib_clean : false,
26603 attrib_white : false,
26605 style_white : false,
26606 style_black : false,
26609 replaceTag : function(node)
26611 if (!node.attributes || !node.attributes.length) {
26615 for (var i = node.attributes.length-1; i > -1 ; i--) {
26616 var a = node.attributes[i];
26618 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26619 node.removeAttribute(a.name);
26625 if (a.name.toLowerCase().substr(0,2)=='on') {
26626 node.removeAttribute(a.name);
26631 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26632 node.removeAttribute(a.name);
26635 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26636 this.cleanAttr(node,a.name,a.value); // fixme..
26639 if (a.name == 'style') {
26640 this.cleanStyle(node,a.name,a.value);
26643 /// clean up MS crap..
26644 // tecnically this should be a list of valid class'es..
26647 if (a.name == 'class') {
26648 if (a.value.match(/^Mso/)) {
26649 node.removeAttribute('class');
26652 if (a.value.match(/^body$/)) {
26653 node.removeAttribute('class');
26663 return true; // clean children
26666 cleanAttr: function(node, n,v)
26669 if (v.match(/^\./) || v.match(/^\//)) {
26672 if (v.match(/^(http|https):\/\//)
26673 || v.match(/^mailto:/)
26674 || v.match(/^ftp:/)
26675 || v.match(/^data:/)
26679 if (v.match(/^#/)) {
26682 if (v.match(/^\{/)) { // allow template editing.
26685 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26686 node.removeAttribute(n);
26689 cleanStyle : function(node, n,v)
26691 if (v.match(/expression/)) { //XSS?? should we even bother..
26692 node.removeAttribute(n);
26696 var parts = v.split(/;/);
26699 Roo.each(parts, function(p) {
26700 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26704 var l = p.split(':').shift().replace(/\s+/g,'');
26705 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26707 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26711 // only allow 'c whitelisted system attributes'
26712 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26720 if (clean.length) {
26721 node.setAttribute(n, clean.join(';'));
26723 node.removeAttribute(n);
26732 * @class Roo.htmleditor.FilterBlack
26733 * remove blacklisted elements.
26735 * Run a new Blacklisted Filter
26736 * @param {Object} config Configuration options
26739 Roo.htmleditor.FilterBlack = function(cfg)
26741 Roo.apply(this, cfg);
26742 this.walk(cfg.node);
26745 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26747 tag : true, // all elements.
26749 replaceTag : function(n)
26751 n.parentNode.removeChild(n);
26755 * @class Roo.htmleditor.FilterComment
26758 * Run a new Comments Filter
26759 * @param {Object} config Configuration options
26761 Roo.htmleditor.FilterComment = function(cfg)
26763 this.walk(cfg.node);
26766 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26769 replaceComment : function(n)
26771 n.parentNode.removeChild(n);
26774 * @class Roo.htmleditor.FilterKeepChildren
26775 * remove tags but keep children
26777 * Run a new Keep Children Filter
26778 * @param {Object} config Configuration options
26781 Roo.htmleditor.FilterKeepChildren = function(cfg)
26783 Roo.apply(this, cfg);
26784 if (this.tag === false) {
26785 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26788 if ((typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)) {
26789 this.cleanNamespace = true;
26792 this.walk(cfg.node);
26795 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26797 cleanNamespace : false, // should really be an option, rather than using ':' inside of this tag.
26799 replaceTag : function(node)
26801 // walk children...
26802 //Roo.log(node.tagName);
26803 var ar = Array.from(node.childNodes);
26806 for (var i = 0; i < ar.length; i++) {
26808 if (e.nodeType == 1) {
26810 (typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1)
26811 || // array and it matches
26812 (typeof(this.tag) == 'string' && this.tag == e.tagName)
26814 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'object' && this.tag.indexOf(":") > -1)
26816 (e.tagName.indexOf(":") > -1 && typeof(this.tag) == 'string' && this.tag == ":")
26818 this.replaceTag(ar[i]); // child is blacklisted as well...
26823 ar = Array.from(node.childNodes);
26824 for (var i = 0; i < ar.length; i++) {
26826 node.removeChild(ar[i]);
26827 // what if we need to walk these???
26828 node.parentNode.insertBefore(ar[i], node);
26829 if (this.tag !== false) {
26834 //Roo.log("REMOVE:" + node.tagName);
26835 node.parentNode.removeChild(node);
26836 return false; // don't walk children
26841 * @class Roo.htmleditor.FilterParagraph
26842 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26843 * like on 'push' to remove the <p> tags and replace them with line breaks.
26845 * Run a new Paragraph Filter
26846 * @param {Object} config Configuration options
26849 Roo.htmleditor.FilterParagraph = function(cfg)
26851 // no need to apply config.
26852 this.walk(cfg.node);
26855 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26862 replaceTag : function(node)
26865 if (node.childNodes.length == 1 &&
26866 node.childNodes[0].nodeType == 3 &&
26867 node.childNodes[0].textContent.trim().length < 1
26869 // remove and replace with '<BR>';
26870 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26871 return false; // no need to walk..
26873 var ar = Array.from(node.childNodes);
26874 for (var i = 0; i < ar.length; i++) {
26875 node.removeChild(ar[i]);
26876 // what if we need to walk these???
26877 node.parentNode.insertBefore(ar[i], node);
26879 // now what about this?
26883 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26884 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26885 node.parentNode.removeChild(node);
26892 * @class Roo.htmleditor.FilterSpan
26893 * filter span's with no attributes out..
26895 * Run a new Span Filter
26896 * @param {Object} config Configuration options
26899 Roo.htmleditor.FilterSpan = function(cfg)
26901 // no need to apply config.
26902 this.walk(cfg.node);
26905 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26911 replaceTag : function(node)
26913 if (node.attributes && node.attributes.length > 0) {
26914 return true; // walk if there are any.
26916 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26922 * @class Roo.htmleditor.FilterTableWidth
26923 try and remove table width data - as that frequently messes up other stuff.
26925 * was cleanTableWidths.
26927 * Quite often pasting from word etc.. results in tables with column and widths.
26928 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26931 * Run a new Table Filter
26932 * @param {Object} config Configuration options
26935 Roo.htmleditor.FilterTableWidth = function(cfg)
26937 // no need to apply config.
26938 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26939 this.walk(cfg.node);
26942 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26947 replaceTag: function(node) {
26951 if (node.hasAttribute('width')) {
26952 node.removeAttribute('width');
26956 if (node.hasAttribute("style")) {
26959 var styles = node.getAttribute("style").split(";");
26961 Roo.each(styles, function(s) {
26962 if (!s.match(/:/)) {
26965 var kv = s.split(":");
26966 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26969 // what ever is left... we allow.
26972 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26973 if (!nstyle.length) {
26974 node.removeAttribute('style');
26978 return true; // continue doing children..
26981 * @class Roo.htmleditor.FilterWord
26982 * try and clean up all the mess that Word generates.
26984 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26987 * Run a new Span Filter
26988 * @param {Object} config Configuration options
26991 Roo.htmleditor.FilterWord = function(cfg)
26993 // no need to apply config.
26994 this.replaceDocBullets(cfg.node);
26996 this.replaceAname(cfg.node);
26997 // this is disabled as the removal is done by other filters;
26998 // this.walk(cfg.node);
26999 this.replaceImageTable(cfg.node);
27003 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
27009 * Clean up MS wordisms...
27011 replaceTag : function(node)
27014 // no idea what this does - span with text, replaceds with just text.
27016 node.nodeName == 'SPAN' &&
27017 !node.hasAttributes() &&
27018 node.childNodes.length == 1 &&
27019 node.firstChild.nodeName == "#text"
27021 var textNode = node.firstChild;
27022 node.removeChild(textNode);
27023 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27024 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
27026 node.parentNode.insertBefore(textNode, node);
27027 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
27028 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
27031 node.parentNode.removeChild(node);
27032 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
27037 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
27038 node.parentNode.removeChild(node);
27039 return false; // dont do chidlren
27041 //Roo.log(node.tagName);
27042 // remove - but keep children..
27043 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
27044 //Roo.log('-- removed');
27045 while (node.childNodes.length) {
27046 var cn = node.childNodes[0];
27047 node.removeChild(cn);
27048 node.parentNode.insertBefore(cn, node);
27049 // move node to parent - and clean it..
27050 if (cn.nodeType == 1) {
27051 this.replaceTag(cn);
27055 node.parentNode.removeChild(node);
27056 /// no need to iterate chidlren = it's got none..
27057 //this.iterateChildren(node, this.cleanWord);
27058 return false; // no need to iterate children.
27061 if (node.className.length) {
27063 var cn = node.className.split(/\W+/);
27065 Roo.each(cn, function(cls) {
27066 if (cls.match(/Mso[a-zA-Z]+/)) {
27071 node.className = cna.length ? cna.join(' ') : '';
27073 node.removeAttribute("class");
27077 if (node.hasAttribute("lang")) {
27078 node.removeAttribute("lang");
27081 if (node.hasAttribute("style")) {
27083 var styles = node.getAttribute("style").split(";");
27085 Roo.each(styles, function(s) {
27086 if (!s.match(/:/)) {
27089 var kv = s.split(":");
27090 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
27093 // what ever is left... we allow.
27096 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
27097 if (!nstyle.length) {
27098 node.removeAttribute('style');
27101 return true; // do children
27107 styleToObject: function(node)
27109 var styles = (node.getAttribute("style") || '').split(";");
27111 Roo.each(styles, function(s) {
27112 if (!s.match(/:/)) {
27115 var kv = s.split(":");
27117 // what ever is left... we allow.
27118 ret[kv[0].trim()] = kv[1];
27124 replaceAname : function (doc)
27126 // replace all the a/name without..
27127 var aa = Array.from(doc.getElementsByTagName('a'));
27128 for (var i = 0; i < aa.length; i++) {
27130 if (a.hasAttribute("name")) {
27131 a.removeAttribute("name");
27133 if (a.hasAttribute("href")) {
27136 // reparent children.
27137 this.removeNodeKeepChildren(a);
27147 replaceDocBullets : function(doc)
27149 // this is a bit odd - but it appears some indents use ql-indent-1
27150 //Roo.log(doc.innerHTML);
27152 var listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpFirst'));
27153 for( var i = 0; i < listpara.length; i ++) {
27154 listpara[i].className = "MsoListParagraph";
27157 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpMiddle'));
27158 for( var i = 0; i < listpara.length; i ++) {
27159 listpara[i].className = "MsoListParagraph";
27161 listpara = Array.from(doc.getElementsByClassName('MsoListParagraphCxSpLast'));
27162 for( var i = 0; i < listpara.length; i ++) {
27163 listpara[i].className = "MsoListParagraph";
27165 listpara = Array.from(doc.getElementsByClassName('ql-indent-1'));
27166 for( var i = 0; i < listpara.length; i ++) {
27167 listpara[i].className = "MsoListParagraph";
27170 // this is a bit hacky - we had one word document where h2 had a miso-list attribute.
27171 var htwo = Array.from(doc.getElementsByTagName('h2'));
27172 for( var i = 0; i < htwo.length; i ++) {
27173 if (htwo[i].hasAttribute('style') && htwo[i].getAttribute('style').match(/mso-list:/)) {
27174 htwo[i].className = "MsoListParagraph";
27177 listpara = Array.from(doc.getElementsByClassName('MsoNormal'));
27178 for( var i = 0; i < listpara.length; i ++) {
27179 if (listpara[i].hasAttribute('style') && listpara[i].getAttribute('style').match(/mso-list:/)) {
27180 listpara[i].className = "MsoListParagraph";
27182 listpara[i].className = "MsoNormalx";
27186 listpara = doc.getElementsByClassName('MsoListParagraph');
27187 // Roo.log(doc.innerHTML);
27191 while(listpara.length) {
27193 this.replaceDocBullet(listpara.item(0));
27200 replaceDocBullet : function(p)
27202 // gather all the siblings.
27204 parent = p.parentNode,
27205 doc = parent.ownerDocument,
27208 //Roo.log("Parsing: " + p.innerText) ;
27209 var listtype = 'ul';
27211 if (ns.nodeType != 1) {
27212 ns = ns.nextSibling;
27215 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
27216 //Roo.log("Missing para r q1indent - got:" + ns.className);
27219 var spans = ns.getElementsByTagName('span');
27221 if (ns.hasAttribute('style') && ns.getAttribute('style').match(/mso-list/)) {
27223 ns = ns.nextSibling;
27225 if (!spans.length) {
27230 for (var i = 0; i < spans.length;i++) {
27232 if (se.hasAttribute('style') && se.hasAttribute('style') && se.style.fontFamily != '') {
27233 ff = se.style.fontFamily;
27239 //Roo.log("got font family: " + ff);
27240 if (typeof(ff) != 'undefined' && !ff.match(/(Symbol|Wingdings)/) && "·o".indexOf(se.innerText.trim()) < 0) {
27246 //Roo.log("no mso-list?");
27248 var spans = ns.getElementsByTagName('span');
27249 if (!spans.length) {
27252 var has_list = false;
27253 for(var i = 0; i < spans.length; i++) {
27254 if (spans[i].hasAttribute('style') && spans[i].getAttribute('style').match(/mso-list/)) {
27263 ns = ns.nextSibling;
27267 if (!items.length) {
27272 var ul = parent.ownerDocument.createElement(listtype); // what about number lists...
27273 parent.insertBefore(ul, p);
27275 var stack = [ ul ];
27276 var last_li = false;
27278 var margin_to_depth = {};
27281 items.forEach(function(n, ipos) {
27282 //Roo.log("got innertHMLT=" + n.innerHTML);
27284 var spans = n.getElementsByTagName('span');
27285 if (!spans.length) {
27286 //Roo.log("No spans found");
27288 parent.removeChild(n);
27291 return; // skip it...
27297 for(var i = 0; i < spans.length; i++) {
27299 style = this.styleToObject(spans[i]);
27300 if (typeof(style['mso-list']) == 'undefined') {
27303 if (listtype == 'ol') {
27304 num = spans[i].innerText.replace(/[^0-9]+]/g,'') * 1;
27306 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
27309 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
27310 style = this.styleToObject(n); // mo-list is from the parent node.
27311 if (typeof(style['mso-list']) == 'undefined') {
27312 //Roo.log("parent is missing level");
27314 parent.removeChild(n);
27319 var margin = style['margin-left'];
27320 if (typeof(margin_to_depth[margin]) == 'undefined') {
27322 margin_to_depth[margin] = max_margins;
27324 nlvl = margin_to_depth[margin] ;
27328 var nul = doc.createElement(listtype); // what about number lists...
27330 last_li = doc.createElement('li');
27331 stack[lvl].appendChild(last_li);
27333 last_li.appendChild(nul);
27339 // not starting at 1..
27340 if (!stack[nlvl].hasAttribute("start") && listtype == "ol") {
27341 stack[nlvl].setAttribute("start", num);
27344 var nli = stack[nlvl].appendChild(doc.createElement('li'));
27346 nli.innerHTML = n.innerHTML;
27347 //Roo.log("innerHTML = " + n.innerHTML);
27348 parent.removeChild(n);
27360 replaceImageTable : function(doc)
27363 <table cellpadding=0 cellspacing=0 align=left>
27365 <td width=423 height=0></td>
27369 <td><img width=601 height=401
27370 src="file:///C:/Users/Alan/AppData/Local/Temp/msohtmlclip1/01/clip_image002.jpg"
27371 v:shapes="Picture_x0020_2"></td>
27375 var imgs = Array.from(doc.getElementsByTagName('img'));
27376 Roo.each(imgs, function(img) {
27377 var td = img.parentNode;
27378 if (td.nodeName != 'TD') {
27381 var tr = td.parentNode;
27382 if (tr.nodeName != 'TR') {
27385 var tbody = tr.parentNode;
27386 if (tbody.nodeName != 'TBODY') {
27389 var table = tbody.parentNode;
27390 if (table.nodeName != 'TABLE') {
27395 if (table.getElementsByTagName('tr').length != 2) {
27398 if (table.getElementsByTagName('td').length != 3) {
27401 if (table.innerText.trim() != '') {
27404 var p = table.parentNode;
27405 img.parentNode.removeChild(img);
27406 p.insertBefore(img, table);
27407 p.removeChild(table);
27418 * @class Roo.htmleditor.FilterStyleToTag
27419 * part of the word stuff... - certain 'styles' should be converted to tags.
27421 * font-weight: bold -> bold
27422 * ?? super / subscrit etc..
27425 * Run a new style to tag filter.
27426 * @param {Object} config Configuration options
27428 Roo.htmleditor.FilterStyleToTag = function(cfg)
27432 B : [ 'fontWeight' , 'bold'],
27433 I : [ 'fontStyle' , 'italic'],
27434 //pre : [ 'font-style' , 'italic'],
27435 // h1.. h6 ?? font-size?
27436 SUP : [ 'verticalAlign' , 'super' ],
27437 SUB : [ 'verticalAlign' , 'sub' ]
27442 Roo.apply(this, cfg);
27445 this.walk(cfg.node);
27452 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
27454 tag: true, // all tags
27459 replaceTag : function(node)
27463 if (node.getAttribute("style") === null) {
27467 for (var k in this.tags) {
27468 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
27470 node.style.removeProperty(this.tags[k][0]);
27473 if (!inject.length) {
27476 var cn = Array.from(node.childNodes);
27478 Roo.each(inject, function(t) {
27479 var nc = node.ownerDocument.createElement(t);
27480 nn.appendChild(nc);
27483 for(var i = 0;i < cn.length;cn++) {
27484 node.removeChild(cn[i]);
27485 nn.appendChild(cn[i]);
27487 return true /// iterate thru
27491 * @class Roo.htmleditor.FilterLongBr
27492 * BR/BR/BR - keep a maximum of 2...
27494 * Run a new Long BR Filter
27495 * @param {Object} config Configuration options
27498 Roo.htmleditor.FilterLongBr = function(cfg)
27500 // no need to apply config.
27501 this.walk(cfg.node);
27504 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
27511 replaceTag : function(node)
27514 var ps = node.nextSibling;
27515 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27516 ps = ps.nextSibling;
27519 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
27520 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
27524 if (!ps || ps.nodeType != 1) {
27528 if (!ps || ps.tagName != 'BR') {
27537 if (!node.previousSibling) {
27540 var ps = node.previousSibling;
27542 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
27543 ps = ps.previousSibling;
27545 if (!ps || ps.nodeType != 1) {
27548 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
27549 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
27553 node.parentNode.removeChild(node); // remove me...
27555 return false; // no need to do children
27562 * @class Roo.htmleditor.FilterBlock
27563 * removes id / data-block and contenteditable that are associated with blocks
27564 * usage should be done on a cloned copy of the dom
27566 * Run a new Attribute Filter { node : xxxx }}
27567 * @param {Object} config Configuration options
27569 Roo.htmleditor.FilterBlock = function(cfg)
27571 Roo.apply(this, cfg);
27572 var qa = cfg.node.querySelectorAll;
27573 this.removeAttributes('data-block');
27574 this.removeAttributes('contenteditable');
27575 this.removeAttributes('id');
27579 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
27581 node: true, // all tags
27584 removeAttributes : function(attr)
27586 var ar = this.node.querySelectorAll('*[' + attr + ']');
27587 for (var i =0;i<ar.length;i++) {
27588 ar[i].removeAttribute(attr);
27597 * This is based loosely on tinymce
27598 * @class Roo.htmleditor.TidySerializer
27599 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27601 * @method Serializer
27602 * @param {Object} settings Name/value settings object.
27606 Roo.htmleditor.TidySerializer = function(settings)
27608 Roo.apply(this, settings);
27610 this.writer = new Roo.htmleditor.TidyWriter(settings);
27615 Roo.htmleditor.TidySerializer.prototype = {
27618 * @param {boolean} inner do the inner of the node.
27625 * Serializes the specified node into a string.
27628 * new tinymce.html.Serializer().serialize(new tinymce.html.DomParser().parse('<p>text</p>'));
27629 * @method serialize
27630 * @param {DomElement} node Node instance to serialize.
27631 * @return {String} String with HTML based on DOM tree.
27633 serialize : function(node) {
27635 // = settings.validate;
27636 var writer = this.writer;
27640 3: function(node) {
27642 writer.text(node.nodeValue, node);
27645 8: function(node) {
27646 writer.comment(node.nodeValue);
27648 // Processing instruction
27649 7: function(node) {
27650 writer.pi(node.name, node.nodeValue);
27653 10: function(node) {
27654 writer.doctype(node.nodeValue);
27657 4: function(node) {
27658 writer.cdata(node.nodeValue);
27660 // Document fragment
27661 11: function(node) {
27662 node = node.firstChild;
27668 node = node.nextSibling
27673 1 != node.nodeType || this.inner ? this.handlers[11](node) : this.walk(node);
27674 return writer.getContent();
27677 walk: function(node)
27679 var attrName, attrValue, sortedAttrs, i, l, elementRule,
27680 handler = this.handlers[node.nodeType];
27687 var name = node.nodeName;
27688 var isEmpty = node.childNodes.length < 1;
27690 var writer = this.writer;
27691 var attrs = node.attributes;
27694 writer.start(node.nodeName, attrs, isEmpty, node);
27698 node = node.firstChild;
27705 node = node.nextSibling;
27711 // Serialize element and treat all non elements as fragments
27716 * This is based loosely on tinymce
27717 * @class Roo.htmleditor.TidyWriter
27718 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
27721 * - not tested much with 'PRE' formated elements.
27727 Roo.htmleditor.TidyWriter = function(settings)
27730 // indent, indentBefore, indentAfter, encode, htmlOutput, html = [];
27731 Roo.apply(this, settings);
27735 this.encode = Roo.htmleditor.TidyEntities.getEncodeFunc(settings.entity_encoding || 'raw', settings.entities);
27738 Roo.htmleditor.TidyWriter.prototype = {
27745 // part of state...
27749 last_inline : false,
27754 * Writes the a start element such as <p id="a">.
27757 * @param {String} name Name of the element.
27758 * @param {Array} attrs Optional attribute array or undefined if it hasn't any.
27759 * @param {Boolean} empty Optional empty state if the tag should end like <br />.
27761 start: function(name, attrs, empty, node)
27763 var i, l, attr, value;
27765 // there are some situations where adding line break && indentation will not work. will not work.
27766 // <span / b / i ... formating?
27768 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27769 var in_pre = this.in_pre || Roo.htmleditor.TidyWriter.whitespace_elements.indexOf(name) > -1;
27771 var is_short = empty ? Roo.htmleditor.TidyWriter.shortend_elements.indexOf(name) > -1 : false;
27773 var add_lb = name == 'BR' ? false : in_inline;
27775 if (!add_lb && !this.in_pre && this.lastElementEndsWS()) {
27779 var indentstr = this.indentstr;
27781 // e_inline = elements that can be inline, but still allow \n before and after?
27782 // only 'BR' ??? any others?
27784 // ADD LINE BEFORE tage
27785 if (!this.in_pre) {
27788 if (name == 'BR') {
27790 } else if (this.lastElementEndsWS()) {
27793 // otherwise - no new line. (and dont indent.)
27804 this.html.push(indentstr + '<', name.toLowerCase());
27807 for (i = 0, l = attrs.length; i < l; i++) {
27809 this.html.push(' ', attr.name, '="', this.encode(attr.value, true), '"');
27815 this.html[this.html.length] = '/>';
27817 this.html[this.html.length] = '></' + name.toLowerCase() + '>';
27819 var e_inline = name == 'BR' ? false : this.in_inline;
27821 if (!e_inline && !this.in_pre) {
27828 this.html[this.html.length] = '>';
27830 // there is a special situation, where we need to turn on in_inline - if any of the imediate chidlren are one of these.
27832 if (!in_inline && !in_pre) {
27833 var cn = node.firstChild;
27835 if (Roo.htmleditor.TidyWriter.inline_elements.indexOf(cn.nodeName) > -1) {
27839 cn = cn.nextSibling;
27847 indentstr : in_pre ? '' : (this.indentstr + this.indent),
27849 in_inline : in_inline
27851 // add a line after if we are not in a
27853 if (!in_inline && !in_pre) {
27862 lastElementEndsWS : function()
27864 var value = this.html.length > 0 ? this.html[this.html.length-1] : false;
27865 if (value === false) {
27868 return value.match(/\s+$/);
27873 * Writes the a end element such as </p>.
27876 * @param {String} name Name of the element.
27878 end: function(name) {
27881 var indentstr = '';
27882 var in_inline = this.in_inline || Roo.htmleditor.TidyWriter.inline_elements.indexOf(name) > -1;
27884 if (!this.in_pre && !in_inline) {
27886 indentstr = this.indentstr;
27888 this.html.push(indentstr + '</', name.toLowerCase(), '>');
27889 this.last_inline = in_inline;
27891 // pop the indent state..
27894 * Writes a text node.
27896 * In pre - we should not mess with the contents.
27900 * @param {String} text String to write out.
27901 * @param {Boolean} raw Optional raw state if true the contents wont get encoded.
27903 text: function(in_text, node)
27905 // if not in whitespace critical
27906 if (in_text.length < 1) {
27909 var text = new XMLSerializer().serializeToString(document.createTextNode(in_text)); // escape it properly?
27912 this.html[this.html.length] = text;
27916 if (this.in_inline) {
27917 text = text.replace(/\s+/g,' '); // all white space inc line breaks to a slingle' '
27919 text = text.replace(/\s+/,' '); // all white space to single white space
27922 // if next tag is '<BR>', then we can trim right..
27923 if (node.nextSibling &&
27924 node.nextSibling.nodeType == 1 &&
27925 node.nextSibling.nodeName == 'BR' )
27927 text = text.replace(/\s+$/g,'');
27929 // if previous tag was a BR, we can also trim..
27930 if (node.previousSibling &&
27931 node.previousSibling.nodeType == 1 &&
27932 node.previousSibling.nodeName == 'BR' )
27934 text = this.indentstr + text.replace(/^\s+/g,'');
27936 if (text.match(/\n/)) {
27937 text = text.replace(
27938 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27940 // remoeve the last whitespace / line break.
27941 text = text.replace(/\n\s+$/,'');
27943 // repace long lines
27947 this.html[this.html.length] = text;
27950 // see if previous element was a inline element.
27951 var indentstr = this.indentstr;
27953 text = text.replace(/\s+/g," "); // all whitespace into single white space.
27955 // should trim left?
27956 if (node.previousSibling &&
27957 node.previousSibling.nodeType == 1 &&
27958 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.previousSibling.nodeName) > -1)
27964 text = text.replace(/^\s+/,''); // trim left
27967 // should trim right?
27968 if (node.nextSibling &&
27969 node.nextSibling.nodeType == 1 &&
27970 Roo.htmleditor.TidyWriter.inline_elements.indexOf(node.nextSibling.nodeName) > -1)
27975 text = text.replace(/\s+$/,''); // trim right
27982 if (text.length < 1) {
27985 if (!text.match(/\n/)) {
27986 this.html.push(indentstr + text);
27990 text = this.indentstr + text.replace(
27991 /(?![^\n]{1,64}$)([^\n]{1,64})\s/g, '$1\n' + this.indentstr
27993 // remoeve the last whitespace / line break.
27994 text = text.replace(/\s+$/,'');
27996 this.html.push(text);
27998 // split and indent..
28003 * Writes a cdata node such as <![CDATA[data]]>.
28006 * @param {String} text String to write out inside the cdata.
28008 cdata: function(text) {
28009 this.html.push('<![CDATA[', text, ']]>');
28012 * Writes a comment node such as <!-- Comment -->.
28015 * @param {String} text String to write out inside the comment.
28017 comment: function(text) {
28018 this.html.push('<!--', text, '-->');
28021 * Writes a PI node such as <?xml attr="value" ?>.
28024 * @param {String} name Name of the pi.
28025 * @param {String} text String to write out inside the pi.
28027 pi: function(name, text) {
28028 text ? this.html.push('<?', name, ' ', this.encode(text), '?>') : this.html.push('<?', name, '?>');
28029 this.indent != '' && this.html.push('\n');
28032 * Writes a doctype node such as <!DOCTYPE data>.
28035 * @param {String} text String to write out inside the doctype.
28037 doctype: function(text) {
28038 this.html.push('<!DOCTYPE', text, '>', this.indent != '' ? '\n' : '');
28041 * Resets the internal buffer if one wants to reuse the writer.
28045 reset: function() {
28046 this.html.length = 0;
28055 * Returns the contents that got serialized.
28057 * @method getContent
28058 * @return {String} HTML contents that got written down.
28060 getContent: function() {
28061 return this.html.join('').replace(/\n$/, '');
28064 pushState : function(cfg)
28066 this.state.push(cfg);
28067 Roo.apply(this, cfg);
28070 popState : function()
28072 if (this.state.length < 1) {
28073 return; // nothing to push
28080 if (this.state.length > 0) {
28081 cfg = this.state[this.state.length-1];
28083 Roo.apply(this, cfg);
28086 addLine: function()
28088 if (this.html.length < 1) {
28093 var value = this.html[this.html.length - 1];
28094 if (value.length > 0 && '\n' !== value) {
28095 this.html.push('\n');
28100 //'pre script noscript style textarea video audio iframe object code'
28101 // shortended... 'area base basefont br col frame hr img input isindex link meta param embed source wbr track');
28105 Roo.htmleditor.TidyWriter.inline_elements = [
28106 'SPAN','STRONG','B','EM','I','FONT','STRIKE','U','VAR',
28107 'CITE','DFN','CODE','MARK','Q','SUP','SUB','SAMP', 'A'
28109 Roo.htmleditor.TidyWriter.shortend_elements = [
28110 'AREA','BASE','BASEFONT','BR','COL','FRAME','HR','IMG','INPUT',
28111 'ISINDEX','LINK','','META','PARAM','EMBED','SOURCE','WBR','TRACK'
28114 Roo.htmleditor.TidyWriter.whitespace_elements = [
28115 'PRE','SCRIPT','NOSCRIPT','STYLE','TEXTAREA','VIDEO','AUDIO','IFRAME','OBJECT','CODE'
28117 * This is based loosely on tinymce
28118 * @class Roo.htmleditor.TidyEntities
28120 * https://github.com/thorn0/tinymce.html/blob/master/tinymce.html.js
28122 * Not 100% sure this is actually used or needed.
28125 Roo.htmleditor.TidyEntities = {
28128 * initialize data..
28130 init : function (){
28132 this.namedEntities = this.buildEntitiesLookup(this.namedEntitiesData, 32);
28137 buildEntitiesLookup: function(items, radix) {
28138 var i, chr, entity, lookup = {};
28142 items = typeof(items) == 'string' ? items.split(',') : items;
28143 radix = radix || 10;
28144 // Build entities lookup table
28145 for (i = 0; i < items.length; i += 2) {
28146 chr = String.fromCharCode(parseInt(items[i], radix));
28147 // Only add non base entities
28148 if (!this.baseEntities[chr]) {
28149 entity = '&' + items[i + 1] + ';';
28150 lookup[chr] = entity;
28151 lookup[entity] = chr;
28190 // Needs to be escaped since the YUI compressor would otherwise break the code
28197 // Reverse lookup table for raw entities
28198 reverseEntities : {
28206 attrsCharsRegExp : /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28207 textCharsRegExp : /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
28208 rawCharsRegExp : /[<>&\"\']/g,
28209 entityRegExp : /&#([a-z0-9]+);?|&([a-z0-9]+);/gi,
28210 namedEntities : false,
28211 namedEntitiesData : [
28712 * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
28714 * @method encodeRaw
28715 * @param {String} text Text to encode.
28716 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28717 * @return {String} Entity encoded text.
28719 encodeRaw: function(text, attr)
28722 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28723 return t.baseEntities[chr] || chr;
28727 * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
28728 * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
28729 * and is exposed as the DOMUtils.encode function.
28731 * @method encodeAllRaw
28732 * @param {String} text Text to encode.
28733 * @return {String} Entity encoded text.
28735 encodeAllRaw: function(text) {
28737 return ('' + text).replace(this.rawCharsRegExp, function(chr) {
28738 return t.baseEntities[chr] || chr;
28742 * Encodes the specified string using numeric entities. The core entities will be
28743 * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
28745 * @method encodeNumeric
28746 * @param {String} text Text to encode.
28747 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28748 * @return {String} Entity encoded text.
28750 encodeNumeric: function(text, attr) {
28752 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28753 // Multi byte sequence convert it to a single entity
28754 if (chr.length > 1) {
28755 return '&#' + (1024 * (chr.charCodeAt(0) - 55296) + (chr.charCodeAt(1) - 56320) + 65536) + ';';
28757 return t.baseEntities[chr] || '&#' + chr.charCodeAt(0) + ';';
28761 * Encodes the specified string using named entities. The core entities will be encoded
28762 * as named ones but all non lower ascii characters will be encoded into named entities.
28764 * @method encodeNamed
28765 * @param {String} text Text to encode.
28766 * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
28767 * @param {Object} entities Optional parameter with entities to use.
28768 * @return {String} Entity encoded text.
28770 encodeNamed: function(text, attr, entities) {
28772 entities = entities || this.namedEntities;
28773 return text.replace(attr ? this.attrsCharsRegExp : this.textCharsRegExp, function(chr) {
28774 return t.baseEntities[chr] || entities[chr] || chr;
28778 * Returns an encode function based on the name(s) and it's optional entities.
28780 * @method getEncodeFunc
28781 * @param {String} name Comma separated list of encoders for example named,numeric.
28782 * @param {String} entities Optional parameter with entities to use instead of the built in set.
28783 * @return {function} Encode function to be used.
28785 getEncodeFunc: function(name, entities) {
28786 entities = this.buildEntitiesLookup(entities) || this.namedEntities;
28788 function encodeNamedAndNumeric(text, attr) {
28789 return text.replace(attr ? t.attrsCharsRegExp : t.textCharsRegExp, function(chr) {
28790 return t.baseEntities[chr] || entities[chr] || '&#' + chr.charCodeAt(0) + ';' || chr;
28794 function encodeCustomNamed(text, attr) {
28795 return t.encodeNamed(text, attr, entities);
28797 // Replace + with , to be compatible with previous TinyMCE versions
28798 name = this.makeMap(name.replace(/\+/g, ','));
28799 // Named and numeric encoder
28800 if (name.named && name.numeric) {
28801 return this.encodeNamedAndNumeric;
28807 return encodeCustomNamed;
28809 return this.encodeNamed;
28812 if (name.numeric) {
28813 return this.encodeNumeric;
28816 return this.encodeRaw;
28819 * Decodes the specified string, this will replace entities with raw UTF characters.
28822 * @param {String} text Text to entity decode.
28823 * @return {String} Entity decoded string.
28825 decode: function(text)
28828 return text.replace(this.entityRegExp, function(all, numeric) {
28830 numeric = 'x' === numeric.charAt(0).toLowerCase() ? parseInt(numeric.substr(1), 16) : parseInt(numeric, 10);
28831 // Support upper UTF
28832 if (numeric > 65535) {
28834 return String.fromCharCode(55296 + (numeric >> 10), 56320 + (1023 & numeric));
28836 return t.asciiMap[numeric] || String.fromCharCode(numeric);
28838 return t.reverseEntities[all] || t.namedEntities[all] || t.nativeDecode(all);
28841 nativeDecode : function (text) {
28844 makeMap : function (items, delim, map) {
28846 items = items || [];
28847 delim = delim || ',';
28848 if (typeof items == "string") {
28849 items = items.split(delim);
28854 map[items[i]] = {};
28862 Roo.htmleditor.TidyEntities.init();
28864 * @class Roo.htmleditor.KeyEnter
28865 * Handle Enter press..
28866 * @cfg {Roo.HtmlEditorCore} core the editor.
28868 * Create a new Filter.
28869 * @param {Object} config Configuration options
28876 Roo.htmleditor.KeyEnter = function(cfg) {
28877 Roo.apply(this, cfg);
28878 // this does not actually call walk as it's really just a abstract class
28880 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
28883 //Roo.htmleditor.KeyEnter.i = 0;
28886 Roo.htmleditor.KeyEnter.prototype = {
28890 keypress : function(e)
28892 if (e.charCode != 13 && e.charCode != 10) {
28893 Roo.log([e.charCode,e]);
28896 e.preventDefault();
28897 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
28898 var doc = this.core.doc;
28902 var sel = this.core.getSelection();
28903 var range = sel.getRangeAt(0);
28904 var n = range.commonAncestorContainer;
28905 var pc = range.closest([ 'ol', 'ul']);
28906 var pli = range.closest('li');
28907 if (!pc || e.ctrlKey) {
28908 // on it list, or ctrl pressed.
28910 sel.insertNode('br', 'after');
28912 // only do this if we have ctrl key..
28913 var br = doc.createElement('br');
28914 br.className = 'clear';
28915 br.setAttribute('style', 'clear: both');
28916 sel.insertNode(br, 'after');
28920 this.core.undoManager.addEvent();
28921 this.core.fireEditorEvent(e);
28925 // deal with <li> insetion
28926 if (pli.innerText.trim() == '' &&
28927 pli.previousSibling &&
28928 pli.previousSibling.nodeName == 'LI' &&
28929 pli.previousSibling.innerText.trim() == '') {
28930 pli.parentNode.removeChild(pli.previousSibling);
28931 sel.cursorAfter(pc);
28932 this.core.undoManager.addEvent();
28933 this.core.fireEditorEvent(e);
28937 var li = doc.createElement('LI');
28938 li.innerHTML = ' ';
28939 if (!pli || !pli.firstSibling) {
28940 pc.appendChild(li);
28942 pli.parentNode.insertBefore(li, pli.firstSibling);
28944 sel.cursorText (li.firstChild);
28946 this.core.undoManager.addEvent();
28947 this.core.fireEditorEvent(e);
28959 * @class Roo.htmleditor.Block
28960 * Base class for html editor blocks - do not use it directly .. extend it..
28961 * @cfg {DomElement} node The node to apply stuff to.
28962 * @cfg {String} friendly_name the name that appears in the context bar about this block
28963 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
28966 * Create a new Filter.
28967 * @param {Object} config Configuration options
28970 Roo.htmleditor.Block = function(cfg)
28972 // do nothing .. should not be called really.
28975 * factory method to get the block from an element (using cache if necessary)
28977 * @param {HtmlElement} the dom element
28979 Roo.htmleditor.Block.factory = function(node)
28981 var cc = Roo.htmleditor.Block.cache;
28982 var id = Roo.get(node).id;
28983 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
28984 Roo.htmleditor.Block.cache[id].readElement(node);
28985 return Roo.htmleditor.Block.cache[id];
28987 var db = node.getAttribute('data-block');
28989 db = node.nodeName.toLowerCase().toUpperCaseFirst();
28991 var cls = Roo.htmleditor['Block' + db];
28992 if (typeof(cls) == 'undefined') {
28993 //Roo.log(node.getAttribute('data-block'));
28994 Roo.log("OOps missing block : " + 'Block' + db);
28997 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
28998 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
29002 * initalize all Elements from content that are 'blockable'
29004 * @param the body element
29006 Roo.htmleditor.Block.initAll = function(body, type)
29008 if (typeof(type) == 'undefined') {
29009 var ia = Roo.htmleditor.Block.initAll;
29015 Roo.each(Roo.get(body).query(type), function(e) {
29016 Roo.htmleditor.Block.factory(e);
29019 // question goes here... do we need to clear out this cache sometimes?
29020 // or show we make it relivant to the htmleditor.
29021 Roo.htmleditor.Block.cache = {};
29023 Roo.htmleditor.Block.prototype = {
29027 // used by context menu
29028 friendly_name : 'Based Block',
29030 // text for button to delete this element
29031 deleteTitle : false,
29035 * Update a node with values from this object
29036 * @param {DomElement} node
29038 updateElement : function(node)
29040 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
29043 * convert to plain HTML for calling insertAtCursor..
29045 toHTML : function()
29047 return Roo.DomHelper.markup(this.toObject());
29050 * used by readEleemnt to extract data from a node
29051 * may need improving as it's pretty basic
29053 * @param {DomElement} node
29054 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
29055 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
29056 * @param {String} style the style property - eg. text-align
29058 getVal : function(node, tag, attr, style)
29061 if (tag !== true && n.tagName != tag.toUpperCase()) {
29062 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
29063 // but kiss for now.
29064 n = node.getElementsByTagName(tag).item(0);
29069 if (attr === false) {
29072 if (attr == 'html') {
29073 return n.innerHTML;
29075 if (attr == 'style') {
29076 return n.style[style];
29079 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
29083 * create a DomHelper friendly object - for use with
29084 * Roo.DomHelper.markup / overwrite / etc..
29087 toObject : function()
29092 * Read a node that has a 'data-block' property - and extract the values from it.
29093 * @param {DomElement} node - the node
29095 readElement : function(node)
29106 * @class Roo.htmleditor.BlockFigure
29107 * Block that has an image and a figcaption
29108 * @cfg {String} image_src the url for the image
29109 * @cfg {String} align (left|right) alignment for the block default left
29110 * @cfg {String} caption the text to appear below (and in the alt tag)
29111 * @cfg {String} caption_display (block|none) display or not the caption
29112 * @cfg {String|number} image_width the width of the image number or %?
29113 * @cfg {String|number} image_height the height of the image number or %?
29116 * Create a new Filter.
29117 * @param {Object} config Configuration options
29120 Roo.htmleditor.BlockFigure = function(cfg)
29123 this.readElement(cfg.node);
29124 this.updateElement(cfg.node);
29126 Roo.apply(this, cfg);
29128 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
29135 caption_display : 'block',
29141 // margin: '2%', not used
29143 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
29146 // used by context menu
29147 friendly_name : 'Image with caption',
29148 deleteTitle : "Delete Image and Caption",
29150 contextMenu : function(toolbar)
29153 var block = function() {
29154 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29158 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29160 var syncValue = toolbar.editorcore.syncValue;
29166 xtype : 'TextItem',
29168 xns : rooui.Toolbar //Boostrap?
29172 text: 'Change Image URL',
29175 click: function (btn, state)
29179 Roo.MessageBox.show({
29180 title : "Image Source URL",
29181 msg : "Enter the url for the image",
29182 buttons: Roo.MessageBox.OKCANCEL,
29183 fn: function(btn, val){
29190 toolbar.editorcore.onEditorEvent();
29194 //multiline: multiline,
29196 value : b.image_src
29200 xns : rooui.Toolbar
29205 text: 'Change Link URL',
29208 click: function (btn, state)
29212 Roo.MessageBox.show({
29213 title : "Link URL",
29214 msg : "Enter the url for the link - leave blank to have no link",
29215 buttons: Roo.MessageBox.OKCANCEL,
29216 fn: function(btn, val){
29223 toolbar.editorcore.onEditorEvent();
29227 //multiline: multiline,
29233 xns : rooui.Toolbar
29237 text: 'Show Video URL',
29240 click: function (btn, state)
29242 Roo.MessageBox.alert("Video URL",
29243 block().video_url == '' ? 'This image is not linked ot a video' :
29244 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
29247 xns : rooui.Toolbar
29252 xtype : 'TextItem',
29254 xns : rooui.Toolbar //Boostrap?
29257 xtype : 'ComboBox',
29258 allowBlank : false,
29259 displayField : 'val',
29262 triggerAction : 'all',
29264 valueField : 'val',
29268 select : function (combo, r, index)
29270 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29272 b.width = r.get('val');
29275 toolbar.editorcore.onEditorEvent();
29280 xtype : 'SimpleStore',
29293 xtype : 'TextItem',
29295 xns : rooui.Toolbar //Boostrap?
29298 xtype : 'ComboBox',
29299 allowBlank : false,
29300 displayField : 'val',
29303 triggerAction : 'all',
29305 valueField : 'val',
29309 select : function (combo, r, index)
29311 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29313 b.align = r.get('val');
29316 toolbar.editorcore.onEditorEvent();
29321 xtype : 'SimpleStore',
29335 text: 'Hide Caption',
29336 name : 'caption_display',
29338 enableToggle : true,
29339 setValue : function(v) {
29340 // this trigger toggle.
29342 this.setText(v ? "Hide Caption" : "Show Caption");
29343 this.setPressed(v != 'block');
29346 toggle: function (btn, state)
29349 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
29350 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
29353 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29354 toolbar.editorcore.onEditorEvent();
29357 xns : rooui.Toolbar
29363 * create a DomHelper friendly object - for use with
29364 * Roo.DomHelper.markup / overwrite / etc..
29366 toObject : function()
29368 var d = document.createElement('div');
29369 d.innerHTML = this.caption;
29371 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
29373 var iw = this.align == 'center' ? this.width : '100%';
29376 contenteditable : 'false',
29377 src : this.image_src,
29378 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
29381 maxWidth : iw + ' !important', // this is not getting rendered?
29387 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
29389 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
29394 if (this.href.length > 0) {
29398 contenteditable : 'true',
29406 if (this.video_url.length > 0) {
29411 allowfullscreen : true,
29412 width : 420, // these are for video tricks - that we replace the outer
29414 src : this.video_url,
29420 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
29421 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
29426 'data-block' : 'Figure',
29427 'data-width' : this.width,
29428 contenteditable : 'false',
29432 float : this.align ,
29433 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
29434 width : this.align == 'center' ? '100%' : this.width,
29436 padding: this.align == 'center' ? '0' : '0 10px' ,
29437 textAlign : this.align // seems to work for email..
29442 align : this.align,
29448 'data-display' : this.caption_display,
29450 textAlign : 'left',
29452 lineHeight : '24px',
29453 display : this.caption_display,
29454 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
29456 width: this.align == 'center' ? this.width : '100%'
29460 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
29465 marginTop : '16px',
29471 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
29473 contenteditable : true,
29489 readElement : function(node)
29491 // this should not really come from the link...
29492 this.video_url = this.getVal(node, 'div', 'src');
29493 this.cls = this.getVal(node, 'div', 'class');
29494 this.href = this.getVal(node, 'a', 'href');
29497 this.image_src = this.getVal(node, 'img', 'src');
29499 this.align = this.getVal(node, 'figure', 'align');
29500 var figcaption = this.getVal(node, 'figcaption', false);
29501 if (figcaption !== '') {
29502 this.caption = this.getVal(figcaption, 'i', 'html');
29506 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
29507 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
29508 this.width = this.getVal(node, true, 'data-width');
29509 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
29512 removeNode : function()
29529 * @class Roo.htmleditor.BlockTable
29530 * Block that manages a table
29533 * Create a new Filter.
29534 * @param {Object} config Configuration options
29537 Roo.htmleditor.BlockTable = function(cfg)
29540 this.readElement(cfg.node);
29541 this.updateElement(cfg.node);
29543 Roo.apply(this, cfg);
29546 for(var r = 0; r < this.no_row; r++) {
29548 for(var c = 0; c < this.no_col; c++) {
29549 this.rows[r][c] = this.emptyCell();
29556 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
29565 // used by context menu
29566 friendly_name : 'Table',
29567 deleteTitle : 'Delete Table',
29568 // context menu is drawn once..
29570 contextMenu : function(toolbar)
29573 var block = function() {
29574 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
29578 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
29580 var syncValue = toolbar.editorcore.syncValue;
29586 xtype : 'TextItem',
29588 xns : rooui.Toolbar //Boostrap?
29591 xtype : 'ComboBox',
29592 allowBlank : false,
29593 displayField : 'val',
29596 triggerAction : 'all',
29598 valueField : 'val',
29602 select : function (combo, r, index)
29604 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29606 b.width = r.get('val');
29609 toolbar.editorcore.onEditorEvent();
29614 xtype : 'SimpleStore',
29626 xtype : 'TextItem',
29627 text : "Columns: ",
29628 xns : rooui.Toolbar //Boostrap?
29635 click : function (_self, e)
29637 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29638 block().removeColumn();
29640 toolbar.editorcore.onEditorEvent();
29643 xns : rooui.Toolbar
29649 click : function (_self, e)
29651 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29652 block().addColumn();
29654 toolbar.editorcore.onEditorEvent();
29657 xns : rooui.Toolbar
29661 xtype : 'TextItem',
29663 xns : rooui.Toolbar //Boostrap?
29670 click : function (_self, e)
29672 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
29673 block().removeRow();
29675 toolbar.editorcore.onEditorEvent();
29678 xns : rooui.Toolbar
29684 click : function (_self, e)
29688 toolbar.editorcore.onEditorEvent();
29691 xns : rooui.Toolbar
29696 text: 'Reset Column Widths',
29699 click : function (_self, e)
29701 block().resetWidths();
29703 toolbar.editorcore.onEditorEvent();
29706 xns : rooui.Toolbar
29717 * create a DomHelper friendly object - for use with
29718 * Roo.DomHelper.markup / overwrite / etc..
29719 * ?? should it be called with option to hide all editing features?
29721 toObject : function()
29726 contenteditable : 'false', // this stops cell selection from picking the table.
29727 'data-block' : 'Table',
29730 border : 'solid 1px #000', // ??? hard coded?
29731 'border-collapse' : 'collapse'
29734 { tag : 'tbody' , cn : [] }
29738 // do we have a head = not really
29740 Roo.each(this.rows, function( row ) {
29745 border : 'solid 1px #000',
29751 ret.cn[0].cn.push(tr);
29752 // does the row have any properties? ?? height?
29754 Roo.each(row, function( cell ) {
29758 contenteditable : 'true',
29759 'data-block' : 'Td',
29763 if (cell.colspan > 1) {
29764 td.colspan = cell.colspan ;
29765 nc += cell.colspan;
29769 if (cell.rowspan > 1) {
29770 td.rowspan = cell.rowspan ;
29779 ncols = Math.max(nc, ncols);
29783 // add the header row..
29792 readElement : function(node)
29794 node = node ? node : this.node ;
29795 this.width = this.getVal(node, true, 'style', 'width') || '100%';
29799 var trs = Array.from(node.rows);
29800 trs.forEach(function(tr) {
29802 this.rows.push(row);
29806 Array.from(tr.cells).forEach(function(td) {
29809 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
29810 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
29811 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
29812 html : td.innerHTML
29814 no_column += add.colspan;
29821 this.no_col = Math.max(this.no_col, no_column);
29828 normalizeRows: function()
29832 this.rows.forEach(function(row) {
29835 row = this.normalizeRow(row);
29837 row.forEach(function(c) {
29838 while (typeof(ret[rid][cid]) != 'undefined') {
29841 if (typeof(ret[rid]) == 'undefined') {
29847 if (c.rowspan < 2) {
29851 for(var i = 1 ;i < c.rowspan; i++) {
29852 if (typeof(ret[rid+i]) == 'undefined') {
29855 ret[rid+i][cid] = c;
29863 normalizeRow: function(row)
29866 row.forEach(function(c) {
29867 if (c.colspan < 2) {
29871 for(var i =0 ;i < c.colspan; i++) {
29879 deleteColumn : function(sel)
29881 if (!sel || sel.type != 'col') {
29884 if (this.no_col < 2) {
29888 this.rows.forEach(function(row) {
29889 var cols = this.normalizeRow(row);
29890 var col = cols[sel.col];
29891 if (col.colspan > 1) {
29901 removeColumn : function()
29903 this.deleteColumn({
29905 col : this.no_col-1
29907 this.updateElement();
29911 addColumn : function()
29914 this.rows.forEach(function(row) {
29915 row.push(this.emptyCell());
29918 this.updateElement();
29921 deleteRow : function(sel)
29923 if (!sel || sel.type != 'row') {
29927 if (this.no_row < 2) {
29931 var rows = this.normalizeRows();
29934 rows[sel.row].forEach(function(col) {
29935 if (col.rowspan > 1) {
29938 col.remove = 1; // flage it as removed.
29943 this.rows.forEach(function(row) {
29945 row.forEach(function(c) {
29946 if (typeof(c.remove) == 'undefined') {
29951 if (newrow.length > 0) {
29955 this.rows = newrows;
29960 this.updateElement();
29963 removeRow : function()
29967 row : this.no_row-1
29973 addRow : function()
29977 for (var i = 0; i < this.no_col; i++ ) {
29979 row.push(this.emptyCell());
29982 this.rows.push(row);
29983 this.updateElement();
29987 // the default cell object... at present...
29988 emptyCell : function() {
29989 return (new Roo.htmleditor.BlockTd({})).toObject();
29994 removeNode : function()
30001 resetWidths : function()
30003 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
30004 var nn = Roo.htmleditor.Block.factory(n);
30006 nn.updateElement(n);
30019 * since selections really work on the table cell, then editing really should work from there
30021 * The original plan was to support merging etc... - but that may not be needed yet..
30023 * So this simple version will support:
30025 * adjust the width +/-
30026 * reset the width...
30035 * @class Roo.htmleditor.BlockTable
30036 * Block that manages a table
30039 * Create a new Filter.
30040 * @param {Object} config Configuration options
30043 Roo.htmleditor.BlockTd = function(cfg)
30046 this.readElement(cfg.node);
30047 this.updateElement(cfg.node);
30049 Roo.apply(this, cfg);
30054 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
30059 textAlign : 'left',
30066 // used by context menu
30067 friendly_name : 'Table Cell',
30068 deleteTitle : false, // use our customer delete
30070 // context menu is drawn once..
30072 contextMenu : function(toolbar)
30075 var cell = function() {
30076 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
30079 var table = function() {
30080 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
30084 var saveSel = function()
30086 lr = toolbar.editorcore.getSelection().getRangeAt(0);
30088 var restoreSel = function()
30092 toolbar.editorcore.focus();
30093 var cr = toolbar.editorcore.getSelection();
30094 cr.removeAllRanges();
30096 toolbar.editorcore.onEditorEvent();
30097 }).defer(10, this);
30103 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
30105 var syncValue = toolbar.editorcore.syncValue;
30112 text : 'Edit Table',
30114 click : function() {
30115 var t = toolbar.tb.selectedNode.closest('table');
30116 toolbar.editorcore.selectNode(t);
30117 toolbar.editorcore.onEditorEvent();
30126 xtype : 'TextItem',
30127 text : "Column Width: ",
30128 xns : rooui.Toolbar
30135 click : function (_self, e)
30137 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30138 cell().shrinkColumn();
30140 toolbar.editorcore.onEditorEvent();
30143 xns : rooui.Toolbar
30149 click : function (_self, e)
30151 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30152 cell().growColumn();
30154 toolbar.editorcore.onEditorEvent();
30157 xns : rooui.Toolbar
30161 xtype : 'TextItem',
30162 text : "Vertical Align: ",
30163 xns : rooui.Toolbar //Boostrap?
30166 xtype : 'ComboBox',
30167 allowBlank : false,
30168 displayField : 'val',
30171 triggerAction : 'all',
30173 valueField : 'val',
30177 select : function (combo, r, index)
30179 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30181 b.valign = r.get('val');
30184 toolbar.editorcore.onEditorEvent();
30189 xtype : 'SimpleStore',
30193 ['bottom'] // there are afew more...
30201 xtype : 'TextItem',
30202 text : "Merge Cells: ",
30203 xns : rooui.Toolbar
30212 click : function (_self, e)
30214 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30215 cell().mergeRight();
30216 //block().growColumn();
30218 toolbar.editorcore.onEditorEvent();
30221 xns : rooui.Toolbar
30228 click : function (_self, e)
30230 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30231 cell().mergeBelow();
30232 //block().growColumn();
30234 toolbar.editorcore.onEditorEvent();
30237 xns : rooui.Toolbar
30240 xtype : 'TextItem',
30242 xns : rooui.Toolbar
30250 click : function (_self, e)
30252 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30255 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
30256 toolbar.editorcore.onEditorEvent();
30260 xns : rooui.Toolbar
30264 xns : rooui.Toolbar
30273 xns : rooui.Toolbar,
30282 click : function (_self, e)
30286 cell().deleteColumn();
30288 toolbar.editorcore.selectNode(t.node);
30289 toolbar.editorcore.onEditorEvent();
30298 click : function (_self, e)
30301 cell().deleteRow();
30304 toolbar.editorcore.selectNode(t.node);
30305 toolbar.editorcore.onEditorEvent();
30312 xtype : 'Separator',
30319 click : function (_self, e)
30322 var nn = t.node.nextSibling || t.node.previousSibling;
30323 t.node.parentNode.removeChild(t.node);
30325 toolbar.editorcore.selectNode(nn, true);
30327 toolbar.editorcore.onEditorEvent();
30337 // align... << fixme
30345 * create a DomHelper friendly object - for use with
30346 * Roo.DomHelper.markup / overwrite / etc..
30347 * ?? should it be called with option to hide all editing features?
30350 * create a DomHelper friendly object - for use with
30351 * Roo.DomHelper.markup / overwrite / etc..
30352 * ?? should it be called with option to hide all editing features?
30354 toObject : function()
30358 contenteditable : 'true', // this stops cell selection from picking the table.
30359 'data-block' : 'Td',
30360 valign : this.valign,
30362 'text-align' : this.textAlign,
30363 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
30364 'border-collapse' : 'collapse',
30365 padding : '6px', // 8 for desktop / 4 for mobile
30366 'vertical-align': this.valign
30370 if (this.width != '') {
30371 ret.width = this.width;
30372 ret.style.width = this.width;
30376 if (this.colspan > 1) {
30377 ret.colspan = this.colspan ;
30379 if (this.rowspan > 1) {
30380 ret.rowspan = this.rowspan ;
30389 readElement : function(node)
30391 node = node ? node : this.node ;
30392 this.width = node.style.width;
30393 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
30394 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
30395 this.html = node.innerHTML;
30396 if (node.style.textAlign != '') {
30397 this.textAlign = node.style.textAlign;
30403 // the default cell object... at present...
30404 emptyCell : function() {
30408 textAlign : 'left',
30409 html : " " // is this going to be editable now?
30414 removeNode : function()
30416 return this.node.closest('table');
30424 toTableArray : function()
30427 var tab = this.node.closest('tr').closest('table');
30428 Array.from(tab.rows).forEach(function(r, ri){
30432 this.colWidths = [];
30433 var all_auto = true;
30434 Array.from(tab.rows).forEach(function(r, ri){
30437 Array.from(r.cells).forEach(function(ce, ci){
30442 colspan : ce.colSpan,
30443 rowspan : ce.rowSpan
30445 if (ce.isEqualNode(this.node)) {
30448 // if we have been filled up by a row?
30449 if (typeof(ret[rn][cn]) != 'undefined') {
30450 while(typeof(ret[rn][cn]) != 'undefined') {
30456 if (typeof(this.colWidths[cn]) == 'undefined' && c.colspan < 2) {
30457 this.colWidths[cn] = ce.style.width;
30458 if (this.colWidths[cn] != '') {
30464 if (c.colspan < 2 && c.rowspan < 2 ) {
30469 for(var j = 0; j < c.rowspan; j++) {
30470 if (typeof(ret[rn+j]) == 'undefined') {
30471 continue; // we have a problem..
30474 for(var i = 0; i < c.colspan; i++) {
30475 ret[rn+j][cn+i] = c;
30484 // initalize widths.?
30485 // either all widths or no widths..
30487 this.colWidths[0] = false; // no widths flag.
30498 mergeRight: function()
30501 // get the contents of the next cell along..
30502 var tr = this.node.closest('tr');
30503 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
30504 if (i >= tr.childNodes.length - 1) {
30505 return; // no cells on right to merge with.
30507 var table = this.toTableArray();
30509 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
30510 return; // nothing right?
30512 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
30513 // right cell - must be same rowspan and on the same row.
30514 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
30515 return; // right hand side is not same rowspan.
30520 this.node.innerHTML += ' ' + rc.cell.innerHTML;
30521 tr.removeChild(rc.cell);
30522 this.colspan += rc.colspan;
30523 this.node.setAttribute('colspan', this.colspan);
30525 var table = this.toTableArray();
30526 this.normalizeWidths(table);
30527 this.updateWidths(table);
30531 mergeBelow : function()
30533 var table = this.toTableArray();
30534 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
30535 return; // no row below
30537 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
30538 return; // nothing right?
30540 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
30542 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
30543 return; // right hand side is not same rowspan.
30545 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
30546 rc.cell.parentNode.removeChild(rc.cell);
30547 this.rowspan += rc.rowspan;
30548 this.node.setAttribute('rowspan', this.rowspan);
30553 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
30556 var table = this.toTableArray();
30557 var cd = this.cellData;
30561 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
30564 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
30565 if (r == cd.row && c == cd.col) {
30566 this.node.removeAttribute('rowspan');
30567 this.node.removeAttribute('colspan');
30570 var ntd = this.node.cloneNode(); // which col/row should be 0..
30571 ntd.removeAttribute('id');
30572 ntd.style.width = this.colWidths[c];
30573 ntd.innerHTML = '';
30574 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
30578 this.redrawAllCells(table);
30584 redrawAllCells: function(table)
30588 var tab = this.node.closest('tr').closest('table');
30589 var ctr = tab.rows[0].parentNode;
30590 Array.from(tab.rows).forEach(function(r, ri){
30592 Array.from(r.cells).forEach(function(ce, ci){
30593 ce.parentNode.removeChild(ce);
30595 r.parentNode.removeChild(r);
30597 for(var r = 0 ; r < table.length; r++) {
30598 var re = tab.rows[r];
30600 var re = tab.ownerDocument.createElement('tr');
30601 ctr.appendChild(re);
30602 for(var c = 0 ; c < table[r].length; c++) {
30603 if (table[r][c].cell === false) {
30607 re.appendChild(table[r][c].cell);
30609 table[r][c].cell = false;
30614 updateWidths : function(table)
30616 for(var r = 0 ; r < table.length; r++) {
30618 for(var c = 0 ; c < table[r].length; c++) {
30619 if (table[r][c].cell === false) {
30623 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
30624 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30625 el.width = Math.floor(this.colWidths[c]) +'%';
30626 el.updateElement(el.node);
30628 if (this.colWidths[0] != false && table[r][c].colspan > 1) {
30629 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
30631 for(var i = 0; i < table[r][c].colspan; i ++) {
30632 width += Math.floor(this.colWidths[c + i]);
30634 el.width = width +'%';
30635 el.updateElement(el.node);
30637 table[r][c].cell = false; // done
30641 normalizeWidths : function(table)
30643 if (this.colWidths[0] === false) {
30644 var nw = 100.0 / this.colWidths.length;
30645 this.colWidths.forEach(function(w,i) {
30646 this.colWidths[i] = nw;
30651 var t = 0, missing = [];
30653 this.colWidths.forEach(function(w,i) {
30655 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
30656 var add = this.colWidths[i];
30665 var nc = this.colWidths.length;
30666 if (missing.length) {
30667 var mult = (nc - missing.length) / (1.0 * nc);
30669 var ew = (100 -t) / (1.0 * missing.length);
30670 this.colWidths.forEach(function(w,i) {
30672 this.colWidths[i] = w * mult;
30676 this.colWidths[i] = ew;
30678 // have to make up numbers..
30681 // now we should have all the widths..
30686 shrinkColumn : function()
30688 var table = this.toTableArray();
30689 this.normalizeWidths(table);
30690 var col = this.cellData.col;
30691 var nw = this.colWidths[col] * 0.8;
30695 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30696 this.colWidths.forEach(function(w,i) {
30698 this.colWidths[i] = nw;
30701 this.colWidths[i] += otherAdd
30703 this.updateWidths(table);
30706 growColumn : function()
30708 var table = this.toTableArray();
30709 this.normalizeWidths(table);
30710 var col = this.cellData.col;
30711 var nw = this.colWidths[col] * 1.2;
30715 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
30716 this.colWidths.forEach(function(w,i) {
30718 this.colWidths[i] = nw;
30721 this.colWidths[i] -= otherSub
30723 this.updateWidths(table);
30726 deleteRow : function()
30728 // delete this rows 'tr'
30729 // if any of the cells in this row have a rowspan > 1 && row!= this row..
30730 // then reduce the rowspan.
30731 var table = this.toTableArray();
30732 // this.cellData.row;
30733 for (var i =0;i< table[this.cellData.row].length ; i++) {
30734 var c = table[this.cellData.row][i];
30735 if (c.row != this.cellData.row) {
30738 c.cell.setAttribute('rowspan', c.rowspan);
30741 if (c.rowspan > 1) {
30743 c.cell.setAttribute('rowspan', c.rowspan);
30746 table.splice(this.cellData.row,1);
30747 this.redrawAllCells(table);
30750 deleteColumn : function()
30752 var table = this.toTableArray();
30754 for (var i =0;i< table.length ; i++) {
30755 var c = table[i][this.cellData.col];
30756 if (c.col != this.cellData.col) {
30757 table[i][this.cellData.col].colspan--;
30758 } else if (c.colspan > 1) {
30760 c.cell.setAttribute('colspan', c.colspan);
30762 table[i].splice(this.cellData.col,1);
30765 this.redrawAllCells(table);
30773 //<script type="text/javascript">
30776 * Based Ext JS Library 1.1.1
30777 * Copyright(c) 2006-2007, Ext JS, LLC.
30783 * @class Roo.HtmlEditorCore
30784 * @extends Roo.Component
30785 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
30787 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
30790 Roo.HtmlEditorCore = function(config){
30793 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
30798 * @event initialize
30799 * Fires when the editor is fully initialized (including the iframe)
30800 * @param {Roo.HtmlEditorCore} this
30805 * Fires when the editor is first receives the focus. Any insertion must wait
30806 * until after this event.
30807 * @param {Roo.HtmlEditorCore} this
30811 * @event beforesync
30812 * Fires before the textarea is updated with content from the editor iframe. Return false
30813 * to cancel the sync.
30814 * @param {Roo.HtmlEditorCore} this
30815 * @param {String} html
30819 * @event beforepush
30820 * Fires before the iframe editor is updated with content from the textarea. Return false
30821 * to cancel the push.
30822 * @param {Roo.HtmlEditorCore} this
30823 * @param {String} html
30828 * Fires when the textarea is updated with content from the editor iframe.
30829 * @param {Roo.HtmlEditorCore} this
30830 * @param {String} html
30835 * Fires when the iframe editor is updated with content from the textarea.
30836 * @param {Roo.HtmlEditorCore} this
30837 * @param {String} html
30842 * @event editorevent
30843 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30844 * @param {Roo.HtmlEditorCore} this
30851 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
30853 // defaults : white / black...
30854 this.applyBlacklists();
30861 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
30865 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
30871 * @cfg {String} css styling for resizing. (used on bootstrap only)
30875 * @cfg {Number} height (in pixels)
30879 * @cfg {Number} width (in pixels)
30883 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
30884 * if you are doing an email editor, this probably needs disabling, it's designed
30889 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
30891 enableBlocks : true,
30893 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30896 stylesheets: false,
30898 * @cfg {String} language default en - language of text (usefull for rtl languages)
30904 * @cfg {boolean} allowComments - default false - allow comments in HTML source
30905 * - by default they are stripped - if you are editing email you may need this.
30907 allowComments: false,
30911 // private properties
30912 validationEvent : false,
30914 initialized : false,
30916 sourceEditMode : false,
30917 onFocus : Roo.emptyFn,
30919 hideMode:'offsets',
30923 // blacklist + whitelisted elements..
30930 undoManager : false,
30932 * Protected method that will not generally be called directly. It
30933 * is called when the editor initializes the iframe with HTML contents. Override this method if you
30934 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
30936 getDocMarkup : function(){
30940 // inherit styels from page...??
30941 if (this.stylesheets === false) {
30943 Roo.get(document.head).select('style').each(function(node) {
30944 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30947 Roo.get(document.head).select('link').each(function(node) {
30948 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
30951 } else if (!this.stylesheets.length) {
30953 st = '<style type="text/css">' +
30954 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30957 for (var i in this.stylesheets) {
30958 if (typeof(this.stylesheets[i]) != 'string') {
30961 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
30966 st += '<style type="text/css">' +
30967 'IMG { cursor: pointer } ' +
30970 st += '<meta name="google" content="notranslate">';
30972 var cls = 'notranslate roo-htmleditor-body';
30974 if(this.bodyCls.length){
30975 cls += ' ' + this.bodyCls;
30978 return '<html class="notranslate" translate="no"><head>' + st +
30979 //<style type="text/css">' +
30980 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
30982 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
30986 onRender : function(ct, position)
30989 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
30990 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
30993 this.el.dom.style.border = '0 none';
30994 this.el.dom.setAttribute('tabIndex', -1);
30995 this.el.addClass('x-hidden hide');
30999 if(Roo.isIE){ // fix IE 1px bogus margin
31000 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
31004 this.frameId = Roo.id();
31008 cls: 'form-control', // bootstrap..
31010 name: this.frameId,
31011 frameBorder : 'no',
31012 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
31015 ifcfg.style = { resize : this.resize };
31018 var iframe = this.owner.wrap.createChild(ifcfg, this.el);
31021 this.iframe = iframe.dom;
31023 this.assignDocWin();
31025 this.doc.designMode = 'on';
31028 this.doc.write(this.getDocMarkup());
31032 var task = { // must defer to wait for browser to be ready
31034 //console.log("run task?" + this.doc.readyState);
31035 this.assignDocWin();
31036 if(this.doc.body || this.doc.readyState == 'complete'){
31038 this.doc.designMode="on";
31043 Roo.TaskMgr.stop(task);
31044 this.initEditor.defer(10, this);
31051 Roo.TaskMgr.start(task);
31056 onResize : function(w, h)
31058 Roo.log('resize: ' +w + ',' + h );
31059 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
31063 if(typeof w == 'number'){
31065 this.iframe.style.width = w + 'px';
31067 if(typeof h == 'number'){
31069 this.iframe.style.height = h + 'px';
31071 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
31078 * Toggles the editor between standard and source edit mode.
31079 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
31081 toggleSourceEdit : function(sourceEditMode){
31083 this.sourceEditMode = sourceEditMode === true;
31085 if(this.sourceEditMode){
31087 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
31090 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
31091 //this.iframe.className = '';
31094 //this.setSize(this.owner.wrap.getSize());
31095 //this.fireEvent('editmodechange', this, this.sourceEditMode);
31102 * Protected method that will not generally be called directly. If you need/want
31103 * custom HTML cleanup, this is the method you should override.
31104 * @param {String} html The HTML to be cleaned
31105 * return {String} The cleaned HTML
31107 cleanHtml : function(html)
31109 html = String(html);
31110 if(html.length > 5){
31111 if(Roo.isSafari){ // strip safari nonsense
31112 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
31115 if(html == ' '){
31122 * HTML Editor -> Textarea
31123 * Protected method that will not generally be called directly. Syncs the contents
31124 * of the editor iframe with the textarea.
31126 syncValue : function()
31128 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
31129 if(this.initialized){
31131 if (this.undoManager) {
31132 this.undoManager.addEvent();
31136 var bd = (this.doc.body || this.doc.documentElement);
31139 var sel = this.win.getSelection();
31141 var div = document.createElement('div');
31142 div.innerHTML = bd.innerHTML;
31143 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
31144 if (gtx.length > 0) {
31145 var rm = gtx.item(0).parentNode;
31146 rm.parentNode.removeChild(rm);
31150 if (this.enableBlocks) {
31151 new Roo.htmleditor.FilterBlock({ node : div });
31154 var html = div.innerHTML;
31157 if (this.autoClean) {
31159 new Roo.htmleditor.FilterAttributes({
31180 attrib_clean : ['href', 'src' ]
31183 var tidy = new Roo.htmleditor.TidySerializer({
31186 html = tidy.serialize(div);
31192 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
31193 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
31195 html = '<div style="'+m[0]+'">' + html + '</div>';
31198 html = this.cleanHtml(html);
31199 // fix up the special chars.. normaly like back quotes in word...
31200 // however we do not want to do this with chinese..
31201 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
31203 var cc = match.charCodeAt();
31205 // Get the character value, handling surrogate pairs
31206 if (match.length == 2) {
31207 // It's a surrogate pair, calculate the Unicode code point
31208 var high = match.charCodeAt(0) - 0xD800;
31209 var low = match.charCodeAt(1) - 0xDC00;
31210 cc = (high * 0x400) + low + 0x10000;
31212 (cc >= 0x4E00 && cc < 0xA000 ) ||
31213 (cc >= 0x3400 && cc < 0x4E00 ) ||
31214 (cc >= 0xf900 && cc < 0xfb00 )
31219 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
31220 return "&#" + cc + ";";
31227 if(this.owner.fireEvent('beforesync', this, html) !== false){
31228 this.el.dom.value = html;
31229 this.owner.fireEvent('sync', this, html);
31235 * TEXTAREA -> EDITABLE
31236 * Protected method that will not generally be called directly. Pushes the value of the textarea
31237 * into the iframe editor.
31239 pushValue : function()
31241 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
31242 if(this.initialized){
31243 var v = this.el.dom.value.trim();
31246 if(this.owner.fireEvent('beforepush', this, v) !== false){
31247 var d = (this.doc.body || this.doc.documentElement);
31250 this.el.dom.value = d.innerHTML;
31251 this.owner.fireEvent('push', this, v);
31253 if (this.autoClean) {
31254 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
31255 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
31257 if (this.enableBlocks) {
31258 Roo.htmleditor.Block.initAll(this.doc.body);
31261 this.updateLanguage();
31263 var lc = this.doc.body.lastChild;
31264 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
31265 // add an extra line at the end.
31266 this.doc.body.appendChild(this.doc.createElement('br'));
31274 deferFocus : function(){
31275 this.focus.defer(10, this);
31279 focus : function(){
31280 if(this.win && !this.sourceEditMode){
31287 assignDocWin: function()
31289 var iframe = this.iframe;
31292 this.doc = iframe.contentWindow.document;
31293 this.win = iframe.contentWindow;
31295 // if (!Roo.get(this.frameId)) {
31298 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31299 // this.win = Roo.get(this.frameId).dom.contentWindow;
31301 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
31305 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
31306 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
31311 initEditor : function(){
31312 //console.log("INIT EDITOR");
31313 this.assignDocWin();
31317 this.doc.designMode="on";
31319 this.doc.write(this.getDocMarkup());
31322 var dbody = (this.doc.body || this.doc.documentElement);
31323 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
31324 // this copies styles from the containing element into thsi one..
31325 // not sure why we need all of this..
31326 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
31328 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
31329 //ss['background-attachment'] = 'fixed'; // w3c
31330 dbody.bgProperties = 'fixed'; // ie
31331 dbody.setAttribute("translate", "no");
31333 //Roo.DomHelper.applyStyles(dbody, ss);
31334 Roo.EventManager.on(this.doc, {
31336 'mouseup': this.onEditorEvent,
31337 'dblclick': this.onEditorEvent,
31338 'click': this.onEditorEvent,
31339 'keyup': this.onEditorEvent,
31344 Roo.EventManager.on(this.doc, {
31345 'paste': this.onPasteEvent,
31349 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
31352 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
31353 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
31355 this.initialized = true;
31358 // initialize special key events - enter
31359 new Roo.htmleditor.KeyEnter({core : this});
31363 this.owner.fireEvent('initialize', this);
31366 // this is to prevent a href clicks resulting in a redirect?
31368 onPasteEvent : function(e,v)
31370 // I think we better assume paste is going to be a dirty load of rubish from word..
31372 // even pasting into a 'email version' of this widget will have to clean up that mess.
31373 var cd = (e.browserEvent.clipboardData || window.clipboardData);
31375 // check what type of paste - if it's an image, then handle it differently.
31376 if (cd.files && cd.files.length > 0) {
31378 var urlAPI = (window.createObjectURL && window) ||
31379 (window.URL && URL.revokeObjectURL && URL) ||
31380 (window.webkitURL && webkitURL);
31382 var r = new FileReader();
31384 r.addEventListener('load',function()
31387 var d = (new DOMParser().parseFromString('<img src="' + r.result+ '">', 'text/html')).body;
31388 // is insert asycn?
31389 if (t.enableBlocks) {
31391 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31392 if (img.closest('figure')) { // assume!! that it's aready
31395 var fig = new Roo.htmleditor.BlockFigure({
31396 image_src : img.src
31398 fig.updateElement(img); // replace it..
31402 t.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31403 t.owner.fireEvent('paste', this);
31405 r.readAsDataURL(cd.files[0]);
31407 e.preventDefault();
31411 if (cd.types.indexOf('text/html') < 0 ) {
31415 var html = cd.getData('text/html'); // clipboard event
31416 if (cd.types.indexOf('text/rtf') > -1) {
31417 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
31418 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
31423 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
31424 .map(function(g) { return g.toDataURL(); })
31425 .filter(function(g) { return g != 'about:blank'; });
31428 html = this.cleanWordChars(html);
31430 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
31433 var sn = this.getParentElement();
31434 // check if d contains a table, and prevent nesting??
31435 //Roo.log(d.getElementsByTagName('table'));
31437 //Roo.log(sn.closest('table'));
31438 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
31439 e.preventDefault();
31440 this.insertAtCursor("You can not nest tables");
31441 //Roo.log("prevent?"); // fixme -
31447 if (images.length > 0) {
31448 // replace all v:imagedata - with img.
31449 var ar = Array.from(d.getElementsByTagName('v:imagedata'));
31450 Roo.each(ar, function(node) {
31451 node.parentNode.insertBefore(d.ownerDocument.createElement('img'), node );
31452 node.parentNode.removeChild(node);
31456 Roo.each(d.getElementsByTagName('img'), function(img, i) {
31457 img.setAttribute('src', images[i]);
31460 if (this.autoClean) {
31461 new Roo.htmleditor.FilterWord({ node : d });
31463 new Roo.htmleditor.FilterStyleToTag({ node : d });
31464 new Roo.htmleditor.FilterAttributes({
31466 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width', 'start'],
31467 attrib_clean : ['href', 'src' ]
31469 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
31470 // should be fonts..
31471 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', ':' ]} );
31472 new Roo.htmleditor.FilterParagraph({ node : d });
31473 new Roo.htmleditor.FilterSpan({ node : d });
31474 new Roo.htmleditor.FilterLongBr({ node : d });
31475 new Roo.htmleditor.FilterComment({ node : d });
31479 if (this.enableBlocks) {
31481 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
31482 if (img.closest('figure')) { // assume!! that it's aready
31485 var fig = new Roo.htmleditor.BlockFigure({
31486 image_src : img.src
31488 fig.updateElement(img); // replace it..
31494 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
31495 if (this.enableBlocks) {
31496 Roo.htmleditor.Block.initAll(this.doc.body);
31500 e.preventDefault();
31501 this.owner.fireEvent('paste', this);
31503 // default behaveiour should be our local cleanup paste? (optional?)
31504 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
31505 //this.owner.fireEvent('paste', e, v);
31508 onDestroy : function(){
31514 //for (var i =0; i < this.toolbars.length;i++) {
31515 // // fixme - ask toolbars for heights?
31516 // this.toolbars[i].onDestroy();
31519 //this.wrap.dom.innerHTML = '';
31520 //this.wrap.remove();
31525 onFirstFocus : function(){
31527 this.assignDocWin();
31528 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
31530 this.activated = true;
31533 if(Roo.isGecko){ // prevent silly gecko errors
31535 var s = this.win.getSelection();
31536 if(!s.focusNode || s.focusNode.nodeType != 3){
31537 var r = s.getRangeAt(0);
31538 r.selectNodeContents((this.doc.body || this.doc.documentElement));
31543 this.execCmd('useCSS', true);
31544 this.execCmd('styleWithCSS', false);
31547 this.owner.fireEvent('activate', this);
31551 adjustFont: function(btn){
31552 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
31553 //if(Roo.isSafari){ // safari
31556 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
31557 if(Roo.isSafari){ // safari
31558 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
31559 v = (v < 10) ? 10 : v;
31560 v = (v > 48) ? 48 : v;
31561 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
31566 v = Math.max(1, v+adjust);
31568 this.execCmd('FontSize', v );
31571 onEditorEvent : function(e)
31575 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
31576 return; // we do not handle this.. (undo manager does..)
31578 // in theory this detects if the last element is not a br, then we try and do that.
31579 // its so clicking in space at bottom triggers adding a br and moving the cursor.
31581 e.target.nodeName == 'BODY' &&
31582 e.type == "mouseup" &&
31583 this.doc.body.lastChild
31585 var lc = this.doc.body.lastChild;
31586 // gtx-trans is google translate plugin adding crap.
31587 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
31588 lc = lc.previousSibling;
31590 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
31591 // if last element is <BR> - then dont do anything.
31593 var ns = this.doc.createElement('br');
31594 this.doc.body.appendChild(ns);
31595 range = this.doc.createRange();
31596 range.setStartAfter(ns);
31597 range.collapse(true);
31598 var sel = this.win.getSelection();
31599 sel.removeAllRanges();
31600 sel.addRange(range);
31606 this.fireEditorEvent(e);
31607 // this.updateToolbar();
31608 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
31611 fireEditorEvent: function(e)
31613 this.owner.fireEvent('editorevent', this, e);
31616 insertTag : function(tg)
31618 // could be a bit smarter... -> wrap the current selected tRoo..
31619 if (tg.toLowerCase() == 'span' ||
31620 tg.toLowerCase() == 'code' ||
31621 tg.toLowerCase() == 'sup' ||
31622 tg.toLowerCase() == 'sub'
31625 range = this.createRange(this.getSelection());
31626 var wrappingNode = this.doc.createElement(tg.toLowerCase());
31627 wrappingNode.appendChild(range.extractContents());
31628 range.insertNode(wrappingNode);
31635 this.execCmd("formatblock", tg);
31636 this.undoManager.addEvent();
31639 insertText : function(txt)
31643 var range = this.createRange();
31644 range.deleteContents();
31645 //alert(Sender.getAttribute('label'));
31647 range.insertNode(this.doc.createTextNode(txt));
31648 this.undoManager.addEvent();
31654 * Executes a Midas editor command on the editor document and performs necessary focus and
31655 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
31656 * @param {String} cmd The Midas command
31657 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31659 relayCmd : function(cmd, value)
31663 case 'justifyleft':
31664 case 'justifyright':
31665 case 'justifycenter':
31666 // if we are in a cell, then we will adjust the
31667 var n = this.getParentElement();
31668 var td = n.closest('td');
31670 var bl = Roo.htmleditor.Block.factory(td);
31671 bl.textAlign = cmd.replace('justify','');
31672 bl.updateElement();
31673 this.owner.fireEvent('editorevent', this);
31676 this.execCmd('styleWithCSS', true); //
31680 // if there is no selection, then we insert, and set the curson inside it..
31681 this.execCmd('styleWithCSS', false);
31691 this.execCmd(cmd, value);
31692 this.owner.fireEvent('editorevent', this);
31693 //this.updateToolbar();
31694 this.owner.deferFocus();
31698 * Executes a Midas editor command directly on the editor document.
31699 * For visual commands, you should use {@link #relayCmd} instead.
31700 * <b>This should only be called after the editor is initialized.</b>
31701 * @param {String} cmd The Midas command
31702 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
31704 execCmd : function(cmd, value){
31705 this.doc.execCommand(cmd, false, value === undefined ? null : value);
31712 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
31714 * @param {String} text | dom node..
31716 insertAtCursor : function(text)
31719 if(!this.activated){
31723 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
31727 // from jquery ui (MIT licenced)
31729 var win = this.win;
31731 if (win.getSelection && win.getSelection().getRangeAt) {
31733 // delete the existing?
31735 this.createRange(this.getSelection()).deleteContents();
31736 range = win.getSelection().getRangeAt(0);
31737 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
31738 range.insertNode(node);
31739 range = range.cloneRange();
31740 range.collapse(false);
31742 win.getSelection().removeAllRanges();
31743 win.getSelection().addRange(range);
31747 } else if (win.document.selection && win.document.selection.createRange) {
31748 // no firefox support
31749 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31750 win.document.selection.createRange().pasteHTML(txt);
31753 // no firefox support
31754 var txt = typeof(text) == 'string' ? text : text.outerHTML;
31755 this.execCmd('InsertHTML', txt);
31763 mozKeyPress : function(e){
31765 var c = e.getCharCode(), cmd;
31768 c = String.fromCharCode(c).toLowerCase();
31782 // this.cleanUpPaste.defer(100, this);
31788 this.relayCmd(cmd);
31789 //this.win.focus();
31790 //this.execCmd(cmd);
31791 //this.deferFocus();
31792 e.preventDefault();
31800 fixKeys : function(){ // load time branching for fastest keydown performance
31804 return function(e){
31805 var k = e.getKey(), r;
31808 r = this.doc.selection.createRange();
31811 r.pasteHTML('    ');
31816 /// this is handled by Roo.htmleditor.KeyEnter
31819 r = this.doc.selection.createRange();
31821 var target = r.parentElement();
31822 if(!target || target.tagName.toLowerCase() != 'li'){
31824 r.pasteHTML('<br/>');
31831 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31832 // this.cleanUpPaste.defer(100, this);
31838 }else if(Roo.isOpera){
31839 return function(e){
31840 var k = e.getKey();
31844 this.execCmd('InsertHTML','    ');
31848 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31849 // this.cleanUpPaste.defer(100, this);
31854 }else if(Roo.isSafari){
31855 return function(e){
31856 var k = e.getKey();
31860 this.execCmd('InsertText','\t');
31864 this.mozKeyPress(e);
31866 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
31867 // this.cleanUpPaste.defer(100, this);
31875 getAllAncestors: function()
31877 var p = this.getSelectedNode();
31880 a.push(p); // push blank onto stack..
31881 p = this.getParentElement();
31885 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
31889 a.push(this.doc.body);
31893 lastSelNode : false,
31896 getSelection : function()
31898 this.assignDocWin();
31899 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
31902 * Select a dom node
31903 * @param {DomElement} node the node to select
31905 selectNode : function(node, collapse)
31907 var nodeRange = node.ownerDocument.createRange();
31909 nodeRange.selectNode(node);
31911 nodeRange.selectNodeContents(node);
31913 if (collapse === true) {
31914 nodeRange.collapse(true);
31917 var s = this.win.getSelection();
31918 s.removeAllRanges();
31919 s.addRange(nodeRange);
31922 getSelectedNode: function()
31924 // this may only work on Gecko!!!
31926 // should we cache this!!!!
31930 var range = this.createRange(this.getSelection()).cloneRange();
31933 var parent = range.parentElement();
31935 var testRange = range.duplicate();
31936 testRange.moveToElementText(parent);
31937 if (testRange.inRange(range)) {
31940 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
31943 parent = parent.parentElement;
31948 // is ancestor a text element.
31949 var ac = range.commonAncestorContainer;
31950 if (ac.nodeType == 3) {
31951 ac = ac.parentNode;
31954 var ar = ac.childNodes;
31957 var other_nodes = [];
31958 var has_other_nodes = false;
31959 for (var i=0;i<ar.length;i++) {
31960 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
31963 // fullly contained node.
31965 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
31970 // probably selected..
31971 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
31972 other_nodes.push(ar[i]);
31976 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
31981 has_other_nodes = true;
31983 if (!nodes.length && other_nodes.length) {
31984 nodes= other_nodes;
31986 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
31994 createRange: function(sel)
31996 // this has strange effects when using with
31997 // top toolbar - not sure if it's a great idea.
31998 //this.editor.contentWindow.focus();
31999 if (typeof sel != "undefined") {
32001 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
32003 return this.doc.createRange();
32006 return this.doc.createRange();
32009 getParentElement: function()
32012 this.assignDocWin();
32013 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
32015 var range = this.createRange(sel);
32018 var p = range.commonAncestorContainer;
32019 while (p.nodeType == 3) { // text node
32030 * Range intersection.. the hard stuff...
32034 * [ -- selected range --- ]
32038 * if end is before start or hits it. fail.
32039 * if start is after end or hits it fail.
32041 * if either hits (but other is outside. - then it's not
32047 // @see http://www.thismuchiknow.co.uk/?p=64.
32048 rangeIntersectsNode : function(range, node)
32050 var nodeRange = node.ownerDocument.createRange();
32052 nodeRange.selectNode(node);
32054 nodeRange.selectNodeContents(node);
32057 var rangeStartRange = range.cloneRange();
32058 rangeStartRange.collapse(true);
32060 var rangeEndRange = range.cloneRange();
32061 rangeEndRange.collapse(false);
32063 var nodeStartRange = nodeRange.cloneRange();
32064 nodeStartRange.collapse(true);
32066 var nodeEndRange = nodeRange.cloneRange();
32067 nodeEndRange.collapse(false);
32069 return rangeStartRange.compareBoundaryPoints(
32070 Range.START_TO_START, nodeEndRange) == -1 &&
32071 rangeEndRange.compareBoundaryPoints(
32072 Range.START_TO_START, nodeStartRange) == 1;
32076 rangeCompareNode : function(range, node)
32078 var nodeRange = node.ownerDocument.createRange();
32080 nodeRange.selectNode(node);
32082 nodeRange.selectNodeContents(node);
32086 range.collapse(true);
32088 nodeRange.collapse(true);
32090 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
32091 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
32093 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
32095 var nodeIsBefore = ss == 1;
32096 var nodeIsAfter = ee == -1;
32098 if (nodeIsBefore && nodeIsAfter) {
32101 if (!nodeIsBefore && nodeIsAfter) {
32102 return 1; //right trailed.
32105 if (nodeIsBefore && !nodeIsAfter) {
32106 return 2; // left trailed.
32112 cleanWordChars : function(input) {// change the chars to hex code
32115 [ 8211, "–" ],
32116 [ 8212, "—" ],
32124 var output = input;
32125 Roo.each(swapCodes, function(sw) {
32126 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
32128 output = output.replace(swapper, sw[1]);
32138 cleanUpChild : function (node)
32141 new Roo.htmleditor.FilterComment({node : node});
32142 new Roo.htmleditor.FilterAttributes({
32144 attrib_black : this.ablack,
32145 attrib_clean : this.aclean,
32146 style_white : this.cwhite,
32147 style_black : this.cblack
32149 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
32150 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
32156 * Clean up MS wordisms...
32157 * @deprecated - use filter directly
32159 cleanWord : function(node)
32161 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
32162 new Roo.htmleditor.FilterKeepChildren({node : node ? node : this.doc.body, tag : [ 'FONT', ':' ]} );
32169 * @deprecated - use filters
32171 cleanTableWidths : function(node)
32173 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
32180 applyBlacklists : function()
32182 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
32183 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
32185 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
32186 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
32187 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
32191 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
32192 if (b.indexOf(tag) > -1) {
32195 this.white.push(tag);
32199 Roo.each(w, function(tag) {
32200 if (b.indexOf(tag) > -1) {
32203 if (this.white.indexOf(tag) > -1) {
32206 this.white.push(tag);
32211 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
32212 if (w.indexOf(tag) > -1) {
32215 this.black.push(tag);
32219 Roo.each(b, function(tag) {
32220 if (w.indexOf(tag) > -1) {
32223 if (this.black.indexOf(tag) > -1) {
32226 this.black.push(tag);
32231 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
32232 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
32236 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
32237 if (b.indexOf(tag) > -1) {
32240 this.cwhite.push(tag);
32244 Roo.each(w, function(tag) {
32245 if (b.indexOf(tag) > -1) {
32248 if (this.cwhite.indexOf(tag) > -1) {
32251 this.cwhite.push(tag);
32256 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
32257 if (w.indexOf(tag) > -1) {
32260 this.cblack.push(tag);
32264 Roo.each(b, function(tag) {
32265 if (w.indexOf(tag) > -1) {
32268 if (this.cblack.indexOf(tag) > -1) {
32271 this.cblack.push(tag);
32276 setStylesheets : function(stylesheets)
32278 if(typeof(stylesheets) == 'string'){
32279 Roo.get(this.iframe.contentDocument.head).createChild({
32281 rel : 'stylesheet',
32290 Roo.each(stylesheets, function(s) {
32295 Roo.get(_this.iframe.contentDocument.head).createChild({
32297 rel : 'stylesheet',
32307 updateLanguage : function()
32309 if (!this.iframe || !this.iframe.contentDocument) {
32312 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
32316 removeStylesheets : function()
32320 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
32325 setStyle : function(style)
32327 Roo.get(this.iframe.contentDocument.head).createChild({
32336 // hide stuff that is not compatible
32350 * @event specialkey
32354 * @cfg {String} fieldClass @hide
32357 * @cfg {String} focusClass @hide
32360 * @cfg {String} autoCreate @hide
32363 * @cfg {String} inputType @hide
32366 * @cfg {String} invalidClass @hide
32369 * @cfg {String} invalidText @hide
32372 * @cfg {String} msgFx @hide
32375 * @cfg {String} validateOnBlur @hide
32379 Roo.HtmlEditorCore.white = [
32380 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
32382 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
32383 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
32384 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
32385 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
32386 'TABLE', 'UL', 'XMP',
32388 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
32391 'DIR', 'MENU', 'OL', 'UL', 'DL',
32397 Roo.HtmlEditorCore.black = [
32398 // 'embed', 'object', // enable - backend responsiblity to clean thiese
32400 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
32401 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
32402 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
32403 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
32404 //'FONT' // CLEAN LATER..
32405 'COLGROUP', 'COL' // messy tables.
32409 Roo.HtmlEditorCore.clean = [ // ?? needed???
32410 'SCRIPT', 'STYLE', 'TITLE', 'XML'
32412 Roo.HtmlEditorCore.tag_remove = [
32417 Roo.HtmlEditorCore.ablack = [
32421 Roo.HtmlEditorCore.aclean = [
32422 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
32426 Roo.HtmlEditorCore.pwhite= [
32427 'http', 'https', 'mailto'
32430 // white listed style attributes.
32431 Roo.HtmlEditorCore.cwhite= [
32432 // 'text-align', /// default is to allow most things..
32438 // black listed style attributes.
32439 Roo.HtmlEditorCore.cblack= [
32440 // 'font-size' -- this can be set by the project
32454 * @class Roo.bootstrap.form.HtmlEditor
32455 * @extends Roo.bootstrap.form.TextArea
32456 * Bootstrap HtmlEditor class
32459 * Create a new HtmlEditor
32460 * @param {Object} config The config object
32463 Roo.bootstrap.form.HtmlEditor = function(config){
32464 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
32465 if (!this.toolbars) {
32466 this.toolbars = [];
32469 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
32472 * @event initialize
32473 * Fires when the editor is fully initialized (including the iframe)
32474 * @param {HtmlEditor} this
32479 * Fires when the editor is first receives the focus. Any insertion must wait
32480 * until after this event.
32481 * @param {HtmlEditor} this
32485 * @event beforesync
32486 * Fires before the textarea is updated with content from the editor iframe. Return false
32487 * to cancel the sync.
32488 * @param {HtmlEditor} this
32489 * @param {String} html
32493 * @event beforepush
32494 * Fires before the iframe editor is updated with content from the textarea. Return false
32495 * to cancel the push.
32496 * @param {HtmlEditor} this
32497 * @param {String} html
32502 * Fires when the textarea is updated with content from the editor iframe.
32503 * @param {HtmlEditor} this
32504 * @param {String} html
32509 * Fires when the iframe editor is updated with content from the textarea.
32510 * @param {HtmlEditor} this
32511 * @param {String} html
32515 * @event editmodechange
32516 * Fires when the editor switches edit modes
32517 * @param {HtmlEditor} this
32518 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
32520 editmodechange: true,
32522 * @event editorevent
32523 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
32524 * @param {HtmlEditor} this
32528 * @event firstfocus
32529 * Fires when on first focus - needed by toolbars..
32530 * @param {HtmlEditor} this
32535 * Auto save the htmlEditor value as a file into Events
32536 * @param {HtmlEditor} this
32540 * @event savedpreview
32541 * preview the saved version of htmlEditor
32542 * @param {HtmlEditor} this
32549 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
32553 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
32558 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
32563 * @cfg {String} resize (none|both|horizontal|vertical) - css resize of element
32567 * @cfg {Number} height (in pixels)
32571 * @cfg {Number} width (in pixels)
32576 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
32579 stylesheets: false,
32584 // private properties
32585 validationEvent : false,
32587 initialized : false,
32590 onFocus : Roo.emptyFn,
32592 hideMode:'offsets',
32594 tbContainer : false,
32598 toolbarContainer :function() {
32599 return this.wrap.select('.x-html-editor-tb',true).first();
32603 * Protected method that will not generally be called directly. It
32604 * is called when the editor creates its toolbar. Override this method if you need to
32605 * add custom toolbar buttons.
32606 * @param {HtmlEditor} editor
32608 createToolbar : function(){
32609 Roo.log('renewing');
32610 Roo.log("create toolbars");
32612 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
32613 this.toolbars[0].render(this.toolbarContainer());
32617 // if (!editor.toolbars || !editor.toolbars.length) {
32618 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
32621 // for (var i =0 ; i < editor.toolbars.length;i++) {
32622 // editor.toolbars[i] = Roo.factory(
32623 // typeof(editor.toolbars[i]) == 'string' ?
32624 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
32625 // Roo.bootstrap.form.HtmlEditor);
32626 // editor.toolbars[i].init(editor);
32632 onRender : function(ct, position)
32634 // Roo.log("Call onRender: " + this.xtype);
32636 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
32638 this.wrap = this.inputEl().wrap({
32639 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
32642 this.editorcore.onRender(ct, position);
32645 this.createToolbar(this);
32653 onResize : function(w, h)
32655 Roo.log('resize: ' +w + ',' + h );
32656 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
32660 if(this.inputEl() ){
32661 if(typeof w == 'number'){
32662 var aw = w - this.wrap.getFrameWidth('lr');
32663 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
32666 if(typeof h == 'number'){
32667 var tbh = -11; // fixme it needs to tool bar size!
32668 for (var i =0; i < this.toolbars.length;i++) {
32669 // fixme - ask toolbars for heights?
32670 tbh += this.toolbars[i].el.getHeight();
32671 //if (this.toolbars[i].footer) {
32672 // tbh += this.toolbars[i].footer.el.getHeight();
32680 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
32681 ah -= 5; // knock a few pixes off for look..
32682 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
32686 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
32687 this.editorcore.onResize(ew,eh);
32692 * Toggles the editor between standard and source edit mode.
32693 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
32695 toggleSourceEdit : function(sourceEditMode)
32697 this.editorcore.toggleSourceEdit(sourceEditMode);
32699 if(this.editorcore.sourceEditMode){
32700 Roo.log('editor - showing textarea');
32703 // Roo.log(this.syncValue());
32705 this.inputEl().removeClass(['hide', 'x-hidden']);
32706 this.inputEl().dom.removeAttribute('tabIndex');
32707 this.inputEl().focus();
32709 Roo.log('editor - hiding textarea');
32711 // Roo.log(this.pushValue());
32714 this.inputEl().addClass(['hide', 'x-hidden']);
32715 this.inputEl().dom.setAttribute('tabIndex', -1);
32716 //this.deferFocus();
32719 //if(this.resizable){
32720 // this.setSize(this.wrap.getSize());
32723 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
32726 // private (for BoxComponent)
32727 adjustSize : Roo.BoxComponent.prototype.adjustSize,
32729 // private (for BoxComponent)
32730 getResizeEl : function(){
32734 // private (for BoxComponent)
32735 getPositionEl : function(){
32740 initEvents : function(){
32741 this.originalValue = this.getValue();
32745 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32748 // markInvalid : Roo.emptyFn,
32750 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
32753 // clearInvalid : Roo.emptyFn,
32755 setValue : function(v){
32756 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
32757 this.editorcore.pushValue();
32762 deferFocus : function(){
32763 this.focus.defer(10, this);
32767 focus : function(){
32768 this.editorcore.focus();
32774 onDestroy : function(){
32780 for (var i =0; i < this.toolbars.length;i++) {
32781 // fixme - ask toolbars for heights?
32782 this.toolbars[i].onDestroy();
32785 this.wrap.dom.innerHTML = '';
32786 this.wrap.remove();
32791 onFirstFocus : function(){
32792 //Roo.log("onFirstFocus");
32793 this.editorcore.onFirstFocus();
32794 for (var i =0; i < this.toolbars.length;i++) {
32795 this.toolbars[i].onFirstFocus();
32801 syncValue : function()
32803 this.editorcore.syncValue();
32806 pushValue : function()
32808 this.editorcore.pushValue();
32812 // hide stuff that is not compatible
32826 * @event specialkey
32830 * @cfg {String} fieldClass @hide
32833 * @cfg {String} focusClass @hide
32836 * @cfg {String} autoCreate @hide
32839 * @cfg {String} inputType @hide
32843 * @cfg {String} invalidText @hide
32846 * @cfg {String} msgFx @hide
32849 * @cfg {String} validateOnBlur @hide
32858 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
32860 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
32861 * @parent Roo.bootstrap.form.HtmlEditor
32862 * @extends Roo.bootstrap.nav.Simplebar
32868 new Roo.bootstrap.form.HtmlEditor({
32871 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
32872 disable : { fonts: 1 , format: 1, ..., ... , ...],
32878 * @cfg {Object} disable List of elements to disable..
32879 * @cfg {Array} btns List of additional buttons.
32883 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
32886 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
32889 Roo.apply(this, config);
32891 // default disabled, based on 'good practice'..
32892 this.disable = this.disable || {};
32893 Roo.applyIf(this.disable, {
32896 specialElements : true
32898 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
32900 this.editor = config.editor;
32901 this.editorcore = config.editor.editorcore;
32903 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
32905 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
32906 // dont call parent... till later.
32908 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
32913 editorcore : false,
32918 "h1","h2","h3","h4","h5","h6",
32920 "abbr", "acronym", "address", "cite", "samp", "var",
32924 onRender : function(ct, position)
32926 // Roo.log("Call onRender: " + this.xtype);
32928 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
32930 this.el.dom.style.marginBottom = '0';
32932 var editorcore = this.editorcore;
32933 var editor= this.editor;
32936 var btn = function(id,cmd , toggle, handler, html){
32938 var event = toggle ? 'toggle' : 'click';
32943 xns: Roo.bootstrap,
32947 enableToggle:toggle !== false,
32949 pressed : toggle ? false : null,
32952 a.listeners[toggle ? 'toggle' : 'click'] = function() {
32953 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
32959 // var cb_box = function...
32964 xns: Roo.bootstrap,
32969 xns: Roo.bootstrap,
32973 Roo.each(this.formats, function(f) {
32974 style.menu.items.push({
32976 xns: Roo.bootstrap,
32977 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
32982 editorcore.insertTag(this.tagname);
32989 children.push(style);
32991 btn('bold',false,true);
32992 btn('italic',false,true);
32993 btn('align-left', 'justifyleft',true);
32994 btn('align-center', 'justifycenter',true);
32995 btn('align-right' , 'justifyright',true);
32996 btn('link', false, false, function(btn) {
32997 //Roo.log("create link?");
32998 var url = prompt(this.createLinkText, this.defaultLinkValue);
32999 if(url && url != 'http:/'+'/'){
33000 this.editorcore.relayCmd('createlink', url);
33003 btn('list','insertunorderedlist',true);
33004 btn('list-ol','insertorderedlist',true);
33006 btn('pencil', false,true, function(btn){
33008 this.toggleSourceEdit(btn.pressed);
33011 if (this.editor.btns.length > 0) {
33012 for (var i = 0; i<this.editor.btns.length; i++) {
33013 children.push(this.editor.btns[i]);
33021 xns: Roo.bootstrap,
33026 xns: Roo.bootstrap,
33031 cog.menu.items.push({
33033 xns: Roo.bootstrap,
33034 html : Clean styles,
33039 editorcore.insertTag(this.tagname);
33048 this.xtype = 'NavSimplebar';
33050 for(var i=0;i< children.length;i++) {
33052 this.buttons.add(this.addxtypeChild(children[i]));
33056 editor.on('editorevent', this.updateToolbar, this);
33058 onBtnClick : function(id)
33060 this.editorcore.relayCmd(id);
33061 this.editorcore.focus();
33065 * Protected method that will not generally be called directly. It triggers
33066 * a toolbar update by reading the markup state of the current selection in the editor.
33068 updateToolbar: function(){
33070 if(!this.editorcore.activated){
33071 this.editor.onFirstFocus(); // is this neeed?
33075 var btns = this.buttons;
33076 var doc = this.editorcore.doc;
33077 btns.get('bold').setActive(doc.queryCommandState('bold'));
33078 btns.get('italic').setActive(doc.queryCommandState('italic'));
33079 //btns.get('underline').setActive(doc.queryCommandState('underline'));
33081 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
33082 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
33083 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
33085 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
33086 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
33089 var ans = this.editorcore.getAllAncestors();
33090 if (this.formatCombo) {
33093 var store = this.formatCombo.store;
33094 this.formatCombo.setValue("");
33095 for (var i =0; i < ans.length;i++) {
33096 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
33098 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
33106 // hides menus... - so this cant be on a menu...
33107 Roo.bootstrap.MenuMgr.hideAll();
33109 Roo.bootstrap.menu.Manager.hideAll();
33110 //this.editorsyncValue();
33112 onFirstFocus: function() {
33113 this.buttons.each(function(item){
33117 toggleSourceEdit : function(sourceEditMode){
33120 if(sourceEditMode){
33121 Roo.log("disabling buttons");
33122 this.buttons.each( function(item){
33123 if(item.cmd != 'pencil'){
33129 Roo.log("enabling buttons");
33130 if(this.editorcore.initialized){
33131 this.buttons.each( function(item){
33137 Roo.log("calling toggole on editor");
33138 // tell the editor that it's been pressed..
33139 this.editor.toggleSourceEdit(sourceEditMode);
33153 * @class Roo.bootstrap.form.Markdown
33154 * @extends Roo.bootstrap.form.TextArea
33155 * Bootstrap Showdown editable area
33156 * @cfg {string} content
33159 * Create a new Showdown
33162 Roo.bootstrap.form.Markdown = function(config){
33163 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
33167 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
33171 initEvents : function()
33174 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
33175 this.markdownEl = this.el.createChild({
33176 cls : 'roo-markdown-area'
33178 this.inputEl().addClass('d-none');
33179 if (this.getValue() == '') {
33180 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33183 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33185 this.markdownEl.on('click', this.toggleTextEdit, this);
33186 this.on('blur', this.toggleTextEdit, this);
33187 this.on('specialkey', this.resizeTextArea, this);
33190 toggleTextEdit : function()
33192 var sh = this.markdownEl.getHeight();
33193 this.inputEl().addClass('d-none');
33194 this.markdownEl.addClass('d-none');
33195 if (!this.editing) {
33197 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
33198 this.inputEl().removeClass('d-none');
33199 this.inputEl().focus();
33200 this.editing = true;
33203 // show showdown...
33204 this.updateMarkdown();
33205 this.markdownEl.removeClass('d-none');
33206 this.editing = false;
33209 updateMarkdown : function()
33211 if (this.getValue() == '') {
33212 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
33216 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
33219 resizeTextArea: function () {
33222 Roo.log([sh, this.getValue().split("\n").length * 30]);
33223 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
33225 setValue : function(val)
33227 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
33228 if (!this.editing) {
33229 this.updateMarkdown();
33235 if (!this.editing) {
33236 this.toggleTextEdit();
33244 * Ext JS Library 1.1.1
33245 * Copyright(c) 2006-2007, Ext JS, LLC.
33247 * Originally Released Under LGPL - original licence link has changed is not relivant.
33250 * <script type="text/javascript">
33254 * @class Roo.bootstrap.PagingToolbar
33255 * @extends Roo.bootstrap.nav.Simplebar
33256 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
33258 * Create a new PagingToolbar
33259 * @param {Object} config The config object
33260 * @param {Roo.data.Store} store
33262 Roo.bootstrap.PagingToolbar = function(config)
33264 // old args format still supported... - xtype is prefered..
33265 // created from xtype...
33267 this.ds = config.dataSource;
33269 if (config.store && !this.ds) {
33270 this.store= Roo.factory(config.store, Roo.data);
33271 this.ds = this.store;
33272 this.ds.xmodule = this.xmodule || false;
33275 this.toolbarItems = [];
33276 if (config.items) {
33277 this.toolbarItems = config.items;
33280 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
33285 this.bind(this.ds);
33288 if (Roo.bootstrap.version == 4) {
33289 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
33291 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
33296 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
33298 * @cfg {Roo.bootstrap.Button} buttons[]
33299 * Buttons for the toolbar
33302 * @cfg {Roo.data.Store} store
33303 * The underlying data store providing the paged data
33306 * @cfg {String/HTMLElement/Element} container
33307 * container The id or element that will contain the toolbar
33310 * @cfg {Boolean} displayInfo
33311 * True to display the displayMsg (defaults to false)
33314 * @cfg {Number} pageSize
33315 * The number of records to display per page (defaults to 20)
33319 * @cfg {String} displayMsg
33320 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
33322 displayMsg : 'Displaying {0} - {1} of {2}',
33324 * @cfg {String} emptyMsg
33325 * The message to display when no records are found (defaults to "No data to display")
33327 emptyMsg : 'No data to display',
33329 * Customizable piece of the default paging text (defaults to "Page")
33332 beforePageText : "Page",
33334 * Customizable piece of the default paging text (defaults to "of %0")
33337 afterPageText : "of {0}",
33339 * Customizable piece of the default paging text (defaults to "First Page")
33342 firstText : "First Page",
33344 * Customizable piece of the default paging text (defaults to "Previous Page")
33347 prevText : "Previous Page",
33349 * Customizable piece of the default paging text (defaults to "Next Page")
33352 nextText : "Next Page",
33354 * Customizable piece of the default paging text (defaults to "Last Page")
33357 lastText : "Last Page",
33359 * Customizable piece of the default paging text (defaults to "Refresh")
33362 refreshText : "Refresh",
33366 onRender : function(ct, position)
33368 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
33369 this.navgroup.parentId = this.id;
33370 this.navgroup.onRender(this.el, null);
33371 // add the buttons to the navgroup
33373 if(this.displayInfo){
33374 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
33375 this.displayEl = this.el.select('.x-paging-info', true).first();
33376 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
33377 // this.displayEl = navel.el.select('span',true).first();
33383 Roo.each(_this.buttons, function(e){ // this might need to use render????
33384 Roo.factory(e).render(_this.el);
33388 Roo.each(_this.toolbarItems, function(e) {
33389 _this.navgroup.addItem(e);
33393 this.first = this.navgroup.addItem({
33394 tooltip: this.firstText,
33395 cls: "prev btn-outline-secondary",
33396 html : ' <i class="fa fa-step-backward"></i>',
33398 preventDefault: true,
33399 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
33402 this.prev = this.navgroup.addItem({
33403 tooltip: this.prevText,
33404 cls: "prev btn-outline-secondary",
33405 html : ' <i class="fa fa-backward"></i>',
33407 preventDefault: true,
33408 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
33410 //this.addSeparator();
33413 var field = this.navgroup.addItem( {
33415 cls : 'x-paging-position btn-outline-secondary',
33417 html : this.beforePageText +
33418 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
33419 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
33422 this.field = field.el.select('input', true).first();
33423 this.field.on("keydown", this.onPagingKeydown, this);
33424 this.field.on("focus", function(){this.dom.select();});
33427 this.afterTextEl = field.el.select('.x-paging-after',true).first();
33428 //this.field.setHeight(18);
33429 //this.addSeparator();
33430 this.next = this.navgroup.addItem({
33431 tooltip: this.nextText,
33432 cls: "next btn-outline-secondary",
33433 html : ' <i class="fa fa-forward"></i>',
33435 preventDefault: true,
33436 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
33438 this.last = this.navgroup.addItem({
33439 tooltip: this.lastText,
33440 html : ' <i class="fa fa-step-forward"></i>',
33441 cls: "next btn-outline-secondary",
33443 preventDefault: true,
33444 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
33446 //this.addSeparator();
33447 this.loading = this.navgroup.addItem({
33448 tooltip: this.refreshText,
33449 cls: "btn-outline-secondary",
33450 html : ' <i class="fa fa-refresh"></i>',
33451 preventDefault: true,
33452 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
33458 updateInfo : function(){
33459 if(this.displayEl){
33460 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
33461 var msg = count == 0 ?
33465 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
33467 this.displayEl.update(msg);
33472 onLoad : function(ds, r, o)
33474 this.cursor = o.params && o.params.start ? o.params.start : 0;
33476 var d = this.getPageData(),
33481 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
33482 this.field.dom.value = ap;
33483 this.first.setDisabled(ap == 1);
33484 this.prev.setDisabled(ap == 1);
33485 this.next.setDisabled(ap == ps);
33486 this.last.setDisabled(ap == ps);
33487 this.loading.enable();
33492 getPageData : function(){
33493 var total = this.ds.getTotalCount();
33496 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
33497 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
33502 onLoadError : function(proxy, o){
33503 this.loading.enable();
33504 if (this.ds.events.loadexception.listeners.length < 2) {
33505 // nothing has been assigned to loadexception except this...
33507 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
33513 onPagingKeydown : function(e){
33514 var k = e.getKey();
33515 var d = this.getPageData();
33517 var v = this.field.dom.value, pageNum;
33518 if(!v || isNaN(pageNum = parseInt(v, 10))){
33519 this.field.dom.value = d.activePage;
33522 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
33523 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33526 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))
33528 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
33529 this.field.dom.value = pageNum;
33530 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
33533 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
33535 var v = this.field.dom.value, pageNum;
33536 var increment = (e.shiftKey) ? 10 : 1;
33537 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
33540 if(!v || isNaN(pageNum = parseInt(v, 10))) {
33541 this.field.dom.value = d.activePage;
33544 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
33546 this.field.dom.value = parseInt(v, 10) + increment;
33547 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
33548 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
33555 beforeLoad : function(){
33557 this.loading.disable();
33562 onClick : function(which){
33571 ds.load({params:{start: 0, limit: this.pageSize}});
33574 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
33577 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
33580 var total = ds.getTotalCount();
33581 var extra = total % this.pageSize;
33582 var lastStart = extra ? (total - extra) : total-this.pageSize;
33583 ds.load({params:{start: lastStart, limit: this.pageSize}});
33586 ds.load({params:{start: this.cursor, limit: this.pageSize}});
33592 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
33593 * @param {Roo.data.Store} store The data store to unbind
33595 unbind : function(ds){
33596 ds.un("beforeload", this.beforeLoad, this);
33597 ds.un("load", this.onLoad, this);
33598 ds.un("loadexception", this.onLoadError, this);
33599 ds.un("remove", this.updateInfo, this);
33600 ds.un("add", this.updateInfo, this);
33601 this.ds = undefined;
33605 * Binds the paging toolbar to the specified {@link Roo.data.Store}
33606 * @param {Roo.data.Store} store The data store to bind
33608 bind : function(ds){
33609 ds.on("beforeload", this.beforeLoad, this);
33610 ds.on("load", this.onLoad, this);
33611 ds.on("loadexception", this.onLoadError, this);
33612 ds.on("remove", this.updateInfo, this);
33613 ds.on("add", this.updateInfo, this);
33624 * @class Roo.bootstrap.MessageBar
33625 * @extends Roo.bootstrap.Component
33626 * Bootstrap MessageBar class
33627 * @cfg {String} html contents of the MessageBar
33628 * @cfg {String} weight (info | success | warning | danger) default info
33629 * @cfg {String} beforeClass insert the bar before the given class
33630 * @cfg {Boolean} closable (true | false) default false
33631 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
33634 * Create a new Element
33635 * @param {Object} config The config object
33638 Roo.bootstrap.MessageBar = function(config){
33639 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
33642 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
33648 beforeClass: 'bootstrap-sticky-wrap',
33650 getAutoCreate : function(){
33654 cls: 'alert alert-dismissable alert-' + this.weight,
33659 html: this.html || ''
33665 cfg.cls += ' alert-messages-fixed';
33679 onRender : function(ct, position)
33681 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
33684 var cfg = Roo.apply({}, this.getAutoCreate());
33688 cfg.cls += ' ' + this.cls;
33691 cfg.style = this.style;
33693 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
33695 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33698 this.el.select('>button.close').on('click', this.hide, this);
33704 if (!this.rendered) {
33710 this.fireEvent('show', this);
33716 if (!this.rendered) {
33722 this.fireEvent('hide', this);
33725 update : function()
33727 // var e = this.el.dom.firstChild;
33729 // if(this.closable){
33730 // e = e.nextSibling;
33733 // e.data = this.html || '';
33735 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
33751 * @class Roo.bootstrap.Graph
33752 * @extends Roo.bootstrap.Component
33753 * Bootstrap Graph class
33757 @cfg {String} graphtype bar | vbar | pie
33758 @cfg {number} g_x coodinator | centre x (pie)
33759 @cfg {number} g_y coodinator | centre y (pie)
33760 @cfg {number} g_r radius (pie)
33761 @cfg {number} g_height height of the chart (respected by all elements in the set)
33762 @cfg {number} g_width width of the chart (respected by all elements in the set)
33763 @cfg {Object} title The title of the chart
33766 -opts (object) options for the chart
33768 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
33769 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
33771 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.
33772 o stacked (boolean) whether or not to tread values as in a stacked bar chart
33774 o stretch (boolean)
33776 -opts (object) options for the pie
33779 o startAngle (number)
33780 o endAngle (number)
33784 * Create a new Input
33785 * @param {Object} config The config object
33788 Roo.bootstrap.Graph = function(config){
33789 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
33795 * The img click event for the img.
33796 * @param {Roo.EventObject} e
33802 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
33813 //g_colors: this.colors,
33820 getAutoCreate : function(){
33831 onRender : function(ct,position){
33834 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
33836 if (typeof(Raphael) == 'undefined') {
33837 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
33841 this.raphael = Raphael(this.el.dom);
33843 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33844 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33845 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
33846 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
33848 r.text(160, 10, "Single Series Chart").attr(txtattr);
33849 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
33850 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
33851 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
33853 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
33854 r.barchart(330, 10, 300, 220, data1);
33855 r.barchart(10, 250, 300, 220, data2, {stacked: true});
33856 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
33859 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33860 // r.barchart(30, 30, 560, 250, xdata, {
33861 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
33862 // axis : "0 0 1 1",
33863 // axisxlabels : xdata
33864 // //yvalues : cols,
33867 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
33869 // this.load(null,xdata,{
33870 // axis : "0 0 1 1",
33871 // axisxlabels : xdata
33876 load : function(graphtype,xdata,opts)
33878 this.raphael.clear();
33880 graphtype = this.graphtype;
33885 var r = this.raphael,
33886 fin = function () {
33887 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
33889 fout = function () {
33890 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
33892 pfin = function() {
33893 this.sector.stop();
33894 this.sector.scale(1.1, 1.1, this.cx, this.cy);
33897 this.label[0].stop();
33898 this.label[0].attr({ r: 7.5 });
33899 this.label[1].attr({ "font-weight": 800 });
33902 pfout = function() {
33903 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
33906 this.label[0].animate({ r: 5 }, 500, "bounce");
33907 this.label[1].attr({ "font-weight": 400 });
33913 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33916 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
33919 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
33920 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
33922 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
33929 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
33934 setTitle: function(o)
33939 initEvents: function() {
33942 this.el.on('click', this.onClick, this);
33946 onClick : function(e)
33948 Roo.log('img onclick');
33949 this.fireEvent('click', this, e);
33955 Roo.bootstrap.dash = {};/*
33961 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
33964 * @class Roo.bootstrap.dash.NumberBox
33965 * @extends Roo.bootstrap.Component
33966 * Bootstrap NumberBox class
33967 * @cfg {String} headline Box headline
33968 * @cfg {String} content Box content
33969 * @cfg {String} icon Box icon
33970 * @cfg {String} footer Footer text
33971 * @cfg {String} fhref Footer href
33974 * Create a new NumberBox
33975 * @param {Object} config The config object
33979 Roo.bootstrap.dash.NumberBox = function(config){
33980 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
33984 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
33993 getAutoCreate : function(){
33997 cls : 'small-box ',
34005 cls : 'roo-headline',
34006 html : this.headline
34010 cls : 'roo-content',
34011 html : this.content
34025 cls : 'ion ' + this.icon
34034 cls : 'small-box-footer',
34035 href : this.fhref || '#',
34039 cfg.cn.push(footer);
34046 onRender : function(ct,position){
34047 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
34054 setHeadline: function (value)
34056 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
34059 setFooter: function (value, href)
34061 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
34064 this.el.select('a.small-box-footer',true).first().attr('href', href);
34069 setContent: function (value)
34071 this.el.select('.roo-content',true).first().dom.innerHTML = value;
34074 initEvents: function()
34088 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34091 * @class Roo.bootstrap.dash.TabBox
34092 * @extends Roo.bootstrap.Component
34093 * @children Roo.bootstrap.dash.TabPane
34094 * Bootstrap TabBox class
34095 * @cfg {String} title Title of the TabBox
34096 * @cfg {String} icon Icon of the TabBox
34097 * @cfg {Boolean} showtabs (true|false) show the tabs default true
34098 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
34101 * Create a new TabBox
34102 * @param {Object} config The config object
34106 Roo.bootstrap.dash.TabBox = function(config){
34107 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
34112 * When a pane is added
34113 * @param {Roo.bootstrap.dash.TabPane} pane
34117 * @event activatepane
34118 * When a pane is activated
34119 * @param {Roo.bootstrap.dash.TabPane} pane
34121 "activatepane" : true
34129 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
34134 tabScrollable : false,
34136 getChildContainer : function()
34138 return this.el.select('.tab-content', true).first();
34141 getAutoCreate : function(){
34145 cls: 'pull-left header',
34153 cls: 'fa ' + this.icon
34159 cls: 'nav nav-tabs pull-right',
34165 if(this.tabScrollable){
34172 cls: 'nav nav-tabs pull-right',
34183 cls: 'nav-tabs-custom',
34188 cls: 'tab-content no-padding',
34196 initEvents : function()
34198 //Roo.log('add add pane handler');
34199 this.on('addpane', this.onAddPane, this);
34202 * Updates the box title
34203 * @param {String} html to set the title to.
34205 setTitle : function(value)
34207 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
34209 onAddPane : function(pane)
34211 this.panes.push(pane);
34212 //Roo.log('addpane');
34214 // tabs are rendere left to right..
34215 if(!this.showtabs){
34219 var ctr = this.el.select('.nav-tabs', true).first();
34222 var existing = ctr.select('.nav-tab',true);
34223 var qty = existing.getCount();;
34226 var tab = ctr.createChild({
34228 cls : 'nav-tab' + (qty ? '' : ' active'),
34236 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
34239 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
34241 pane.el.addClass('active');
34246 onTabClick : function(ev,un,ob,pane)
34248 //Roo.log('tab - prev default');
34249 ev.preventDefault();
34252 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
34253 pane.tab.addClass('active');
34254 //Roo.log(pane.title);
34255 this.getChildContainer().select('.tab-pane',true).removeClass('active');
34256 // technically we should have a deactivate event.. but maybe add later.
34257 // and it should not de-activate the selected tab...
34258 this.fireEvent('activatepane', pane);
34259 pane.el.addClass('active');
34260 pane.fireEvent('activate');
34265 getActivePane : function()
34268 Roo.each(this.panes, function(p) {
34269 if(p.el.hasClass('active')){
34290 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
34292 * @class Roo.bootstrap.TabPane
34293 * @extends Roo.bootstrap.Component
34294 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
34295 * Bootstrap TabPane class
34296 * @cfg {Boolean} active (false | true) Default false
34297 * @cfg {String} title title of panel
34301 * Create a new TabPane
34302 * @param {Object} config The config object
34305 Roo.bootstrap.dash.TabPane = function(config){
34306 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
34312 * When a pane is activated
34313 * @param {Roo.bootstrap.dash.TabPane} pane
34320 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
34325 // the tabBox that this is attached to.
34328 getAutoCreate : function()
34336 cfg.cls += ' active';
34341 initEvents : function()
34343 //Roo.log('trigger add pane handler');
34344 this.parent().fireEvent('addpane', this)
34348 * Updates the tab title
34349 * @param {String} html to set the title to.
34351 setTitle: function(str)
34357 this.tab.select('a', true).first().dom.innerHTML = str;
34376 * @class Roo.bootstrap.Tooltip
34377 * Bootstrap Tooltip class
34378 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
34379 * to determine which dom element triggers the tooltip.
34381 * It needs to add support for additional attributes like tooltip-position
34384 * Create a new Toolti
34385 * @param {Object} config The config object
34388 Roo.bootstrap.Tooltip = function(config){
34389 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
34391 this.alignment = Roo.bootstrap.Tooltip.alignment;
34393 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
34394 this.alignment = config.alignment;
34399 Roo.apply(Roo.bootstrap.Tooltip, {
34401 * @function init initialize tooltip monitoring.
34405 currentTip : false,
34406 currentRegion : false,
34412 Roo.get(document).on('mouseover', this.enter ,this);
34413 Roo.get(document).on('mouseout', this.leave, this);
34416 this.currentTip = new Roo.bootstrap.Tooltip();
34419 enter : function(ev)
34421 var dom = ev.getTarget();
34423 //Roo.log(['enter',dom]);
34424 var el = Roo.fly(dom);
34425 if (this.currentEl) {
34427 //Roo.log(this.currentEl);
34428 //Roo.log(this.currentEl.contains(dom));
34429 if (this.currentEl == el) {
34432 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
34438 if (this.currentTip.el) {
34439 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
34443 if(!el || el.dom == document){
34449 if (!el.attr('tooltip')) {
34450 pel = el.findParent("[tooltip]");
34452 bindEl = Roo.get(pel);
34458 // you can not look for children, as if el is the body.. then everythign is the child..
34459 if (!pel && !el.attr('tooltip')) { //
34460 if (!el.select("[tooltip]").elements.length) {
34463 // is the mouse over this child...?
34464 bindEl = el.select("[tooltip]").first();
34465 var xy = ev.getXY();
34466 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
34467 //Roo.log("not in region.");
34470 //Roo.log("child element over..");
34473 this.currentEl = el;
34474 this.currentTip.bind(bindEl);
34475 this.currentRegion = Roo.lib.Region.getRegion(dom);
34476 this.currentTip.enter();
34479 leave : function(ev)
34481 var dom = ev.getTarget();
34482 //Roo.log(['leave',dom]);
34483 if (!this.currentEl) {
34488 if (dom != this.currentEl.dom) {
34491 var xy = ev.getXY();
34492 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
34495 // only activate leave if mouse cursor is outside... bounding box..
34500 if (this.currentTip) {
34501 this.currentTip.leave();
34503 //Roo.log('clear currentEl');
34504 this.currentEl = false;
34509 'left' : ['r-l', [-2,0], 'right'],
34510 'right' : ['l-r', [2,0], 'left'],
34511 'bottom' : ['t-b', [0,2], 'top'],
34512 'top' : [ 'b-t', [0,-2], 'bottom']
34518 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
34523 delay : null, // can be { show : 300 , hide: 500}
34527 hoverState : null, //???
34529 placement : 'bottom',
34533 getAutoCreate : function(){
34540 cls : 'tooltip-arrow arrow'
34543 cls : 'tooltip-inner'
34550 bind : function(el)
34555 initEvents : function()
34557 this.arrowEl = this.el.select('.arrow', true).first();
34558 this.innerEl = this.el.select('.tooltip-inner', true).first();
34561 enter : function () {
34563 if (this.timeout != null) {
34564 clearTimeout(this.timeout);
34567 this.hoverState = 'in';
34568 //Roo.log("enter - show");
34569 if (!this.delay || !this.delay.show) {
34574 this.timeout = setTimeout(function () {
34575 if (_t.hoverState == 'in') {
34578 }, this.delay.show);
34582 clearTimeout(this.timeout);
34584 this.hoverState = 'out';
34585 if (!this.delay || !this.delay.hide) {
34591 this.timeout = setTimeout(function () {
34592 //Roo.log("leave - timeout");
34594 if (_t.hoverState == 'out') {
34596 Roo.bootstrap.Tooltip.currentEl = false;
34601 show : function (msg)
34604 this.render(document.body);
34607 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
34609 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
34611 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
34613 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
34614 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
34616 if(this.bindEl.attr('tooltip-class')) {
34617 this.el.addClass(this.bindEl.attr('tooltip-class'));
34620 var placement = typeof this.placement == 'function' ?
34621 this.placement.call(this, this.el, on_el) :
34624 if(this.bindEl.attr('tooltip-placement')) {
34625 placement = this.bindEl.attr('tooltip-placement');
34628 var autoToken = /\s?auto?\s?/i;
34629 var autoPlace = autoToken.test(placement);
34631 placement = placement.replace(autoToken, '') || 'top';
34635 //this.el.setXY([0,0]);
34637 //this.el.dom.style.display='block';
34639 //this.el.appendTo(on_el);
34641 var p = this.getPosition();
34642 var box = this.el.getBox();
34648 var align = this.alignment[placement];
34650 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
34652 if(placement == 'top' || placement == 'bottom'){
34654 placement = 'right';
34657 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
34658 placement = 'left';
34661 var scroll = Roo.select('body', true).first().getScroll();
34663 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
34667 align = this.alignment[placement];
34669 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
34673 var elems = document.getElementsByTagName('div');
34674 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
34675 for (var i = 0; i < elems.length; i++) {
34676 var zindex = Number.parseInt(
34677 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
34680 if (zindex > highest) {
34687 this.el.dom.style.zIndex = highest;
34689 this.el.alignTo(this.bindEl, align[0],align[1]);
34690 //var arrow = this.el.select('.arrow',true).first();
34691 //arrow.set(align[2],
34693 this.el.addClass(placement);
34694 this.el.addClass("bs-tooltip-"+ placement);
34696 this.el.addClass('in fade show');
34698 this.hoverState = null;
34700 if (this.el.hasClass('fade')) {
34715 //this.el.setXY([0,0]);
34716 if(this.bindEl.attr('tooltip-class')) {
34717 this.el.removeClass(this.bindEl.attr('tooltip-class'));
34719 this.el.removeClass(['show', 'in']);
34735 * @class Roo.bootstrap.LocationPicker
34736 * @extends Roo.bootstrap.Component
34737 * Bootstrap LocationPicker class
34738 * @cfg {Number} latitude Position when init default 0
34739 * @cfg {Number} longitude Position when init default 0
34740 * @cfg {Number} zoom default 15
34741 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
34742 * @cfg {Boolean} mapTypeControl default false
34743 * @cfg {Boolean} disableDoubleClickZoom default false
34744 * @cfg {Boolean} scrollwheel default true
34745 * @cfg {Boolean} streetViewControl default false
34746 * @cfg {Number} radius default 0
34747 * @cfg {String} locationName
34748 * @cfg {Boolean} draggable default true
34749 * @cfg {Boolean} enableAutocomplete default false
34750 * @cfg {Boolean} enableReverseGeocode default true
34751 * @cfg {String} markerTitle
34754 * Create a new LocationPicker
34755 * @param {Object} config The config object
34759 Roo.bootstrap.LocationPicker = function(config){
34761 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
34766 * Fires when the picker initialized.
34767 * @param {Roo.bootstrap.LocationPicker} this
34768 * @param {Google Location} location
34772 * @event positionchanged
34773 * Fires when the picker position changed.
34774 * @param {Roo.bootstrap.LocationPicker} this
34775 * @param {Google Location} location
34777 positionchanged : true,
34780 * Fires when the map resize.
34781 * @param {Roo.bootstrap.LocationPicker} this
34786 * Fires when the map show.
34787 * @param {Roo.bootstrap.LocationPicker} this
34792 * Fires when the map hide.
34793 * @param {Roo.bootstrap.LocationPicker} this
34798 * Fires when click the map.
34799 * @param {Roo.bootstrap.LocationPicker} this
34800 * @param {Map event} e
34804 * @event mapRightClick
34805 * Fires when right click the map.
34806 * @param {Roo.bootstrap.LocationPicker} this
34807 * @param {Map event} e
34809 mapRightClick : true,
34811 * @event markerClick
34812 * Fires when click the marker.
34813 * @param {Roo.bootstrap.LocationPicker} this
34814 * @param {Map event} e
34816 markerClick : true,
34818 * @event markerRightClick
34819 * Fires when right click the marker.
34820 * @param {Roo.bootstrap.LocationPicker} this
34821 * @param {Map event} e
34823 markerRightClick : true,
34825 * @event OverlayViewDraw
34826 * Fires when OverlayView Draw
34827 * @param {Roo.bootstrap.LocationPicker} this
34829 OverlayViewDraw : true,
34831 * @event OverlayViewOnAdd
34832 * Fires when OverlayView Draw
34833 * @param {Roo.bootstrap.LocationPicker} this
34835 OverlayViewOnAdd : true,
34837 * @event OverlayViewOnRemove
34838 * Fires when OverlayView Draw
34839 * @param {Roo.bootstrap.LocationPicker} this
34841 OverlayViewOnRemove : true,
34843 * @event OverlayViewShow
34844 * Fires when OverlayView Draw
34845 * @param {Roo.bootstrap.LocationPicker} this
34846 * @param {Pixel} cpx
34848 OverlayViewShow : true,
34850 * @event OverlayViewHide
34851 * Fires when OverlayView Draw
34852 * @param {Roo.bootstrap.LocationPicker} this
34854 OverlayViewHide : true,
34856 * @event loadexception
34857 * Fires when load google lib failed.
34858 * @param {Roo.bootstrap.LocationPicker} this
34860 loadexception : true
34865 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
34867 gMapContext: false,
34873 mapTypeControl: false,
34874 disableDoubleClickZoom: false,
34876 streetViewControl: false,
34880 enableAutocomplete: false,
34881 enableReverseGeocode: true,
34884 getAutoCreate: function()
34889 cls: 'roo-location-picker'
34895 initEvents: function(ct, position)
34897 if(!this.el.getWidth() || this.isApplied()){
34901 this.el.setVisibilityMode(Roo.Element.DISPLAY);
34906 initial: function()
34908 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
34909 this.fireEvent('loadexception', this);
34913 if(!this.mapTypeId){
34914 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
34917 this.gMapContext = this.GMapContext();
34919 this.initOverlayView();
34921 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
34925 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
34926 _this.setPosition(_this.gMapContext.marker.position);
34929 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
34930 _this.fireEvent('mapClick', this, event);
34934 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
34935 _this.fireEvent('mapRightClick', this, event);
34939 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
34940 _this.fireEvent('markerClick', this, event);
34944 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
34945 _this.fireEvent('markerRightClick', this, event);
34949 this.setPosition(this.gMapContext.location);
34951 this.fireEvent('initial', this, this.gMapContext.location);
34954 initOverlayView: function()
34958 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
34962 _this.fireEvent('OverlayViewDraw', _this);
34967 _this.fireEvent('OverlayViewOnAdd', _this);
34970 onRemove: function()
34972 _this.fireEvent('OverlayViewOnRemove', _this);
34975 show: function(cpx)
34977 _this.fireEvent('OverlayViewShow', _this, cpx);
34982 _this.fireEvent('OverlayViewHide', _this);
34988 fromLatLngToContainerPixel: function(event)
34990 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
34993 isApplied: function()
34995 return this.getGmapContext() == false ? false : true;
34998 getGmapContext: function()
35000 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
35003 GMapContext: function()
35005 var position = new google.maps.LatLng(this.latitude, this.longitude);
35007 var _map = new google.maps.Map(this.el.dom, {
35010 mapTypeId: this.mapTypeId,
35011 mapTypeControl: this.mapTypeControl,
35012 disableDoubleClickZoom: this.disableDoubleClickZoom,
35013 scrollwheel: this.scrollwheel,
35014 streetViewControl: this.streetViewControl,
35015 locationName: this.locationName,
35016 draggable: this.draggable,
35017 enableAutocomplete: this.enableAutocomplete,
35018 enableReverseGeocode: this.enableReverseGeocode
35021 var _marker = new google.maps.Marker({
35022 position: position,
35024 title: this.markerTitle,
35025 draggable: this.draggable
35032 location: position,
35033 radius: this.radius,
35034 locationName: this.locationName,
35035 addressComponents: {
35036 formatted_address: null,
35037 addressLine1: null,
35038 addressLine2: null,
35040 streetNumber: null,
35044 stateOrProvince: null
35047 domContainer: this.el.dom,
35048 geodecoder: new google.maps.Geocoder()
35052 drawCircle: function(center, radius, options)
35054 if (this.gMapContext.circle != null) {
35055 this.gMapContext.circle.setMap(null);
35059 options = Roo.apply({}, options, {
35060 strokeColor: "#0000FF",
35061 strokeOpacity: .35,
35063 fillColor: "#0000FF",
35067 options.map = this.gMapContext.map;
35068 options.radius = radius;
35069 options.center = center;
35070 this.gMapContext.circle = new google.maps.Circle(options);
35071 return this.gMapContext.circle;
35077 setPosition: function(location)
35079 this.gMapContext.location = location;
35080 this.gMapContext.marker.setPosition(location);
35081 this.gMapContext.map.panTo(location);
35082 this.drawCircle(location, this.gMapContext.radius, {});
35086 if (this.gMapContext.settings.enableReverseGeocode) {
35087 this.gMapContext.geodecoder.geocode({
35088 latLng: this.gMapContext.location
35089 }, function(results, status) {
35091 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
35092 _this.gMapContext.locationName = results[0].formatted_address;
35093 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
35095 _this.fireEvent('positionchanged', this, location);
35102 this.fireEvent('positionchanged', this, location);
35107 google.maps.event.trigger(this.gMapContext.map, "resize");
35109 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
35111 this.fireEvent('resize', this);
35114 setPositionByLatLng: function(latitude, longitude)
35116 this.setPosition(new google.maps.LatLng(latitude, longitude));
35119 getCurrentPosition: function()
35122 latitude: this.gMapContext.location.lat(),
35123 longitude: this.gMapContext.location.lng()
35127 getAddressName: function()
35129 return this.gMapContext.locationName;
35132 getAddressComponents: function()
35134 return this.gMapContext.addressComponents;
35137 address_component_from_google_geocode: function(address_components)
35141 for (var i = 0; i < address_components.length; i++) {
35142 var component = address_components[i];
35143 if (component.types.indexOf("postal_code") >= 0) {
35144 result.postalCode = component.short_name;
35145 } else if (component.types.indexOf("street_number") >= 0) {
35146 result.streetNumber = component.short_name;
35147 } else if (component.types.indexOf("route") >= 0) {
35148 result.streetName = component.short_name;
35149 } else if (component.types.indexOf("neighborhood") >= 0) {
35150 result.city = component.short_name;
35151 } else if (component.types.indexOf("locality") >= 0) {
35152 result.city = component.short_name;
35153 } else if (component.types.indexOf("sublocality") >= 0) {
35154 result.district = component.short_name;
35155 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
35156 result.stateOrProvince = component.short_name;
35157 } else if (component.types.indexOf("country") >= 0) {
35158 result.country = component.short_name;
35162 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
35163 result.addressLine2 = "";
35167 setZoomLevel: function(zoom)
35169 this.gMapContext.map.setZoom(zoom);
35182 this.fireEvent('show', this);
35193 this.fireEvent('hide', this);
35198 Roo.apply(Roo.bootstrap.LocationPicker, {
35200 OverlayView : function(map, options)
35202 options = options || {};
35209 * @class Roo.bootstrap.Alert
35210 * @extends Roo.bootstrap.Component
35211 * Bootstrap Alert class - shows an alert area box
35213 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
35214 Enter a valid email address
35217 * @cfg {String} title The title of alert
35218 * @cfg {String} html The content of alert
35219 * @cfg {String} weight (success|info|warning|danger) Weight of the message
35220 * @cfg {String} fa font-awesomeicon
35221 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
35222 * @cfg {Boolean} close true to show a x closer
35226 * Create a new alert
35227 * @param {Object} config The config object
35231 Roo.bootstrap.Alert = function(config){
35232 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
35236 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
35242 faicon: false, // BC
35246 getAutoCreate : function()
35258 style : this.close ? '' : 'display:none'
35262 cls : 'roo-alert-icon'
35267 cls : 'roo-alert-title',
35272 cls : 'roo-alert-text',
35279 cfg.cn[0].cls += ' fa ' + this.faicon;
35282 cfg.cn[0].cls += ' fa ' + this.fa;
35286 cfg.cls += ' alert-' + this.weight;
35292 initEvents: function()
35294 this.el.setVisibilityMode(Roo.Element.DISPLAY);
35295 this.titleEl = this.el.select('.roo-alert-title',true).first();
35296 this.iconEl = this.el.select('.roo-alert-icon',true).first();
35297 this.htmlEl = this.el.select('.roo-alert-text',true).first();
35298 if (this.seconds > 0) {
35299 this.hide.defer(this.seconds, this);
35303 * Set the Title Message HTML
35304 * @param {String} html
35306 setTitle : function(str)
35308 this.titleEl.dom.innerHTML = str;
35312 * Set the Body Message HTML
35313 * @param {String} html
35315 setHtml : function(str)
35317 this.htmlEl.dom.innerHTML = str;
35320 * Set the Weight of the alert
35321 * @param {String} (success|info|warning|danger) weight
35324 setWeight : function(weight)
35327 this.el.removeClass('alert-' + this.weight);
35330 this.weight = weight;
35332 this.el.addClass('alert-' + this.weight);
35335 * Set the Icon of the alert
35336 * @param {String} see fontawsome names (name without the 'fa-' bit)
35338 setIcon : function(icon)
35341 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
35344 this.faicon = icon;
35346 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
35371 * @class Roo.bootstrap.UploadCropbox
35372 * @extends Roo.bootstrap.Component
35373 * Bootstrap UploadCropbox class
35374 * @cfg {String} emptyText show when image has been loaded
35375 * @cfg {String} rotateNotify show when image too small to rotate
35376 * @cfg {Number} errorTimeout default 3000
35377 * @cfg {Number} minWidth default 300
35378 * @cfg {Number} minHeight default 300
35379 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
35380 * @cfg {Boolean} isDocument (true|false) default false
35381 * @cfg {String} url action url
35382 * @cfg {String} paramName default 'imageUpload'
35383 * @cfg {String} method default POST
35384 * @cfg {Boolean} loadMask (true|false) default true
35385 * @cfg {Boolean} loadingText default 'Loading...'
35388 * Create a new UploadCropbox
35389 * @param {Object} config The config object
35392 Roo.bootstrap.UploadCropbox = function(config){
35393 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
35397 * @event beforeselectfile
35398 * Fire before select file
35399 * @param {Roo.bootstrap.UploadCropbox} this
35401 "beforeselectfile" : true,
35404 * Fire after initEvent
35405 * @param {Roo.bootstrap.UploadCropbox} this
35410 * Fire after initEvent
35411 * @param {Roo.bootstrap.UploadCropbox} this
35412 * @param {String} data
35417 * Fire when preparing the file data
35418 * @param {Roo.bootstrap.UploadCropbox} this
35419 * @param {Object} file
35424 * Fire when get exception
35425 * @param {Roo.bootstrap.UploadCropbox} this
35426 * @param {XMLHttpRequest} xhr
35428 "exception" : true,
35430 * @event beforeloadcanvas
35431 * Fire before load the canvas
35432 * @param {Roo.bootstrap.UploadCropbox} this
35433 * @param {String} src
35435 "beforeloadcanvas" : true,
35438 * Fire when trash image
35439 * @param {Roo.bootstrap.UploadCropbox} this
35444 * Fire when download the image
35445 * @param {Roo.bootstrap.UploadCropbox} this
35449 * @event footerbuttonclick
35450 * Fire when footerbuttonclick
35451 * @param {Roo.bootstrap.UploadCropbox} this
35452 * @param {String} type
35454 "footerbuttonclick" : true,
35458 * @param {Roo.bootstrap.UploadCropbox} this
35463 * Fire when rotate the image
35464 * @param {Roo.bootstrap.UploadCropbox} this
35465 * @param {String} pos
35470 * Fire when inspect the file
35471 * @param {Roo.bootstrap.UploadCropbox} this
35472 * @param {Object} file
35477 * Fire when xhr upload the file
35478 * @param {Roo.bootstrap.UploadCropbox} this
35479 * @param {Object} data
35484 * Fire when arrange the file data
35485 * @param {Roo.bootstrap.UploadCropbox} this
35486 * @param {Object} formData
35491 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
35494 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
35496 emptyText : 'Click to upload image',
35497 rotateNotify : 'Image is too small to rotate',
35498 errorTimeout : 3000,
35512 cropType : 'image/jpeg',
35514 canvasLoaded : false,
35515 isDocument : false,
35517 paramName : 'imageUpload',
35519 loadingText : 'Loading...',
35522 getAutoCreate : function()
35526 cls : 'roo-upload-cropbox',
35530 cls : 'roo-upload-cropbox-selector',
35535 cls : 'roo-upload-cropbox-body',
35536 style : 'cursor:pointer',
35540 cls : 'roo-upload-cropbox-preview'
35544 cls : 'roo-upload-cropbox-thumb'
35548 cls : 'roo-upload-cropbox-empty-notify',
35549 html : this.emptyText
35553 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
35554 html : this.rotateNotify
35560 cls : 'roo-upload-cropbox-footer',
35563 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
35573 onRender : function(ct, position)
35575 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
35577 if (this.buttons.length) {
35579 Roo.each(this.buttons, function(bb) {
35581 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
35583 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
35589 this.maskEl = this.el;
35593 initEvents : function()
35595 this.urlAPI = (window.createObjectURL && window) ||
35596 (window.URL && URL.revokeObjectURL && URL) ||
35597 (window.webkitURL && webkitURL);
35599 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
35600 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35602 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
35603 this.selectorEl.hide();
35605 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
35606 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35608 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
35609 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35610 this.thumbEl.hide();
35612 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
35613 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35615 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
35616 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35617 this.errorEl.hide();
35619 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
35620 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35621 this.footerEl.hide();
35623 this.setThumbBoxSize();
35629 this.fireEvent('initial', this);
35636 window.addEventListener("resize", function() { _this.resize(); } );
35638 this.bodyEl.on('click', this.beforeSelectFile, this);
35641 this.bodyEl.on('touchstart', this.onTouchStart, this);
35642 this.bodyEl.on('touchmove', this.onTouchMove, this);
35643 this.bodyEl.on('touchend', this.onTouchEnd, this);
35647 this.bodyEl.on('mousedown', this.onMouseDown, this);
35648 this.bodyEl.on('mousemove', this.onMouseMove, this);
35649 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
35650 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
35651 Roo.get(document).on('mouseup', this.onMouseUp, this);
35654 this.selectorEl.on('change', this.onFileSelected, this);
35660 this.baseScale = 1;
35662 this.baseRotate = 1;
35663 this.dragable = false;
35664 this.pinching = false;
35667 this.cropData = false;
35668 this.notifyEl.dom.innerHTML = this.emptyText;
35670 this.selectorEl.dom.value = '';
35674 resize : function()
35676 if(this.fireEvent('resize', this) != false){
35677 this.setThumbBoxPosition();
35678 this.setCanvasPosition();
35682 onFooterButtonClick : function(e, el, o, type)
35685 case 'rotate-left' :
35686 this.onRotateLeft(e);
35688 case 'rotate-right' :
35689 this.onRotateRight(e);
35692 this.beforeSelectFile(e);
35707 this.fireEvent('footerbuttonclick', this, type);
35710 beforeSelectFile : function(e)
35712 e.preventDefault();
35714 if(this.fireEvent('beforeselectfile', this) != false){
35715 this.selectorEl.dom.click();
35719 onFileSelected : function(e)
35721 e.preventDefault();
35723 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35727 var file = this.selectorEl.dom.files[0];
35729 if(this.fireEvent('inspect', this, file) != false){
35730 this.prepare(file);
35735 trash : function(e)
35737 this.fireEvent('trash', this);
35740 download : function(e)
35742 this.fireEvent('download', this);
35745 loadCanvas : function(src)
35747 if(this.fireEvent('beforeloadcanvas', this, src) != false){
35751 this.imageEl = document.createElement('img');
35755 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
35757 this.imageEl.src = src;
35761 onLoadCanvas : function()
35763 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
35764 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
35766 this.bodyEl.un('click', this.beforeSelectFile, this);
35768 this.notifyEl.hide();
35769 this.thumbEl.show();
35770 this.footerEl.show();
35772 this.baseRotateLevel();
35774 if(this.isDocument){
35775 this.setThumbBoxSize();
35778 this.setThumbBoxPosition();
35780 this.baseScaleLevel();
35786 this.canvasLoaded = true;
35789 this.maskEl.unmask();
35794 setCanvasPosition : function()
35796 if(!this.canvasEl){
35800 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
35801 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
35803 this.previewEl.setLeft(pw);
35804 this.previewEl.setTop(ph);
35808 onMouseDown : function(e)
35812 this.dragable = true;
35813 this.pinching = false;
35815 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
35816 this.dragable = false;
35820 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35821 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35825 onMouseMove : function(e)
35829 if(!this.canvasLoaded){
35833 if (!this.dragable){
35837 var minX = Math.ceil(this.thumbEl.getLeft(true));
35838 var minY = Math.ceil(this.thumbEl.getTop(true));
35840 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
35841 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
35843 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35844 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35846 x = x - this.mouseX;
35847 y = y - this.mouseY;
35849 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
35850 var bgY = Math.ceil(y + this.previewEl.getTop(true));
35852 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
35853 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
35855 this.previewEl.setLeft(bgX);
35856 this.previewEl.setTop(bgY);
35858 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
35859 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
35862 onMouseUp : function(e)
35866 this.dragable = false;
35869 onMouseWheel : function(e)
35873 this.startScale = this.scale;
35875 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
35877 if(!this.zoomable()){
35878 this.scale = this.startScale;
35887 zoomable : function()
35889 var minScale = this.thumbEl.getWidth() / this.minWidth;
35891 if(this.minWidth < this.minHeight){
35892 minScale = this.thumbEl.getHeight() / this.minHeight;
35895 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
35896 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
35900 (this.rotate == 0 || this.rotate == 180) &&
35902 width > this.imageEl.OriginWidth ||
35903 height > this.imageEl.OriginHeight ||
35904 (width < this.minWidth && height < this.minHeight)
35912 (this.rotate == 90 || this.rotate == 270) &&
35914 width > this.imageEl.OriginWidth ||
35915 height > this.imageEl.OriginHeight ||
35916 (width < this.minHeight && height < this.minWidth)
35923 !this.isDocument &&
35924 (this.rotate == 0 || this.rotate == 180) &&
35926 width < this.minWidth ||
35927 width > this.imageEl.OriginWidth ||
35928 height < this.minHeight ||
35929 height > this.imageEl.OriginHeight
35936 !this.isDocument &&
35937 (this.rotate == 90 || this.rotate == 270) &&
35939 width < this.minHeight ||
35940 width > this.imageEl.OriginWidth ||
35941 height < this.minWidth ||
35942 height > this.imageEl.OriginHeight
35952 onRotateLeft : function(e)
35954 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
35956 var minScale = this.thumbEl.getWidth() / this.minWidth;
35958 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
35959 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
35961 this.startScale = this.scale;
35963 while (this.getScaleLevel() < minScale){
35965 this.scale = this.scale + 1;
35967 if(!this.zoomable()){
35972 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
35973 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
35978 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35985 this.scale = this.startScale;
35987 this.onRotateFail();
35992 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
35994 if(this.isDocument){
35995 this.setThumbBoxSize();
35996 this.setThumbBoxPosition();
35997 this.setCanvasPosition();
36002 this.fireEvent('rotate', this, 'left');
36006 onRotateRight : function(e)
36008 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
36010 var minScale = this.thumbEl.getWidth() / this.minWidth;
36012 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
36013 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
36015 this.startScale = this.scale;
36017 while (this.getScaleLevel() < minScale){
36019 this.scale = this.scale + 1;
36021 if(!this.zoomable()){
36026 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
36027 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
36032 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36039 this.scale = this.startScale;
36041 this.onRotateFail();
36046 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
36048 if(this.isDocument){
36049 this.setThumbBoxSize();
36050 this.setThumbBoxPosition();
36051 this.setCanvasPosition();
36056 this.fireEvent('rotate', this, 'right');
36059 onRotateFail : function()
36061 this.errorEl.show(true);
36065 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
36070 this.previewEl.dom.innerHTML = '';
36072 var canvasEl = document.createElement("canvas");
36074 var contextEl = canvasEl.getContext("2d");
36076 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36077 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36078 var center = this.imageEl.OriginWidth / 2;
36080 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
36081 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36082 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36083 center = this.imageEl.OriginHeight / 2;
36086 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
36088 contextEl.translate(center, center);
36089 contextEl.rotate(this.rotate * Math.PI / 180);
36091 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36093 this.canvasEl = document.createElement("canvas");
36095 this.contextEl = this.canvasEl.getContext("2d");
36097 switch (this.rotate) {
36100 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36101 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36103 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36108 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36109 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36111 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36112 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);
36116 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36121 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
36122 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
36124 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36125 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);
36129 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);
36134 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
36135 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
36137 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36138 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
36142 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);
36149 this.previewEl.appendChild(this.canvasEl);
36151 this.setCanvasPosition();
36156 if(!this.canvasLoaded){
36160 var imageCanvas = document.createElement("canvas");
36162 var imageContext = imageCanvas.getContext("2d");
36164 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36165 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
36167 var center = imageCanvas.width / 2;
36169 imageContext.translate(center, center);
36171 imageContext.rotate(this.rotate * Math.PI / 180);
36173 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
36175 var canvas = document.createElement("canvas");
36177 var context = canvas.getContext("2d");
36179 canvas.width = this.minWidth;
36180 canvas.height = this.minHeight;
36182 switch (this.rotate) {
36185 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36186 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36188 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36189 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36191 var targetWidth = this.minWidth - 2 * x;
36192 var targetHeight = this.minHeight - 2 * y;
36196 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36197 scale = targetWidth / width;
36200 if(x > 0 && y == 0){
36201 scale = targetHeight / height;
36204 if(x > 0 && y > 0){
36205 scale = targetWidth / width;
36207 if(width < height){
36208 scale = targetHeight / height;
36212 context.scale(scale, scale);
36214 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36215 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36217 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36218 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36220 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36225 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36226 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36228 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36229 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36231 var targetWidth = this.minWidth - 2 * x;
36232 var targetHeight = this.minHeight - 2 * y;
36236 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36237 scale = targetWidth / width;
36240 if(x > 0 && y == 0){
36241 scale = targetHeight / height;
36244 if(x > 0 && y > 0){
36245 scale = targetWidth / width;
36247 if(width < height){
36248 scale = targetHeight / height;
36252 context.scale(scale, scale);
36254 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36255 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36257 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36258 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36260 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36262 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36267 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
36268 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
36270 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36271 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36273 var targetWidth = this.minWidth - 2 * x;
36274 var targetHeight = this.minHeight - 2 * y;
36278 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36279 scale = targetWidth / width;
36282 if(x > 0 && y == 0){
36283 scale = targetHeight / height;
36286 if(x > 0 && y > 0){
36287 scale = targetWidth / width;
36289 if(width < height){
36290 scale = targetHeight / height;
36294 context.scale(scale, scale);
36296 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36297 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36299 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36300 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36302 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36303 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
36305 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36310 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
36311 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
36313 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
36314 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
36316 var targetWidth = this.minWidth - 2 * x;
36317 var targetHeight = this.minHeight - 2 * y;
36321 if((x == 0 && y == 0) || (x == 0 && y > 0)){
36322 scale = targetWidth / width;
36325 if(x > 0 && y == 0){
36326 scale = targetHeight / height;
36329 if(x > 0 && y > 0){
36330 scale = targetWidth / width;
36332 if(width < height){
36333 scale = targetHeight / height;
36337 context.scale(scale, scale);
36339 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
36340 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
36342 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
36343 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
36345 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
36347 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
36354 this.cropData = canvas.toDataURL(this.cropType);
36356 if(this.fireEvent('crop', this, this.cropData) !== false){
36357 this.process(this.file, this.cropData);
36364 setThumbBoxSize : function()
36368 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
36369 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
36370 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
36372 this.minWidth = width;
36373 this.minHeight = height;
36375 if(this.rotate == 90 || this.rotate == 270){
36376 this.minWidth = height;
36377 this.minHeight = width;
36382 width = Math.ceil(this.minWidth * height / this.minHeight);
36384 if(this.minWidth > this.minHeight){
36386 height = Math.ceil(this.minHeight * width / this.minWidth);
36389 this.thumbEl.setStyle({
36390 width : width + 'px',
36391 height : height + 'px'
36398 setThumbBoxPosition : function()
36400 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
36401 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
36403 this.thumbEl.setLeft(x);
36404 this.thumbEl.setTop(y);
36408 baseRotateLevel : function()
36410 this.baseRotate = 1;
36413 typeof(this.exif) != 'undefined' &&
36414 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
36415 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
36417 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
36420 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
36424 baseScaleLevel : function()
36428 if(this.isDocument){
36430 if(this.baseRotate == 6 || this.baseRotate == 8){
36432 height = this.thumbEl.getHeight();
36433 this.baseScale = height / this.imageEl.OriginWidth;
36435 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
36436 width = this.thumbEl.getWidth();
36437 this.baseScale = width / this.imageEl.OriginHeight;
36443 height = this.thumbEl.getHeight();
36444 this.baseScale = height / this.imageEl.OriginHeight;
36446 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
36447 width = this.thumbEl.getWidth();
36448 this.baseScale = width / this.imageEl.OriginWidth;
36454 if(this.baseRotate == 6 || this.baseRotate == 8){
36456 width = this.thumbEl.getHeight();
36457 this.baseScale = width / this.imageEl.OriginHeight;
36459 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
36460 height = this.thumbEl.getWidth();
36461 this.baseScale = height / this.imageEl.OriginHeight;
36464 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36465 height = this.thumbEl.getWidth();
36466 this.baseScale = height / this.imageEl.OriginHeight;
36468 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
36469 width = this.thumbEl.getHeight();
36470 this.baseScale = width / this.imageEl.OriginWidth;
36477 width = this.thumbEl.getWidth();
36478 this.baseScale = width / this.imageEl.OriginWidth;
36480 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
36481 height = this.thumbEl.getHeight();
36482 this.baseScale = height / this.imageEl.OriginHeight;
36485 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
36487 height = this.thumbEl.getHeight();
36488 this.baseScale = height / this.imageEl.OriginHeight;
36490 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
36491 width = this.thumbEl.getWidth();
36492 this.baseScale = width / this.imageEl.OriginWidth;
36500 getScaleLevel : function()
36502 return this.baseScale * Math.pow(1.1, this.scale);
36505 onTouchStart : function(e)
36507 if(!this.canvasLoaded){
36508 this.beforeSelectFile(e);
36512 var touches = e.browserEvent.touches;
36518 if(touches.length == 1){
36519 this.onMouseDown(e);
36523 if(touches.length != 2){
36529 for(var i = 0, finger; finger = touches[i]; i++){
36530 coords.push(finger.pageX, finger.pageY);
36533 var x = Math.pow(coords[0] - coords[2], 2);
36534 var y = Math.pow(coords[1] - coords[3], 2);
36536 this.startDistance = Math.sqrt(x + y);
36538 this.startScale = this.scale;
36540 this.pinching = true;
36541 this.dragable = false;
36545 onTouchMove : function(e)
36547 if(!this.pinching && !this.dragable){
36551 var touches = e.browserEvent.touches;
36558 this.onMouseMove(e);
36564 for(var i = 0, finger; finger = touches[i]; i++){
36565 coords.push(finger.pageX, finger.pageY);
36568 var x = Math.pow(coords[0] - coords[2], 2);
36569 var y = Math.pow(coords[1] - coords[3], 2);
36571 this.endDistance = Math.sqrt(x + y);
36573 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
36575 if(!this.zoomable()){
36576 this.scale = this.startScale;
36584 onTouchEnd : function(e)
36586 this.pinching = false;
36587 this.dragable = false;
36591 process : function(file, crop)
36594 this.maskEl.mask(this.loadingText);
36597 this.xhr = new XMLHttpRequest();
36599 file.xhr = this.xhr;
36601 this.xhr.open(this.method, this.url, true);
36604 "Accept": "application/json",
36605 "Cache-Control": "no-cache",
36606 "X-Requested-With": "XMLHttpRequest"
36609 for (var headerName in headers) {
36610 var headerValue = headers[headerName];
36612 this.xhr.setRequestHeader(headerName, headerValue);
36618 this.xhr.onload = function()
36620 _this.xhrOnLoad(_this.xhr);
36623 this.xhr.onerror = function()
36625 _this.xhrOnError(_this.xhr);
36628 var formData = new FormData();
36630 formData.append('returnHTML', 'NO');
36633 formData.append('crop', crop);
36636 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
36637 formData.append(this.paramName, file, file.name);
36640 if(typeof(file.filename) != 'undefined'){
36641 formData.append('filename', file.filename);
36644 if(typeof(file.mimetype) != 'undefined'){
36645 formData.append('mimetype', file.mimetype);
36648 if(this.fireEvent('arrange', this, formData) != false){
36649 this.xhr.send(formData);
36653 xhrOnLoad : function(xhr)
36656 this.maskEl.unmask();
36659 if (xhr.readyState !== 4) {
36660 this.fireEvent('exception', this, xhr);
36664 var response = Roo.decode(xhr.responseText);
36666 if(!response.success){
36667 this.fireEvent('exception', this, xhr);
36671 var response = Roo.decode(xhr.responseText);
36673 this.fireEvent('upload', this, response);
36677 xhrOnError : function()
36680 this.maskEl.unmask();
36683 Roo.log('xhr on error');
36685 var response = Roo.decode(xhr.responseText);
36691 prepare : function(file)
36694 this.maskEl.mask(this.loadingText);
36700 if(typeof(file) === 'string'){
36701 this.loadCanvas(file);
36705 if(!file || !this.urlAPI){
36710 this.cropType = file.type;
36714 if(this.fireEvent('prepare', this, this.file) != false){
36716 var reader = new FileReader();
36718 reader.onload = function (e) {
36719 if (e.target.error) {
36720 Roo.log(e.target.error);
36724 var buffer = e.target.result,
36725 dataView = new DataView(buffer),
36727 maxOffset = dataView.byteLength - 4,
36731 if (dataView.getUint16(0) === 0xffd8) {
36732 while (offset < maxOffset) {
36733 markerBytes = dataView.getUint16(offset);
36735 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
36736 markerLength = dataView.getUint16(offset + 2) + 2;
36737 if (offset + markerLength > dataView.byteLength) {
36738 Roo.log('Invalid meta data: Invalid segment size.');
36742 if(markerBytes == 0xffe1){
36743 _this.parseExifData(
36750 offset += markerLength;
36760 var url = _this.urlAPI.createObjectURL(_this.file);
36762 _this.loadCanvas(url);
36767 reader.readAsArrayBuffer(this.file);
36773 parseExifData : function(dataView, offset, length)
36775 var tiffOffset = offset + 10,
36779 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36780 // No Exif data, might be XMP data instead
36784 // Check for the ASCII code for "Exif" (0x45786966):
36785 if (dataView.getUint32(offset + 4) !== 0x45786966) {
36786 // No Exif data, might be XMP data instead
36789 if (tiffOffset + 8 > dataView.byteLength) {
36790 Roo.log('Invalid Exif data: Invalid segment size.');
36793 // Check for the two null bytes:
36794 if (dataView.getUint16(offset + 8) !== 0x0000) {
36795 Roo.log('Invalid Exif data: Missing byte alignment offset.');
36798 // Check the byte alignment:
36799 switch (dataView.getUint16(tiffOffset)) {
36801 littleEndian = true;
36804 littleEndian = false;
36807 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
36810 // Check for the TIFF tag marker (0x002A):
36811 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
36812 Roo.log('Invalid Exif data: Missing TIFF marker.');
36815 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
36816 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
36818 this.parseExifTags(
36821 tiffOffset + dirOffset,
36826 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
36831 if (dirOffset + 6 > dataView.byteLength) {
36832 Roo.log('Invalid Exif data: Invalid directory offset.');
36835 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
36836 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
36837 if (dirEndOffset + 4 > dataView.byteLength) {
36838 Roo.log('Invalid Exif data: Invalid directory size.');
36841 for (i = 0; i < tagsNumber; i += 1) {
36845 dirOffset + 2 + 12 * i, // tag offset
36849 // Return the offset to the next directory:
36850 return dataView.getUint32(dirEndOffset, littleEndian);
36853 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
36855 var tag = dataView.getUint16(offset, littleEndian);
36857 this.exif[tag] = this.getExifValue(
36861 dataView.getUint16(offset + 2, littleEndian), // tag type
36862 dataView.getUint32(offset + 4, littleEndian), // tag length
36867 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
36869 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
36878 Roo.log('Invalid Exif data: Invalid tag type.');
36882 tagSize = tagType.size * length;
36883 // Determine if the value is contained in the dataOffset bytes,
36884 // or if the value at the dataOffset is a pointer to the actual data:
36885 dataOffset = tagSize > 4 ?
36886 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
36887 if (dataOffset + tagSize > dataView.byteLength) {
36888 Roo.log('Invalid Exif data: Invalid data offset.');
36891 if (length === 1) {
36892 return tagType.getValue(dataView, dataOffset, littleEndian);
36895 for (i = 0; i < length; i += 1) {
36896 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
36899 if (tagType.ascii) {
36901 // Concatenate the chars:
36902 for (i = 0; i < values.length; i += 1) {
36904 // Ignore the terminating NULL byte(s):
36905 if (c === '\u0000') {
36917 Roo.apply(Roo.bootstrap.UploadCropbox, {
36919 'Orientation': 0x0112
36923 1: 0, //'top-left',
36925 3: 180, //'bottom-right',
36926 // 4: 'bottom-left',
36928 6: 90, //'right-top',
36929 // 7: 'right-bottom',
36930 8: 270 //'left-bottom'
36934 // byte, 8-bit unsigned int:
36936 getValue: function (dataView, dataOffset) {
36937 return dataView.getUint8(dataOffset);
36941 // ascii, 8-bit byte:
36943 getValue: function (dataView, dataOffset) {
36944 return String.fromCharCode(dataView.getUint8(dataOffset));
36949 // short, 16 bit int:
36951 getValue: function (dataView, dataOffset, littleEndian) {
36952 return dataView.getUint16(dataOffset, littleEndian);
36956 // long, 32 bit int:
36958 getValue: function (dataView, dataOffset, littleEndian) {
36959 return dataView.getUint32(dataOffset, littleEndian);
36963 // rational = two long values, first is numerator, second is denominator:
36965 getValue: function (dataView, dataOffset, littleEndian) {
36966 return dataView.getUint32(dataOffset, littleEndian) /
36967 dataView.getUint32(dataOffset + 4, littleEndian);
36971 // slong, 32 bit signed int:
36973 getValue: function (dataView, dataOffset, littleEndian) {
36974 return dataView.getInt32(dataOffset, littleEndian);
36978 // srational, two slongs, first is numerator, second is denominator:
36980 getValue: function (dataView, dataOffset, littleEndian) {
36981 return dataView.getInt32(dataOffset, littleEndian) /
36982 dataView.getInt32(dataOffset + 4, littleEndian);
36992 cls : 'btn-group roo-upload-cropbox-rotate-left',
36993 action : 'rotate-left',
36997 cls : 'btn btn-default',
36998 html : '<i class="fa fa-undo"></i>'
37004 cls : 'btn-group roo-upload-cropbox-picture',
37005 action : 'picture',
37009 cls : 'btn btn-default',
37010 html : '<i class="fa fa-picture-o"></i>'
37016 cls : 'btn-group roo-upload-cropbox-rotate-right',
37017 action : 'rotate-right',
37021 cls : 'btn btn-default',
37022 html : '<i class="fa fa-repeat"></i>'
37030 cls : 'btn-group roo-upload-cropbox-rotate-left',
37031 action : 'rotate-left',
37035 cls : 'btn btn-default',
37036 html : '<i class="fa fa-undo"></i>'
37042 cls : 'btn-group roo-upload-cropbox-download',
37043 action : 'download',
37047 cls : 'btn btn-default',
37048 html : '<i class="fa fa-download"></i>'
37054 cls : 'btn-group roo-upload-cropbox-crop',
37059 cls : 'btn btn-default',
37060 html : '<i class="fa fa-crop"></i>'
37066 cls : 'btn-group roo-upload-cropbox-trash',
37071 cls : 'btn btn-default',
37072 html : '<i class="fa fa-trash"></i>'
37078 cls : 'btn-group roo-upload-cropbox-rotate-right',
37079 action : 'rotate-right',
37083 cls : 'btn btn-default',
37084 html : '<i class="fa fa-repeat"></i>'
37092 cls : 'btn-group roo-upload-cropbox-rotate-left',
37093 action : 'rotate-left',
37097 cls : 'btn btn-default',
37098 html : '<i class="fa fa-undo"></i>'
37104 cls : 'btn-group roo-upload-cropbox-rotate-right',
37105 action : 'rotate-right',
37109 cls : 'btn btn-default',
37110 html : '<i class="fa fa-repeat"></i>'
37123 * @class Roo.bootstrap.DocumentManager
37124 * @extends Roo.bootstrap.Component
37125 * Bootstrap DocumentManager class
37126 * @cfg {String} paramName default 'imageUpload'
37127 * @cfg {String} toolTipName default 'filename'
37128 * @cfg {String} method default POST
37129 * @cfg {String} url action url
37130 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
37131 * @cfg {Boolean} multiple multiple upload default true
37132 * @cfg {Number} thumbSize default 300
37133 * @cfg {String} fieldLabel
37134 * @cfg {Number} labelWidth default 4
37135 * @cfg {String} labelAlign (left|top) default left
37136 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
37137 * @cfg {Number} labellg set the width of label (1-12)
37138 * @cfg {Number} labelmd set the width of label (1-12)
37139 * @cfg {Number} labelsm set the width of label (1-12)
37140 * @cfg {Number} labelxs set the width of label (1-12)
37143 * Create a new DocumentManager
37144 * @param {Object} config The config object
37147 Roo.bootstrap.DocumentManager = function(config){
37148 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
37151 this.delegates = [];
37156 * Fire when initial the DocumentManager
37157 * @param {Roo.bootstrap.DocumentManager} this
37162 * inspect selected file
37163 * @param {Roo.bootstrap.DocumentManager} this
37164 * @param {File} file
37169 * Fire when xhr load exception
37170 * @param {Roo.bootstrap.DocumentManager} this
37171 * @param {XMLHttpRequest} xhr
37173 "exception" : true,
37175 * @event afterupload
37176 * Fire when xhr load exception
37177 * @param {Roo.bootstrap.DocumentManager} this
37178 * @param {XMLHttpRequest} xhr
37180 "afterupload" : true,
37183 * prepare the form data
37184 * @param {Roo.bootstrap.DocumentManager} this
37185 * @param {Object} formData
37190 * Fire when remove the file
37191 * @param {Roo.bootstrap.DocumentManager} this
37192 * @param {Object} file
37197 * Fire after refresh the file
37198 * @param {Roo.bootstrap.DocumentManager} this
37203 * Fire after click the image
37204 * @param {Roo.bootstrap.DocumentManager} this
37205 * @param {Object} file
37210 * Fire when upload a image and editable set to true
37211 * @param {Roo.bootstrap.DocumentManager} this
37212 * @param {Object} file
37216 * @event beforeselectfile
37217 * Fire before select file
37218 * @param {Roo.bootstrap.DocumentManager} this
37220 "beforeselectfile" : true,
37223 * Fire before process file
37224 * @param {Roo.bootstrap.DocumentManager} this
37225 * @param {Object} file
37229 * @event previewrendered
37230 * Fire when preview rendered
37231 * @param {Roo.bootstrap.DocumentManager} this
37232 * @param {Object} file
37234 "previewrendered" : true,
37237 "previewResize" : true
37242 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
37251 paramName : 'imageUpload',
37252 toolTipName : 'filename',
37255 labelAlign : 'left',
37265 getAutoCreate : function()
37267 var managerWidget = {
37269 cls : 'roo-document-manager',
37273 cls : 'roo-document-manager-selector',
37278 cls : 'roo-document-manager-uploader',
37282 cls : 'roo-document-manager-upload-btn',
37283 html : '<i class="fa fa-plus"></i>'
37294 cls : 'column col-md-12',
37299 if(this.fieldLabel.length){
37304 cls : 'column col-md-12',
37305 html : this.fieldLabel
37309 cls : 'column col-md-12',
37314 if(this.labelAlign == 'left'){
37319 html : this.fieldLabel
37328 if(this.labelWidth > 12){
37329 content[0].style = "width: " + this.labelWidth + 'px';
37332 if(this.labelWidth < 13 && this.labelmd == 0){
37333 this.labelmd = this.labelWidth;
37336 if(this.labellg > 0){
37337 content[0].cls += ' col-lg-' + this.labellg;
37338 content[1].cls += ' col-lg-' + (12 - this.labellg);
37341 if(this.labelmd > 0){
37342 content[0].cls += ' col-md-' + this.labelmd;
37343 content[1].cls += ' col-md-' + (12 - this.labelmd);
37346 if(this.labelsm > 0){
37347 content[0].cls += ' col-sm-' + this.labelsm;
37348 content[1].cls += ' col-sm-' + (12 - this.labelsm);
37351 if(this.labelxs > 0){
37352 content[0].cls += ' col-xs-' + this.labelxs;
37353 content[1].cls += ' col-xs-' + (12 - this.labelxs);
37361 cls : 'row clearfix',
37369 initEvents : function()
37371 this.managerEl = this.el.select('.roo-document-manager', true).first();
37372 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37374 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
37375 this.selectorEl.hide();
37378 this.selectorEl.attr('multiple', 'multiple');
37381 this.selectorEl.on('change', this.onFileSelected, this);
37383 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
37384 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37386 this.uploader.on('click', this.onUploaderClick, this);
37388 this.renderProgressDialog();
37392 window.addEventListener("resize", function() { _this.refresh(); } );
37394 this.fireEvent('initial', this);
37397 renderProgressDialog : function()
37401 this.progressDialog = new Roo.bootstrap.Modal({
37402 cls : 'roo-document-manager-progress-dialog',
37403 allow_close : false,
37414 btnclick : function() {
37415 _this.uploadCancel();
37421 this.progressDialog.render(Roo.get(document.body));
37423 this.progress = new Roo.bootstrap.Progress({
37424 cls : 'roo-document-manager-progress',
37429 this.progress.render(this.progressDialog.getChildContainer());
37431 this.progressBar = new Roo.bootstrap.ProgressBar({
37432 cls : 'roo-document-manager-progress-bar',
37435 aria_valuemax : 12,
37439 this.progressBar.render(this.progress.getChildContainer());
37442 onUploaderClick : function(e)
37444 e.preventDefault();
37446 if(this.fireEvent('beforeselectfile', this) != false){
37447 this.selectorEl.dom.click();
37452 onFileSelected : function(e)
37454 e.preventDefault();
37456 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
37460 Roo.each(this.selectorEl.dom.files, function(file){
37461 if(this.fireEvent('inspect', this, file) != false){
37462 this.files.push(file);
37472 this.selectorEl.dom.value = '';
37474 if(!this.files || !this.files.length){
37478 if(this.boxes > 0 && this.files.length > this.boxes){
37479 this.files = this.files.slice(0, this.boxes);
37482 this.uploader.show();
37484 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37485 this.uploader.hide();
37494 Roo.each(this.files, function(file){
37496 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37497 var f = this.renderPreview(file);
37502 if(file.type.indexOf('image') != -1){
37503 this.delegates.push(
37505 _this.process(file);
37506 }).createDelegate(this)
37514 _this.process(file);
37515 }).createDelegate(this)
37520 this.files = files;
37522 this.delegates = this.delegates.concat(docs);
37524 if(!this.delegates.length){
37529 this.progressBar.aria_valuemax = this.delegates.length;
37536 arrange : function()
37538 if(!this.delegates.length){
37539 this.progressDialog.hide();
37544 var delegate = this.delegates.shift();
37546 this.progressDialog.show();
37548 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
37550 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
37555 refresh : function()
37557 this.uploader.show();
37559 if(this.boxes > 0 && this.files.length > this.boxes - 1){
37560 this.uploader.hide();
37563 Roo.isTouch ? this.closable(false) : this.closable(true);
37565 this.fireEvent('refresh', this);
37568 onRemove : function(e, el, o)
37570 e.preventDefault();
37572 this.fireEvent('remove', this, o);
37576 remove : function(o)
37580 Roo.each(this.files, function(file){
37581 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
37590 this.files = files;
37597 Roo.each(this.files, function(file){
37602 file.target.remove();
37611 onClick : function(e, el, o)
37613 e.preventDefault();
37615 this.fireEvent('click', this, o);
37619 closable : function(closable)
37621 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
37623 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
37635 xhrOnLoad : function(xhr)
37637 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37641 if (xhr.readyState !== 4) {
37643 this.fireEvent('exception', this, xhr);
37647 var response = Roo.decode(xhr.responseText);
37649 if(!response.success){
37651 this.fireEvent('exception', this, xhr);
37655 var file = this.renderPreview(response.data);
37657 this.files.push(file);
37661 this.fireEvent('afterupload', this, xhr);
37665 xhrOnError : function(xhr)
37667 Roo.log('xhr on error');
37669 var response = Roo.decode(xhr.responseText);
37676 process : function(file)
37678 if(this.fireEvent('process', this, file) !== false){
37679 if(this.editable && file.type.indexOf('image') != -1){
37680 this.fireEvent('edit', this, file);
37684 this.uploadStart(file, false);
37691 uploadStart : function(file, crop)
37693 this.xhr = new XMLHttpRequest();
37695 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
37700 file.xhr = this.xhr;
37702 this.managerEl.createChild({
37704 cls : 'roo-document-manager-loading',
37708 tooltip : file.name,
37709 cls : 'roo-document-manager-thumb',
37710 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37716 this.xhr.open(this.method, this.url, true);
37719 "Accept": "application/json",
37720 "Cache-Control": "no-cache",
37721 "X-Requested-With": "XMLHttpRequest"
37724 for (var headerName in headers) {
37725 var headerValue = headers[headerName];
37727 this.xhr.setRequestHeader(headerName, headerValue);
37733 this.xhr.onload = function()
37735 _this.xhrOnLoad(_this.xhr);
37738 this.xhr.onerror = function()
37740 _this.xhrOnError(_this.xhr);
37743 var formData = new FormData();
37745 formData.append('returnHTML', 'NO');
37748 formData.append('crop', crop);
37751 formData.append(this.paramName, file, file.name);
37758 if(this.fireEvent('prepare', this, formData, options) != false){
37760 if(options.manually){
37764 this.xhr.send(formData);
37768 this.uploadCancel();
37771 uploadCancel : function()
37777 this.delegates = [];
37779 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
37786 renderPreview : function(file)
37788 if(typeof(file.target) != 'undefined' && file.target){
37792 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
37794 var previewEl = this.managerEl.createChild({
37796 cls : 'roo-document-manager-preview',
37800 tooltip : file[this.toolTipName],
37801 cls : 'roo-document-manager-thumb',
37802 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
37807 html : '<i class="fa fa-times-circle"></i>'
37812 var close = previewEl.select('button.close', true).first();
37814 close.on('click', this.onRemove, this, file);
37816 file.target = previewEl;
37818 var image = previewEl.select('img', true).first();
37822 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
37824 image.on('click', this.onClick, this, file);
37826 this.fireEvent('previewrendered', this, file);
37832 onPreviewLoad : function(file, image)
37834 if(typeof(file.target) == 'undefined' || !file.target){
37838 var width = image.dom.naturalWidth || image.dom.width;
37839 var height = image.dom.naturalHeight || image.dom.height;
37841 if(!this.previewResize) {
37845 if(width > height){
37846 file.target.addClass('wide');
37850 file.target.addClass('tall');
37855 uploadFromSource : function(file, crop)
37857 this.xhr = new XMLHttpRequest();
37859 this.managerEl.createChild({
37861 cls : 'roo-document-manager-loading',
37865 tooltip : file.name,
37866 cls : 'roo-document-manager-thumb',
37867 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
37873 this.xhr.open(this.method, this.url, true);
37876 "Accept": "application/json",
37877 "Cache-Control": "no-cache",
37878 "X-Requested-With": "XMLHttpRequest"
37881 for (var headerName in headers) {
37882 var headerValue = headers[headerName];
37884 this.xhr.setRequestHeader(headerName, headerValue);
37890 this.xhr.onload = function()
37892 _this.xhrOnLoad(_this.xhr);
37895 this.xhr.onerror = function()
37897 _this.xhrOnError(_this.xhr);
37900 var formData = new FormData();
37902 formData.append('returnHTML', 'NO');
37904 formData.append('crop', crop);
37906 if(typeof(file.filename) != 'undefined'){
37907 formData.append('filename', file.filename);
37910 if(typeof(file.mimetype) != 'undefined'){
37911 formData.append('mimetype', file.mimetype);
37916 if(this.fireEvent('prepare', this, formData) != false){
37917 this.xhr.send(formData);
37927 * @class Roo.bootstrap.DocumentViewer
37928 * @extends Roo.bootstrap.Component
37929 * Bootstrap DocumentViewer class
37930 * @cfg {Boolean} showDownload (true|false) show download button (default true)
37931 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
37934 * Create a new DocumentViewer
37935 * @param {Object} config The config object
37938 Roo.bootstrap.DocumentViewer = function(config){
37939 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
37944 * Fire after initEvent
37945 * @param {Roo.bootstrap.DocumentViewer} this
37951 * @param {Roo.bootstrap.DocumentViewer} this
37956 * Fire after download button
37957 * @param {Roo.bootstrap.DocumentViewer} this
37962 * Fire after trash button
37963 * @param {Roo.bootstrap.DocumentViewer} this
37970 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
37972 showDownload : true,
37976 getAutoCreate : function()
37980 cls : 'roo-document-viewer',
37984 cls : 'roo-document-viewer-body',
37988 cls : 'roo-document-viewer-thumb',
37992 cls : 'roo-document-viewer-image'
38000 cls : 'roo-document-viewer-footer',
38003 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
38007 cls : 'btn-group roo-document-viewer-download',
38011 cls : 'btn btn-default',
38012 html : '<i class="fa fa-download"></i>'
38018 cls : 'btn-group roo-document-viewer-trash',
38022 cls : 'btn btn-default',
38023 html : '<i class="fa fa-trash"></i>'
38036 initEvents : function()
38038 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
38039 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
38041 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
38042 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
38044 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
38045 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
38047 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
38048 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
38050 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
38051 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
38053 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
38054 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
38056 this.bodyEl.on('click', this.onClick, this);
38057 this.downloadBtn.on('click', this.onDownload, this);
38058 this.trashBtn.on('click', this.onTrash, this);
38060 this.downloadBtn.hide();
38061 this.trashBtn.hide();
38063 if(this.showDownload){
38064 this.downloadBtn.show();
38067 if(this.showTrash){
38068 this.trashBtn.show();
38071 if(!this.showDownload && !this.showTrash) {
38072 this.footerEl.hide();
38077 initial : function()
38079 this.fireEvent('initial', this);
38083 onClick : function(e)
38085 e.preventDefault();
38087 this.fireEvent('click', this);
38090 onDownload : function(e)
38092 e.preventDefault();
38094 this.fireEvent('download', this);
38097 onTrash : function(e)
38099 e.preventDefault();
38101 this.fireEvent('trash', this);
38113 * @class Roo.bootstrap.form.FieldLabel
38114 * @extends Roo.bootstrap.Component
38115 * Bootstrap FieldLabel class
38116 * @cfg {String} html contents of the element
38117 * @cfg {String} tag tag of the element default label
38118 * @cfg {String} cls class of the element
38119 * @cfg {String} target label target
38120 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
38121 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
38122 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
38123 * @cfg {String} iconTooltip default "This field is required"
38124 * @cfg {String} indicatorpos (left|right) default left
38127 * Create a new FieldLabel
38128 * @param {Object} config The config object
38131 Roo.bootstrap.form.FieldLabel = function(config){
38132 Roo.bootstrap.Element.superclass.constructor.call(this, config);
38137 * Fires after the field has been marked as invalid.
38138 * @param {Roo.form.FieldLabel} this
38139 * @param {String} msg The validation message
38144 * Fires after the field has been validated with no errors.
38145 * @param {Roo.form.FieldLabel} this
38151 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
38158 invalidClass : 'has-warning',
38159 validClass : 'has-success',
38160 iconTooltip : 'This field is required',
38161 indicatorpos : 'left',
38163 getAutoCreate : function(){
38166 if (!this.allowBlank) {
38172 cls : 'roo-bootstrap-field-label ' + this.cls,
38177 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
38178 tooltip : this.iconTooltip
38187 if(this.indicatorpos == 'right'){
38190 cls : 'roo-bootstrap-field-label ' + this.cls,
38199 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
38200 tooltip : this.iconTooltip
38209 initEvents: function()
38211 Roo.bootstrap.Element.superclass.initEvents.call(this);
38213 this.indicator = this.indicatorEl();
38215 if(this.indicator){
38216 this.indicator.removeClass('visible');
38217 this.indicator.addClass('invisible');
38220 Roo.bootstrap.form.FieldLabel.register(this);
38223 indicatorEl : function()
38225 var indicator = this.el.select('i.roo-required-indicator',true).first();
38236 * Mark this field as valid
38238 markValid : function()
38240 if(this.indicator){
38241 this.indicator.removeClass('visible');
38242 this.indicator.addClass('invisible');
38244 if (Roo.bootstrap.version == 3) {
38245 this.el.removeClass(this.invalidClass);
38246 this.el.addClass(this.validClass);
38248 this.el.removeClass('is-invalid');
38249 this.el.addClass('is-valid');
38253 this.fireEvent('valid', this);
38257 * Mark this field as invalid
38258 * @param {String} msg The validation message
38260 markInvalid : function(msg)
38262 if(this.indicator){
38263 this.indicator.removeClass('invisible');
38264 this.indicator.addClass('visible');
38266 if (Roo.bootstrap.version == 3) {
38267 this.el.removeClass(this.validClass);
38268 this.el.addClass(this.invalidClass);
38270 this.el.removeClass('is-valid');
38271 this.el.addClass('is-invalid');
38275 this.fireEvent('invalid', this, msg);
38281 Roo.apply(Roo.bootstrap.form.FieldLabel, {
38286 * register a FieldLabel Group
38287 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
38289 register : function(label)
38291 if(this.groups.hasOwnProperty(label.target)){
38295 this.groups[label.target] = label;
38299 * fetch a FieldLabel Group based on the target
38300 * @param {string} target
38301 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
38303 get: function(target) {
38304 if (typeof(this.groups[target]) == 'undefined') {
38308 return this.groups[target] ;
38317 * page DateSplitField.
38323 * @class Roo.bootstrap.form.DateSplitField
38324 * @extends Roo.bootstrap.Component
38325 * Bootstrap DateSplitField class
38326 * @cfg {string} fieldLabel - the label associated
38327 * @cfg {Number} labelWidth set the width of label (0-12)
38328 * @cfg {String} labelAlign (top|left)
38329 * @cfg {Boolean} dayAllowBlank (true|false) default false
38330 * @cfg {Boolean} monthAllowBlank (true|false) default false
38331 * @cfg {Boolean} yearAllowBlank (true|false) default false
38332 * @cfg {string} dayPlaceholder
38333 * @cfg {string} monthPlaceholder
38334 * @cfg {string} yearPlaceholder
38335 * @cfg {string} dayFormat default 'd'
38336 * @cfg {string} monthFormat default 'm'
38337 * @cfg {string} yearFormat default 'Y'
38338 * @cfg {Number} labellg set the width of label (1-12)
38339 * @cfg {Number} labelmd set the width of label (1-12)
38340 * @cfg {Number} labelsm set the width of label (1-12)
38341 * @cfg {Number} labelxs set the width of label (1-12)
38345 * Create a new DateSplitField
38346 * @param {Object} config The config object
38349 Roo.bootstrap.form.DateSplitField = function(config){
38350 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
38356 * getting the data of years
38357 * @param {Roo.bootstrap.form.DateSplitField} this
38358 * @param {Object} years
38363 * getting the data of days
38364 * @param {Roo.bootstrap.form.DateSplitField} this
38365 * @param {Object} days
38370 * Fires after the field has been marked as invalid.
38371 * @param {Roo.form.Field} this
38372 * @param {String} msg The validation message
38377 * Fires after the field has been validated with no errors.
38378 * @param {Roo.form.Field} this
38384 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
38387 labelAlign : 'top',
38389 dayAllowBlank : false,
38390 monthAllowBlank : false,
38391 yearAllowBlank : false,
38392 dayPlaceholder : '',
38393 monthPlaceholder : '',
38394 yearPlaceholder : '',
38398 isFormField : true,
38404 getAutoCreate : function()
38408 cls : 'row roo-date-split-field-group',
38413 cls : 'form-hidden-field roo-date-split-field-group-value',
38419 var labelCls = 'col-md-12';
38420 var contentCls = 'col-md-4';
38422 if(this.fieldLabel){
38426 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
38430 html : this.fieldLabel
38435 if(this.labelAlign == 'left'){
38437 if(this.labelWidth > 12){
38438 label.style = "width: " + this.labelWidth + 'px';
38441 if(this.labelWidth < 13 && this.labelmd == 0){
38442 this.labelmd = this.labelWidth;
38445 if(this.labellg > 0){
38446 labelCls = ' col-lg-' + this.labellg;
38447 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
38450 if(this.labelmd > 0){
38451 labelCls = ' col-md-' + this.labelmd;
38452 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
38455 if(this.labelsm > 0){
38456 labelCls = ' col-sm-' + this.labelsm;
38457 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
38460 if(this.labelxs > 0){
38461 labelCls = ' col-xs-' + this.labelxs;
38462 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
38466 label.cls += ' ' + labelCls;
38468 cfg.cn.push(label);
38471 Roo.each(['day', 'month', 'year'], function(t){
38474 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
38481 inputEl: function ()
38483 return this.el.select('.roo-date-split-field-group-value', true).first();
38486 onRender : function(ct, position)
38490 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
38492 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
38494 this.dayField = new Roo.bootstrap.form.ComboBox({
38495 allowBlank : this.dayAllowBlank,
38496 alwaysQuery : true,
38497 displayField : 'value',
38500 forceSelection : true,
38502 placeholder : this.dayPlaceholder,
38503 selectOnFocus : true,
38504 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38505 triggerAction : 'all',
38507 valueField : 'value',
38508 store : new Roo.data.SimpleStore({
38509 data : (function() {
38511 _this.fireEvent('days', _this, days);
38514 fields : [ 'value' ]
38517 select : function (_self, record, index)
38519 _this.setValue(_this.getValue());
38524 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
38526 this.monthField = new Roo.bootstrap.form.MonthField({
38527 after : '<i class=\"fa fa-calendar\"></i>',
38528 allowBlank : this.monthAllowBlank,
38529 placeholder : this.monthPlaceholder,
38532 render : function (_self)
38534 this.el.select('span.input-group-addon', true).first().on('click', function(e){
38535 e.preventDefault();
38539 select : function (_self, oldvalue, newvalue)
38541 _this.setValue(_this.getValue());
38546 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
38548 this.yearField = new Roo.bootstrap.form.ComboBox({
38549 allowBlank : this.yearAllowBlank,
38550 alwaysQuery : true,
38551 displayField : 'value',
38554 forceSelection : true,
38556 placeholder : this.yearPlaceholder,
38557 selectOnFocus : true,
38558 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
38559 triggerAction : 'all',
38561 valueField : 'value',
38562 store : new Roo.data.SimpleStore({
38563 data : (function() {
38565 _this.fireEvent('years', _this, years);
38568 fields : [ 'value' ]
38571 select : function (_self, record, index)
38573 _this.setValue(_this.getValue());
38578 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
38581 setValue : function(v, format)
38583 this.inputEl.dom.value = v;
38585 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
38587 var d = Date.parseDate(v, f);
38594 this.setDay(d.format(this.dayFormat));
38595 this.setMonth(d.format(this.monthFormat));
38596 this.setYear(d.format(this.yearFormat));
38603 setDay : function(v)
38605 this.dayField.setValue(v);
38606 this.inputEl.dom.value = this.getValue();
38611 setMonth : function(v)
38613 this.monthField.setValue(v, true);
38614 this.inputEl.dom.value = this.getValue();
38619 setYear : function(v)
38621 this.yearField.setValue(v);
38622 this.inputEl.dom.value = this.getValue();
38627 getDay : function()
38629 return this.dayField.getValue();
38632 getMonth : function()
38634 return this.monthField.getValue();
38637 getYear : function()
38639 return this.yearField.getValue();
38642 getValue : function()
38644 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
38646 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
38656 this.inputEl.dom.value = '';
38661 validate : function()
38663 var d = this.dayField.validate();
38664 var m = this.monthField.validate();
38665 var y = this.yearField.validate();
38670 (!this.dayAllowBlank && !d) ||
38671 (!this.monthAllowBlank && !m) ||
38672 (!this.yearAllowBlank && !y)
38677 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
38686 this.markInvalid();
38691 markValid : function()
38694 var label = this.el.select('label', true).first();
38695 var icon = this.el.select('i.fa-star', true).first();
38701 this.fireEvent('valid', this);
38705 * Mark this field as invalid
38706 * @param {String} msg The validation message
38708 markInvalid : function(msg)
38711 var label = this.el.select('label', true).first();
38712 var icon = this.el.select('i.fa-star', true).first();
38714 if(label && !icon){
38715 this.el.select('.roo-date-split-field-label', true).createChild({
38717 cls : 'text-danger fa fa-lg fa-star',
38718 tooltip : 'This field is required',
38719 style : 'margin-right:5px;'
38723 this.fireEvent('invalid', this, msg);
38726 clearInvalid : function()
38728 var label = this.el.select('label', true).first();
38729 var icon = this.el.select('i.fa-star', true).first();
38735 this.fireEvent('valid', this);
38738 getName: function()
38748 * @class Roo.bootstrap.LayoutMasonry
38749 * @extends Roo.bootstrap.Component
38750 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
38751 * Bootstrap Layout Masonry class
38754 * http://masonry.desandro.com
38756 * The idea is to render all the bricks based on vertical width...
38758 * The original code extends 'outlayer' - we might need to use that....
38761 * Create a new Element
38762 * @param {Object} config The config object
38765 Roo.bootstrap.LayoutMasonry = function(config){
38767 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
38771 Roo.bootstrap.LayoutMasonry.register(this);
38777 * Fire after layout the items
38778 * @param {Roo.bootstrap.LayoutMasonry} this
38779 * @param {Roo.EventObject} e
38786 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
38789 * @cfg {Boolean} isLayoutInstant = no animation?
38791 isLayoutInstant : false, // needed?
38794 * @cfg {Number} boxWidth width of the columns
38799 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
38804 * @cfg {Number} padWidth padding below box..
38809 * @cfg {Number} gutter gutter width..
38814 * @cfg {Number} maxCols maximum number of columns
38820 * @cfg {Boolean} isAutoInitial defalut true
38822 isAutoInitial : true,
38827 * @cfg {Boolean} isHorizontal defalut false
38829 isHorizontal : false,
38831 currentSize : null,
38837 bricks: null, //CompositeElement
38841 _isLayoutInited : false,
38843 // isAlternative : false, // only use for vertical layout...
38846 * @cfg {Number} alternativePadWidth padding below box..
38848 alternativePadWidth : 50,
38850 selectedBrick : [],
38852 getAutoCreate : function(){
38854 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
38858 cls: 'blog-masonary-wrapper ' + this.cls,
38860 cls : 'mas-boxes masonary'
38867 getChildContainer: function( )
38869 if (this.boxesEl) {
38870 return this.boxesEl;
38873 this.boxesEl = this.el.select('.mas-boxes').first();
38875 return this.boxesEl;
38879 initEvents : function()
38883 if(this.isAutoInitial){
38884 Roo.log('hook children rendered');
38885 this.on('childrenrendered', function() {
38886 Roo.log('children rendered');
38892 initial : function()
38894 this.selectedBrick = [];
38896 this.currentSize = this.el.getBox(true);
38898 Roo.EventManager.onWindowResize(this.resize, this);
38900 if(!this.isAutoInitial){
38908 //this.layout.defer(500,this);
38912 resize : function()
38914 var cs = this.el.getBox(true);
38917 this.currentSize.width == cs.width &&
38918 this.currentSize.x == cs.x &&
38919 this.currentSize.height == cs.height &&
38920 this.currentSize.y == cs.y
38922 Roo.log("no change in with or X or Y");
38926 this.currentSize = cs;
38932 layout : function()
38934 this._resetLayout();
38936 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38938 this.layoutItems( isInstant );
38940 this._isLayoutInited = true;
38942 this.fireEvent('layout', this);
38946 _resetLayout : function()
38948 if(this.isHorizontal){
38949 this.horizontalMeasureColumns();
38953 this.verticalMeasureColumns();
38957 verticalMeasureColumns : function()
38959 this.getContainerWidth();
38961 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
38962 // this.colWidth = Math.floor(this.containerWidth * 0.8);
38966 var boxWidth = this.boxWidth + this.padWidth;
38968 if(this.containerWidth < this.boxWidth){
38969 boxWidth = this.containerWidth
38972 var containerWidth = this.containerWidth;
38974 var cols = Math.floor(containerWidth / boxWidth);
38976 this.cols = Math.max( cols, 1 );
38978 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38980 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
38982 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
38984 this.colWidth = boxWidth + avail - this.padWidth;
38986 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
38987 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
38990 horizontalMeasureColumns : function()
38992 this.getContainerWidth();
38994 var boxWidth = this.boxWidth;
38996 if(this.containerWidth < boxWidth){
38997 boxWidth = this.containerWidth;
39000 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
39002 this.el.setHeight(boxWidth);
39006 getContainerWidth : function()
39008 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
39011 layoutItems : function( isInstant )
39013 Roo.log(this.bricks);
39015 var items = Roo.apply([], this.bricks);
39017 if(this.isHorizontal){
39018 this._horizontalLayoutItems( items , isInstant );
39022 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
39023 // this._verticalAlternativeLayoutItems( items , isInstant );
39027 this._verticalLayoutItems( items , isInstant );
39031 _verticalLayoutItems : function ( items , isInstant)
39033 if ( !items || !items.length ) {
39038 ['xs', 'xs', 'xs', 'tall'],
39039 ['xs', 'xs', 'tall'],
39040 ['xs', 'xs', 'sm'],
39041 ['xs', 'xs', 'xs'],
39047 ['sm', 'xs', 'xs'],
39051 ['tall', 'xs', 'xs', 'xs'],
39052 ['tall', 'xs', 'xs'],
39064 Roo.each(items, function(item, k){
39066 switch (item.size) {
39067 // these layouts take up a full box,
39078 boxes.push([item]);
39101 var filterPattern = function(box, length)
39109 var pattern = box.slice(0, length);
39113 Roo.each(pattern, function(i){
39114 format.push(i.size);
39117 Roo.each(standard, function(s){
39119 if(String(s) != String(format)){
39128 if(!match && length == 1){
39133 filterPattern(box, length - 1);
39137 queue.push(pattern);
39139 box = box.slice(length, box.length);
39141 filterPattern(box, 4);
39147 Roo.each(boxes, function(box, k){
39153 if(box.length == 1){
39158 filterPattern(box, 4);
39162 this._processVerticalLayoutQueue( queue, isInstant );
39166 // _verticalAlternativeLayoutItems : function( items , isInstant )
39168 // if ( !items || !items.length ) {
39172 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
39176 _horizontalLayoutItems : function ( items , isInstant)
39178 if ( !items || !items.length || items.length < 3) {
39184 var eItems = items.slice(0, 3);
39186 items = items.slice(3, items.length);
39189 ['xs', 'xs', 'xs', 'wide'],
39190 ['xs', 'xs', 'wide'],
39191 ['xs', 'xs', 'sm'],
39192 ['xs', 'xs', 'xs'],
39198 ['sm', 'xs', 'xs'],
39202 ['wide', 'xs', 'xs', 'xs'],
39203 ['wide', 'xs', 'xs'],
39216 Roo.each(items, function(item, k){
39218 switch (item.size) {
39229 boxes.push([item]);
39253 var filterPattern = function(box, length)
39261 var pattern = box.slice(0, length);
39265 Roo.each(pattern, function(i){
39266 format.push(i.size);
39269 Roo.each(standard, function(s){
39271 if(String(s) != String(format)){
39280 if(!match && length == 1){
39285 filterPattern(box, length - 1);
39289 queue.push(pattern);
39291 box = box.slice(length, box.length);
39293 filterPattern(box, 4);
39299 Roo.each(boxes, function(box, k){
39305 if(box.length == 1){
39310 filterPattern(box, 4);
39317 var pos = this.el.getBox(true);
39321 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39323 var hit_end = false;
39325 Roo.each(queue, function(box){
39329 Roo.each(box, function(b){
39331 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39341 Roo.each(box, function(b){
39343 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39346 mx = Math.max(mx, b.x);
39350 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
39354 Roo.each(box, function(b){
39356 b.el.setVisibilityMode(Roo.Element.DISPLAY);
39370 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
39373 /** Sets position of item in DOM
39374 * @param {Element} item
39375 * @param {Number} x - horizontal position
39376 * @param {Number} y - vertical position
39377 * @param {Boolean} isInstant - disables transitions
39379 _processVerticalLayoutQueue : function( queue, isInstant )
39381 var pos = this.el.getBox(true);
39386 for (var i = 0; i < this.cols; i++){
39390 Roo.each(queue, function(box, k){
39392 var col = k % this.cols;
39394 Roo.each(box, function(b,kk){
39396 b.el.position('absolute');
39398 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39399 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39401 if(b.size == 'md-left' || b.size == 'md-right'){
39402 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39403 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39406 b.el.setWidth(width);
39407 b.el.setHeight(height);
39409 b.el.select('iframe',true).setSize(width,height);
39413 for (var i = 0; i < this.cols; i++){
39415 if(maxY[i] < maxY[col]){
39420 col = Math.min(col, i);
39424 x = pos.x + col * (this.colWidth + this.padWidth);
39428 var positions = [];
39430 switch (box.length){
39432 positions = this.getVerticalOneBoxColPositions(x, y, box);
39435 positions = this.getVerticalTwoBoxColPositions(x, y, box);
39438 positions = this.getVerticalThreeBoxColPositions(x, y, box);
39441 positions = this.getVerticalFourBoxColPositions(x, y, box);
39447 Roo.each(box, function(b,kk){
39449 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39451 var sz = b.el.getSize();
39453 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
39461 for (var i = 0; i < this.cols; i++){
39462 mY = Math.max(mY, maxY[i]);
39465 this.el.setHeight(mY - pos.y);
39469 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
39471 // var pos = this.el.getBox(true);
39474 // var maxX = pos.right;
39476 // var maxHeight = 0;
39478 // Roo.each(items, function(item, k){
39482 // item.el.position('absolute');
39484 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
39486 // item.el.setWidth(width);
39488 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
39490 // item.el.setHeight(height);
39493 // item.el.setXY([x, y], isInstant ? false : true);
39495 // item.el.setXY([maxX - width, y], isInstant ? false : true);
39498 // y = y + height + this.alternativePadWidth;
39500 // maxHeight = maxHeight + height + this.alternativePadWidth;
39504 // this.el.setHeight(maxHeight);
39508 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
39510 var pos = this.el.getBox(true);
39515 var maxX = pos.right;
39517 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
39519 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
39521 Roo.each(queue, function(box, k){
39523 Roo.each(box, function(b, kk){
39525 b.el.position('absolute');
39527 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39528 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39530 if(b.size == 'md-left' || b.size == 'md-right'){
39531 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
39532 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
39535 b.el.setWidth(width);
39536 b.el.setHeight(height);
39544 var positions = [];
39546 switch (box.length){
39548 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
39551 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
39554 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
39557 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
39563 Roo.each(box, function(b,kk){
39565 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
39567 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
39575 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
39577 Roo.each(eItems, function(b,k){
39579 b.size = (k == 0) ? 'sm' : 'xs';
39580 b.x = (k == 0) ? 2 : 1;
39581 b.y = (k == 0) ? 2 : 1;
39583 b.el.position('absolute');
39585 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
39587 b.el.setWidth(width);
39589 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
39591 b.el.setHeight(height);
39595 var positions = [];
39598 x : maxX - this.unitWidth * 2 - this.gutter,
39603 x : maxX - this.unitWidth,
39604 y : minY + (this.unitWidth + this.gutter) * 2
39608 x : maxX - this.unitWidth * 3 - this.gutter * 2,
39612 Roo.each(eItems, function(b,k){
39614 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
39620 getVerticalOneBoxColPositions : function(x, y, box)
39624 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
39626 if(box[0].size == 'md-left'){
39630 if(box[0].size == 'md-right'){
39635 x : x + (this.unitWidth + this.gutter) * rand,
39642 getVerticalTwoBoxColPositions : function(x, y, box)
39646 if(box[0].size == 'xs'){
39650 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
39654 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
39668 x : x + (this.unitWidth + this.gutter) * 2,
39669 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
39676 getVerticalThreeBoxColPositions : function(x, y, box)
39680 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39688 x : x + (this.unitWidth + this.gutter) * 1,
39693 x : x + (this.unitWidth + this.gutter) * 2,
39701 if(box[0].size == 'xs' && box[1].size == 'xs'){
39710 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
39714 x : x + (this.unitWidth + this.gutter) * 1,
39728 x : x + (this.unitWidth + this.gutter) * 2,
39733 x : x + (this.unitWidth + this.gutter) * 2,
39734 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
39741 getVerticalFourBoxColPositions : function(x, y, box)
39745 if(box[0].size == 'xs'){
39754 y : y + (this.unitHeight + this.gutter) * 1
39759 y : y + (this.unitHeight + this.gutter) * 2
39763 x : x + (this.unitWidth + this.gutter) * 1,
39777 x : x + (this.unitWidth + this.gutter) * 2,
39782 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
39783 y : y + (this.unitHeight + this.gutter) * 1
39787 x : x + (this.unitWidth + this.gutter) * 2,
39788 y : y + (this.unitWidth + this.gutter) * 2
39795 getHorizontalOneBoxColPositions : function(maxX, minY, box)
39799 if(box[0].size == 'md-left'){
39801 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39808 if(box[0].size == 'md-right'){
39810 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
39811 y : minY + (this.unitWidth + this.gutter) * 1
39817 var rand = Math.floor(Math.random() * (4 - box[0].y));
39820 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39821 y : minY + (this.unitWidth + this.gutter) * rand
39828 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
39832 if(box[0].size == 'xs'){
39835 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39840 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39841 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
39849 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39854 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39855 y : minY + (this.unitWidth + this.gutter) * 2
39862 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
39866 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
39869 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39874 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39875 y : minY + (this.unitWidth + this.gutter) * 1
39879 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39880 y : minY + (this.unitWidth + this.gutter) * 2
39887 if(box[0].size == 'xs' && box[1].size == 'xs'){
39890 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39895 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39900 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39901 y : minY + (this.unitWidth + this.gutter) * 1
39909 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39914 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39915 y : minY + (this.unitWidth + this.gutter) * 2
39919 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39920 y : minY + (this.unitWidth + this.gutter) * 2
39927 getHorizontalFourBoxColPositions : function(maxX, minY, box)
39931 if(box[0].size == 'xs'){
39934 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39939 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39944 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),
39949 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
39950 y : minY + (this.unitWidth + this.gutter) * 1
39958 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
39963 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
39964 y : minY + (this.unitWidth + this.gutter) * 2
39968 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
39969 y : minY + (this.unitWidth + this.gutter) * 2
39973 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),
39974 y : minY + (this.unitWidth + this.gutter) * 2
39982 * remove a Masonry Brick
39983 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
39985 removeBrick : function(brick_id)
39991 for (var i = 0; i<this.bricks.length; i++) {
39992 if (this.bricks[i].id == brick_id) {
39993 this.bricks.splice(i,1);
39994 this.el.dom.removeChild(Roo.get(brick_id).dom);
40001 * adds a Masonry Brick
40002 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40004 addBrick : function(cfg)
40006 var cn = new Roo.bootstrap.MasonryBrick(cfg);
40007 //this.register(cn);
40008 cn.parentId = this.id;
40009 cn.render(this.el);
40014 * register a Masonry Brick
40015 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
40018 register : function(brick)
40020 this.bricks.push(brick);
40021 brick.masonryId = this.id;
40025 * clear all the Masonry Brick
40027 clearAll : function()
40030 //this.getChildContainer().dom.innerHTML = "";
40031 this.el.dom.innerHTML = '';
40034 getSelected : function()
40036 if (!this.selectedBrick) {
40040 return this.selectedBrick;
40044 Roo.apply(Roo.bootstrap.LayoutMasonry, {
40048 * register a Masonry Layout
40049 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
40052 register : function(layout)
40054 this.groups[layout.id] = layout;
40057 * fetch a Masonry Layout based on the masonry layout ID
40058 * @param {string} the masonry layout to add
40059 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
40062 get: function(layout_id) {
40063 if (typeof(this.groups[layout_id]) == 'undefined') {
40066 return this.groups[layout_id] ;
40078 * http://masonry.desandro.com
40080 * The idea is to render all the bricks based on vertical width...
40082 * The original code extends 'outlayer' - we might need to use that....
40088 * @class Roo.bootstrap.LayoutMasonryAuto
40089 * @extends Roo.bootstrap.Component
40090 * Bootstrap Layout Masonry class
40093 * Create a new Element
40094 * @param {Object} config The config object
40097 Roo.bootstrap.LayoutMasonryAuto = function(config){
40098 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
40101 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
40104 * @cfg {Boolean} isFitWidth - resize the width..
40106 isFitWidth : false, // options..
40108 * @cfg {Boolean} isOriginLeft = left align?
40110 isOriginLeft : true,
40112 * @cfg {Boolean} isOriginTop = top align?
40114 isOriginTop : false,
40116 * @cfg {Boolean} isLayoutInstant = no animation?
40118 isLayoutInstant : false, // needed?
40120 * @cfg {Boolean} isResizingContainer = not sure if this is used..
40122 isResizingContainer : true,
40124 * @cfg {Number} columnWidth width of the columns
40130 * @cfg {Number} maxCols maximum number of columns
40135 * @cfg {Number} padHeight padding below box..
40141 * @cfg {Boolean} isAutoInitial defalut true
40144 isAutoInitial : true,
40150 initialColumnWidth : 0,
40151 currentSize : null,
40153 colYs : null, // array.
40160 bricks: null, //CompositeElement
40161 cols : 0, // array?
40162 // element : null, // wrapped now this.el
40163 _isLayoutInited : null,
40166 getAutoCreate : function(){
40170 cls: 'blog-masonary-wrapper ' + this.cls,
40172 cls : 'mas-boxes masonary'
40179 getChildContainer: function( )
40181 if (this.boxesEl) {
40182 return this.boxesEl;
40185 this.boxesEl = this.el.select('.mas-boxes').first();
40187 return this.boxesEl;
40191 initEvents : function()
40195 if(this.isAutoInitial){
40196 Roo.log('hook children rendered');
40197 this.on('childrenrendered', function() {
40198 Roo.log('children rendered');
40205 initial : function()
40207 this.reloadItems();
40209 this.currentSize = this.el.getBox(true);
40211 /// was window resize... - let's see if this works..
40212 Roo.EventManager.onWindowResize(this.resize, this);
40214 if(!this.isAutoInitial){
40219 this.layout.defer(500,this);
40222 reloadItems: function()
40224 this.bricks = this.el.select('.masonry-brick', true);
40226 this.bricks.each(function(b) {
40227 //Roo.log(b.getSize());
40228 if (!b.attr('originalwidth')) {
40229 b.attr('originalwidth', b.getSize().width);
40234 Roo.log(this.bricks.elements.length);
40237 resize : function()
40240 var cs = this.el.getBox(true);
40242 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
40243 Roo.log("no change in with or X");
40246 this.currentSize = cs;
40250 layout : function()
40253 this._resetLayout();
40254 //this._manageStamps();
40256 // don't animate first layout
40257 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
40258 this.layoutItems( isInstant );
40260 // flag for initalized
40261 this._isLayoutInited = true;
40264 layoutItems : function( isInstant )
40266 //var items = this._getItemsForLayout( this.items );
40267 // original code supports filtering layout items.. we just ignore it..
40269 this._layoutItems( this.bricks , isInstant );
40271 this._postLayout();
40273 _layoutItems : function ( items , isInstant)
40275 //this.fireEvent( 'layout', this, items );
40278 if ( !items || !items.elements.length ) {
40279 // no items, emit event with empty array
40284 items.each(function(item) {
40285 Roo.log("layout item");
40287 // get x/y object from method
40288 var position = this._getItemLayoutPosition( item );
40290 position.item = item;
40291 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
40292 queue.push( position );
40295 this._processLayoutQueue( queue );
40297 /** Sets position of item in DOM
40298 * @param {Element} item
40299 * @param {Number} x - horizontal position
40300 * @param {Number} y - vertical position
40301 * @param {Boolean} isInstant - disables transitions
40303 _processLayoutQueue : function( queue )
40305 for ( var i=0, len = queue.length; i < len; i++ ) {
40306 var obj = queue[i];
40307 obj.item.position('absolute');
40308 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
40314 * Any logic you want to do after each layout,
40315 * i.e. size the container
40317 _postLayout : function()
40319 this.resizeContainer();
40322 resizeContainer : function()
40324 if ( !this.isResizingContainer ) {
40327 var size = this._getContainerSize();
40329 this.el.setSize(size.width,size.height);
40330 this.boxesEl.setSize(size.width,size.height);
40336 _resetLayout : function()
40338 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
40339 this.colWidth = this.el.getWidth();
40340 //this.gutter = this.el.getWidth();
40342 this.measureColumns();
40348 this.colYs.push( 0 );
40354 measureColumns : function()
40356 this.getContainerWidth();
40357 // if columnWidth is 0, default to outerWidth of first item
40358 if ( !this.columnWidth ) {
40359 var firstItem = this.bricks.first();
40360 Roo.log(firstItem);
40361 this.columnWidth = this.containerWidth;
40362 if (firstItem && firstItem.attr('originalwidth') ) {
40363 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
40365 // columnWidth fall back to item of first element
40366 Roo.log("set column width?");
40367 this.initialColumnWidth = this.columnWidth ;
40369 // if first elem has no width, default to size of container
40374 if (this.initialColumnWidth) {
40375 this.columnWidth = this.initialColumnWidth;
40380 // column width is fixed at the top - however if container width get's smaller we should
40383 // this bit calcs how man columns..
40385 var columnWidth = this.columnWidth += this.gutter;
40387 // calculate columns
40388 var containerWidth = this.containerWidth + this.gutter;
40390 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
40391 // fix rounding errors, typically with gutters
40392 var excess = columnWidth - containerWidth % columnWidth;
40395 // if overshoot is less than a pixel, round up, otherwise floor it
40396 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
40397 cols = Math[ mathMethod ]( cols );
40398 this.cols = Math.max( cols, 1 );
40399 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
40401 // padding positioning..
40402 var totalColWidth = this.cols * this.columnWidth;
40403 var padavail = this.containerWidth - totalColWidth;
40404 // so for 2 columns - we need 3 'pads'
40406 var padNeeded = (1+this.cols) * this.padWidth;
40408 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
40410 this.columnWidth += padExtra
40411 //this.padWidth = Math.floor(padavail / ( this.cols));
40413 // adjust colum width so that padding is fixed??
40415 // we have 3 columns ... total = width * 3
40416 // we have X left over... that should be used by
40418 //if (this.expandC) {
40426 getContainerWidth : function()
40428 /* // container is parent if fit width
40429 var container = this.isFitWidth ? this.element.parentNode : this.element;
40430 // check that this.size and size are there
40431 // IE8 triggers resize on body size change, so they might not be
40433 var size = getSize( container ); //FIXME
40434 this.containerWidth = size && size.innerWidth; //FIXME
40437 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
40441 _getItemLayoutPosition : function( item ) // what is item?
40443 // we resize the item to our columnWidth..
40445 item.setWidth(this.columnWidth);
40446 item.autoBoxAdjust = false;
40448 var sz = item.getSize();
40450 // how many columns does this brick span
40451 var remainder = this.containerWidth % this.columnWidth;
40453 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
40454 // round if off by 1 pixel, otherwise use ceil
40455 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
40456 colSpan = Math.min( colSpan, this.cols );
40458 // normally this should be '1' as we dont' currently allow multi width columns..
40460 var colGroup = this._getColGroup( colSpan );
40461 // get the minimum Y value from the columns
40462 var minimumY = Math.min.apply( Math, colGroup );
40463 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40465 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
40467 // position the brick
40469 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
40470 y: this.currentSize.y + minimumY + this.padHeight
40474 // apply setHeight to necessary columns
40475 var setHeight = minimumY + sz.height + this.padHeight;
40476 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
40478 var setSpan = this.cols + 1 - colGroup.length;
40479 for ( var i = 0; i < setSpan; i++ ) {
40480 this.colYs[ shortColIndex + i ] = setHeight ;
40487 * @param {Number} colSpan - number of columns the element spans
40488 * @returns {Array} colGroup
40490 _getColGroup : function( colSpan )
40492 if ( colSpan < 2 ) {
40493 // if brick spans only one column, use all the column Ys
40498 // how many different places could this brick fit horizontally
40499 var groupCount = this.cols + 1 - colSpan;
40500 // for each group potential horizontal position
40501 for ( var i = 0; i < groupCount; i++ ) {
40502 // make an array of colY values for that one group
40503 var groupColYs = this.colYs.slice( i, i + colSpan );
40504 // and get the max value of the array
40505 colGroup[i] = Math.max.apply( Math, groupColYs );
40510 _manageStamp : function( stamp )
40512 var stampSize = stamp.getSize();
40513 var offset = stamp.getBox();
40514 // get the columns that this stamp affects
40515 var firstX = this.isOriginLeft ? offset.x : offset.right;
40516 var lastX = firstX + stampSize.width;
40517 var firstCol = Math.floor( firstX / this.columnWidth );
40518 firstCol = Math.max( 0, firstCol );
40520 var lastCol = Math.floor( lastX / this.columnWidth );
40521 // lastCol should not go over if multiple of columnWidth #425
40522 lastCol -= lastX % this.columnWidth ? 0 : 1;
40523 lastCol = Math.min( this.cols - 1, lastCol );
40525 // set colYs to bottom of the stamp
40526 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
40529 for ( var i = firstCol; i <= lastCol; i++ ) {
40530 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
40535 _getContainerSize : function()
40537 this.maxY = Math.max.apply( Math, this.colYs );
40542 if ( this.isFitWidth ) {
40543 size.width = this._getContainerFitWidth();
40549 _getContainerFitWidth : function()
40551 var unusedCols = 0;
40552 // count unused columns
40555 if ( this.colYs[i] !== 0 ) {
40560 // fit container to columns that have been used
40561 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
40564 needsResizeLayout : function()
40566 var previousWidth = this.containerWidth;
40567 this.getContainerWidth();
40568 return previousWidth !== this.containerWidth;
40583 * @class Roo.bootstrap.MasonryBrick
40584 * @extends Roo.bootstrap.Component
40585 * Bootstrap MasonryBrick class
40588 * Create a new MasonryBrick
40589 * @param {Object} config The config object
40592 Roo.bootstrap.MasonryBrick = function(config){
40594 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
40596 Roo.bootstrap.MasonryBrick.register(this);
40602 * When a MasonryBrick is clcik
40603 * @param {Roo.bootstrap.MasonryBrick} this
40604 * @param {Roo.EventObject} e
40610 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
40613 * @cfg {String} title
40617 * @cfg {String} html
40621 * @cfg {String} bgimage
40625 * @cfg {String} videourl
40629 * @cfg {String} cls
40633 * @cfg {String} href
40637 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
40642 * @cfg {String} placetitle (center|bottom)
40647 * @cfg {Boolean} isFitContainer defalut true
40649 isFitContainer : true,
40652 * @cfg {Boolean} preventDefault defalut false
40654 preventDefault : false,
40657 * @cfg {Boolean} inverse defalut false
40659 maskInverse : false,
40661 getAutoCreate : function()
40663 if(!this.isFitContainer){
40664 return this.getSplitAutoCreate();
40667 var cls = 'masonry-brick masonry-brick-full';
40669 if(this.href.length){
40670 cls += ' masonry-brick-link';
40673 if(this.bgimage.length){
40674 cls += ' masonry-brick-image';
40677 if(this.maskInverse){
40678 cls += ' mask-inverse';
40681 if(!this.html.length && !this.maskInverse && !this.videourl.length){
40682 cls += ' enable-mask';
40686 cls += ' masonry-' + this.size + '-brick';
40689 if(this.placetitle.length){
40691 switch (this.placetitle) {
40693 cls += ' masonry-center-title';
40696 cls += ' masonry-bottom-title';
40703 if(!this.html.length && !this.bgimage.length){
40704 cls += ' masonry-center-title';
40707 if(!this.html.length && this.bgimage.length){
40708 cls += ' masonry-bottom-title';
40713 cls += ' ' + this.cls;
40717 tag: (this.href.length) ? 'a' : 'div',
40722 cls: 'masonry-brick-mask'
40726 cls: 'masonry-brick-paragraph',
40732 if(this.href.length){
40733 cfg.href = this.href;
40736 var cn = cfg.cn[1].cn;
40738 if(this.title.length){
40741 cls: 'masonry-brick-title',
40746 if(this.html.length){
40749 cls: 'masonry-brick-text',
40754 if (!this.title.length && !this.html.length) {
40755 cfg.cn[1].cls += ' hide';
40758 if(this.bgimage.length){
40761 cls: 'masonry-brick-image-view',
40766 if(this.videourl.length){
40767 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40768 // youtube support only?
40771 cls: 'masonry-brick-image-view',
40774 allowfullscreen : true
40782 getSplitAutoCreate : function()
40784 var cls = 'masonry-brick masonry-brick-split';
40786 if(this.href.length){
40787 cls += ' masonry-brick-link';
40790 if(this.bgimage.length){
40791 cls += ' masonry-brick-image';
40795 cls += ' masonry-' + this.size + '-brick';
40798 switch (this.placetitle) {
40800 cls += ' masonry-center-title';
40803 cls += ' masonry-bottom-title';
40806 if(!this.bgimage.length){
40807 cls += ' masonry-center-title';
40810 if(this.bgimage.length){
40811 cls += ' masonry-bottom-title';
40817 cls += ' ' + this.cls;
40821 tag: (this.href.length) ? 'a' : 'div',
40826 cls: 'masonry-brick-split-head',
40830 cls: 'masonry-brick-paragraph',
40837 cls: 'masonry-brick-split-body',
40843 if(this.href.length){
40844 cfg.href = this.href;
40847 if(this.title.length){
40848 cfg.cn[0].cn[0].cn.push({
40850 cls: 'masonry-brick-title',
40855 if(this.html.length){
40856 cfg.cn[1].cn.push({
40858 cls: 'masonry-brick-text',
40863 if(this.bgimage.length){
40864 cfg.cn[0].cn.push({
40866 cls: 'masonry-brick-image-view',
40871 if(this.videourl.length){
40872 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
40873 // youtube support only?
40874 cfg.cn[0].cn.cn.push({
40876 cls: 'masonry-brick-image-view',
40879 allowfullscreen : true
40886 initEvents: function()
40888 switch (this.size) {
40921 this.el.on('touchstart', this.onTouchStart, this);
40922 this.el.on('touchmove', this.onTouchMove, this);
40923 this.el.on('touchend', this.onTouchEnd, this);
40924 this.el.on('contextmenu', this.onContextMenu, this);
40926 this.el.on('mouseenter' ,this.enter, this);
40927 this.el.on('mouseleave', this.leave, this);
40928 this.el.on('click', this.onClick, this);
40931 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
40932 this.parent().bricks.push(this);
40937 onClick: function(e, el)
40939 var time = this.endTimer - this.startTimer;
40940 // Roo.log(e.preventDefault());
40943 e.preventDefault();
40948 if(!this.preventDefault){
40952 e.preventDefault();
40954 if (this.activeClass != '') {
40955 this.selectBrick();
40958 this.fireEvent('click', this, e);
40961 enter: function(e, el)
40963 e.preventDefault();
40965 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40969 if(this.bgimage.length && this.html.length){
40970 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
40974 leave: function(e, el)
40976 e.preventDefault();
40978 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
40982 if(this.bgimage.length && this.html.length){
40983 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
40987 onTouchStart: function(e, el)
40989 // e.preventDefault();
40991 this.touchmoved = false;
40993 if(!this.isFitContainer){
40997 if(!this.bgimage.length || !this.html.length){
41001 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
41003 this.timer = new Date().getTime();
41007 onTouchMove: function(e, el)
41009 this.touchmoved = true;
41012 onContextMenu : function(e,el)
41014 e.preventDefault();
41015 e.stopPropagation();
41019 onTouchEnd: function(e, el)
41021 // e.preventDefault();
41023 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
41030 if(!this.bgimage.length || !this.html.length){
41032 if(this.href.length){
41033 window.location.href = this.href;
41039 if(!this.isFitContainer){
41043 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
41045 window.location.href = this.href;
41048 //selection on single brick only
41049 selectBrick : function() {
41051 if (!this.parentId) {
41055 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
41056 var index = m.selectedBrick.indexOf(this.id);
41059 m.selectedBrick.splice(index,1);
41060 this.el.removeClass(this.activeClass);
41064 for(var i = 0; i < m.selectedBrick.length; i++) {
41065 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
41066 b.el.removeClass(b.activeClass);
41069 m.selectedBrick = [];
41071 m.selectedBrick.push(this.id);
41072 this.el.addClass(this.activeClass);
41076 isSelected : function(){
41077 return this.el.hasClass(this.activeClass);
41082 Roo.apply(Roo.bootstrap.MasonryBrick, {
41085 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
41087 * register a Masonry Brick
41088 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
41091 register : function(brick)
41093 //this.groups[brick.id] = brick;
41094 this.groups.add(brick.id, brick);
41097 * fetch a masonry brick based on the masonry brick ID
41098 * @param {string} the masonry brick to add
41099 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
41102 get: function(brick_id)
41104 // if (typeof(this.groups[brick_id]) == 'undefined') {
41107 // return this.groups[brick_id] ;
41109 if(this.groups.key(brick_id)) {
41110 return this.groups.key(brick_id);
41128 * @class Roo.bootstrap.Brick
41129 * @extends Roo.bootstrap.Component
41130 * Bootstrap Brick class
41133 * Create a new Brick
41134 * @param {Object} config The config object
41137 Roo.bootstrap.Brick = function(config){
41138 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
41144 * When a Brick is click
41145 * @param {Roo.bootstrap.Brick} this
41146 * @param {Roo.EventObject} e
41152 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
41155 * @cfg {String} title
41159 * @cfg {String} html
41163 * @cfg {String} bgimage
41167 * @cfg {String} cls
41171 * @cfg {String} href
41175 * @cfg {String} video
41179 * @cfg {Boolean} square
41183 getAutoCreate : function()
41185 var cls = 'roo-brick';
41187 if(this.href.length){
41188 cls += ' roo-brick-link';
41191 if(this.bgimage.length){
41192 cls += ' roo-brick-image';
41195 if(!this.html.length && !this.bgimage.length){
41196 cls += ' roo-brick-center-title';
41199 if(!this.html.length && this.bgimage.length){
41200 cls += ' roo-brick-bottom-title';
41204 cls += ' ' + this.cls;
41208 tag: (this.href.length) ? 'a' : 'div',
41213 cls: 'roo-brick-paragraph',
41219 if(this.href.length){
41220 cfg.href = this.href;
41223 var cn = cfg.cn[0].cn;
41225 if(this.title.length){
41228 cls: 'roo-brick-title',
41233 if(this.html.length){
41236 cls: 'roo-brick-text',
41243 if(this.bgimage.length){
41246 cls: 'roo-brick-image-view',
41254 initEvents: function()
41256 if(this.title.length || this.html.length){
41257 this.el.on('mouseenter' ,this.enter, this);
41258 this.el.on('mouseleave', this.leave, this);
41261 Roo.EventManager.onWindowResize(this.resize, this);
41263 if(this.bgimage.length){
41264 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
41265 this.imageEl.on('load', this.onImageLoad, this);
41272 onImageLoad : function()
41277 resize : function()
41279 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
41281 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
41283 if(this.bgimage.length){
41284 var image = this.el.select('.roo-brick-image-view', true).first();
41286 image.setWidth(paragraph.getWidth());
41289 image.setHeight(paragraph.getWidth());
41292 this.el.setHeight(image.getHeight());
41293 paragraph.setHeight(image.getHeight());
41299 enter: function(e, el)
41301 e.preventDefault();
41303 if(this.bgimage.length){
41304 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
41305 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
41309 leave: function(e, el)
41311 e.preventDefault();
41313 if(this.bgimage.length){
41314 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
41315 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
41330 * @class Roo.bootstrap.form.NumberField
41331 * @extends Roo.bootstrap.form.Input
41332 * Bootstrap NumberField class
41338 * Create a new NumberField
41339 * @param {Object} config The config object
41342 Roo.bootstrap.form.NumberField = function(config){
41343 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
41346 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
41349 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41351 allowDecimals : true,
41353 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41355 decimalSeparator : ".",
41357 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41359 decimalPrecision : 2,
41361 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41363 allowNegative : true,
41366 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
41370 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41372 minValue : Number.NEGATIVE_INFINITY,
41374 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41376 maxValue : Number.MAX_VALUE,
41378 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41380 minText : "The minimum value for this field is {0}",
41382 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41384 maxText : "The maximum value for this field is {0}",
41386 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
41387 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41389 nanText : "{0} is not a valid number",
41391 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
41393 thousandsDelimiter : false,
41395 * @cfg {String} valueAlign alignment of value
41397 valueAlign : "left",
41399 getAutoCreate : function()
41401 var hiddenInput = {
41405 cls: 'hidden-number-input'
41409 hiddenInput.name = this.name;
41414 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
41416 this.name = hiddenInput.name;
41418 if(cfg.cn.length > 0) {
41419 cfg.cn.push(hiddenInput);
41426 initEvents : function()
41428 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
41430 var allowed = "0123456789";
41432 if(this.allowDecimals){
41433 allowed += this.decimalSeparator;
41436 if(this.allowNegative){
41440 if(this.thousandsDelimiter) {
41444 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41446 var keyPress = function(e){
41448 var k = e.getKey();
41450 var c = e.getCharCode();
41453 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
41454 allowed.indexOf(String.fromCharCode(c)) === -1
41460 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41464 if(allowed.indexOf(String.fromCharCode(c)) === -1){
41469 this.el.on("keypress", keyPress, this);
41472 validateValue : function(value)
41475 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
41479 var num = this.parseValue(value);
41482 this.markInvalid(String.format(this.nanText, value));
41486 if(num < this.minValue){
41487 this.markInvalid(String.format(this.minText, this.minValue));
41491 if(num > this.maxValue){
41492 this.markInvalid(String.format(this.maxText, this.maxValue));
41499 getValue : function()
41501 var v = this.hiddenEl().getValue();
41503 return this.fixPrecision(this.parseValue(v));
41506 parseValue : function(value)
41508 if(this.thousandsDelimiter) {
41510 r = new RegExp(",", "g");
41511 value = value.replace(r, "");
41514 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41515 return isNaN(value) ? '' : value;
41518 fixPrecision : function(value)
41520 if(this.thousandsDelimiter) {
41522 r = new RegExp(",", "g");
41523 value = value.replace(r, "");
41526 var nan = isNaN(value);
41528 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41529 return nan ? '' : value;
41531 return parseFloat(value).toFixed(this.decimalPrecision);
41534 setValue : function(v)
41536 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
41542 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
41544 this.inputEl().dom.value = (v == '') ? '' :
41545 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
41547 if(!this.allowZero && v === '0') {
41548 this.hiddenEl().dom.value = '';
41549 this.inputEl().dom.value = '';
41556 decimalPrecisionFcn : function(v)
41558 return Math.floor(v);
41561 beforeBlur : function()
41563 var v = this.parseValue(this.getRawValue());
41565 if(v || v === 0 || v === ''){
41570 hiddenEl : function()
41572 return this.el.select('input.hidden-number-input',true).first();
41584 * @class Roo.bootstrap.DocumentSlider
41585 * @extends Roo.bootstrap.Component
41586 * Bootstrap DocumentSlider class
41589 * Create a new DocumentViewer
41590 * @param {Object} config The config object
41593 Roo.bootstrap.DocumentSlider = function(config){
41594 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
41601 * Fire after initEvent
41602 * @param {Roo.bootstrap.DocumentSlider} this
41607 * Fire after update
41608 * @param {Roo.bootstrap.DocumentSlider} this
41614 * @param {Roo.bootstrap.DocumentSlider} this
41620 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
41626 getAutoCreate : function()
41630 cls : 'roo-document-slider',
41634 cls : 'roo-document-slider-header',
41638 cls : 'roo-document-slider-header-title'
41644 cls : 'roo-document-slider-body',
41648 cls : 'roo-document-slider-prev',
41652 cls : 'fa fa-chevron-left'
41658 cls : 'roo-document-slider-thumb',
41662 cls : 'roo-document-slider-image'
41668 cls : 'roo-document-slider-next',
41672 cls : 'fa fa-chevron-right'
41684 initEvents : function()
41686 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
41687 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
41689 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
41690 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
41692 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
41693 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
41695 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
41696 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
41698 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
41699 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
41701 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
41702 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41704 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
41705 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
41707 this.thumbEl.on('click', this.onClick, this);
41709 this.prevIndicator.on('click', this.prev, this);
41711 this.nextIndicator.on('click', this.next, this);
41715 initial : function()
41717 if(this.files.length){
41718 this.indicator = 1;
41722 this.fireEvent('initial', this);
41725 update : function()
41727 this.imageEl.attr('src', this.files[this.indicator - 1]);
41729 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
41731 this.prevIndicator.show();
41733 if(this.indicator == 1){
41734 this.prevIndicator.hide();
41737 this.nextIndicator.show();
41739 if(this.indicator == this.files.length){
41740 this.nextIndicator.hide();
41743 this.thumbEl.scrollTo('top');
41745 this.fireEvent('update', this);
41748 onClick : function(e)
41750 e.preventDefault();
41752 this.fireEvent('click', this);
41757 e.preventDefault();
41759 this.indicator = Math.max(1, this.indicator - 1);
41766 e.preventDefault();
41768 this.indicator = Math.min(this.files.length, this.indicator + 1);
41782 * @class Roo.bootstrap.form.RadioSet
41783 * @extends Roo.bootstrap.form.Input
41784 * @children Roo.bootstrap.form.Radio
41785 * Bootstrap RadioSet class
41786 * @cfg {String} indicatorpos (left|right) default left
41787 * @cfg {Boolean} inline (true|false) inline the element (default true)
41788 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
41790 * Create a new RadioSet
41791 * @param {Object} config The config object
41794 Roo.bootstrap.form.RadioSet = function(config){
41796 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
41800 Roo.bootstrap.form.RadioSet.register(this);
41805 * Fires when the element is checked or unchecked.
41806 * @param {Roo.bootstrap.form.RadioSet} this This radio
41807 * @param {Roo.bootstrap.form.Radio} item The checked item
41812 * Fires when the element is click.
41813 * @param {Roo.bootstrap.form.RadioSet} this This radio set
41814 * @param {Roo.bootstrap.form.Radio} item The checked item
41815 * @param {Roo.EventObject} e The event object
41822 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
41830 indicatorpos : 'left',
41832 getAutoCreate : function()
41836 cls : 'roo-radio-set-label',
41840 html : this.fieldLabel
41844 if (Roo.bootstrap.version == 3) {
41847 if(this.indicatorpos == 'left'){
41850 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
41851 tooltip : 'This field is required'
41856 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
41857 tooltip : 'This field is required'
41863 cls : 'roo-radio-set-items'
41866 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
41868 if (align === 'left' && this.fieldLabel.length) {
41871 cls : "roo-radio-set-right",
41877 if(this.labelWidth > 12){
41878 label.style = "width: " + this.labelWidth + 'px';
41881 if(this.labelWidth < 13 && this.labelmd == 0){
41882 this.labelmd = this.labelWidth;
41885 if(this.labellg > 0){
41886 label.cls += ' col-lg-' + this.labellg;
41887 items.cls += ' col-lg-' + (12 - this.labellg);
41890 if(this.labelmd > 0){
41891 label.cls += ' col-md-' + this.labelmd;
41892 items.cls += ' col-md-' + (12 - this.labelmd);
41895 if(this.labelsm > 0){
41896 label.cls += ' col-sm-' + this.labelsm;
41897 items.cls += ' col-sm-' + (12 - this.labelsm);
41900 if(this.labelxs > 0){
41901 label.cls += ' col-xs-' + this.labelxs;
41902 items.cls += ' col-xs-' + (12 - this.labelxs);
41908 cls : 'roo-radio-set',
41912 cls : 'roo-radio-set-input',
41915 value : this.value ? this.value : ''
41922 if(this.weight.length){
41923 cfg.cls += ' roo-radio-' + this.weight;
41927 cfg.cls += ' roo-radio-set-inline';
41931 ['xs','sm','md','lg'].map(function(size){
41932 if (settings[size]) {
41933 cfg.cls += ' col-' + size + '-' + settings[size];
41941 initEvents : function()
41943 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
41944 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
41946 if(!this.fieldLabel.length){
41947 this.labelEl.hide();
41950 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
41951 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
41953 this.indicator = this.indicatorEl();
41955 if(this.indicator){
41956 this.indicator.addClass('invisible');
41959 this.originalValue = this.getValue();
41963 inputEl: function ()
41965 return this.el.select('.roo-radio-set-input', true).first();
41968 getChildContainer : function()
41970 return this.itemsEl;
41973 register : function(item)
41975 this.radioes.push(item);
41979 validate : function()
41981 if(this.getVisibilityEl().hasClass('hidden')){
41987 Roo.each(this.radioes, function(i){
41996 if(this.allowBlank) {
42000 if(this.disabled || valid){
42005 this.markInvalid();
42010 markValid : function()
42012 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42013 this.indicatorEl().removeClass('visible');
42014 this.indicatorEl().addClass('invisible');
42018 if (Roo.bootstrap.version == 3) {
42019 this.el.removeClass([this.invalidClass, this.validClass]);
42020 this.el.addClass(this.validClass);
42022 this.el.removeClass(['is-invalid','is-valid']);
42023 this.el.addClass(['is-valid']);
42025 this.fireEvent('valid', this);
42028 markInvalid : function(msg)
42030 if(this.allowBlank || this.disabled){
42034 if(this.labelEl.isVisible(true) && this.indicatorEl()){
42035 this.indicatorEl().removeClass('invisible');
42036 this.indicatorEl().addClass('visible');
42038 if (Roo.bootstrap.version == 3) {
42039 this.el.removeClass([this.invalidClass, this.validClass]);
42040 this.el.addClass(this.invalidClass);
42042 this.el.removeClass(['is-invalid','is-valid']);
42043 this.el.addClass(['is-invalid']);
42046 this.fireEvent('invalid', this, msg);
42050 setValue : function(v, suppressEvent)
42052 if(this.value === v){
42059 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42062 Roo.each(this.radioes, function(i){
42064 i.el.removeClass('checked');
42067 Roo.each(this.radioes, function(i){
42069 if(i.value === v || i.value.toString() === v.toString()){
42071 i.el.addClass('checked');
42073 if(suppressEvent !== true){
42074 this.fireEvent('check', this, i);
42085 clearInvalid : function(){
42087 if(!this.el || this.preventMark){
42091 this.el.removeClass([this.invalidClass]);
42093 this.fireEvent('valid', this);
42098 Roo.apply(Roo.bootstrap.form.RadioSet, {
42102 register : function(set)
42104 this.groups[set.name] = set;
42107 get: function(name)
42109 if (typeof(this.groups[name]) == 'undefined') {
42113 return this.groups[name] ;
42119 * Ext JS Library 1.1.1
42120 * Copyright(c) 2006-2007, Ext JS, LLC.
42122 * Originally Released Under LGPL - original licence link has changed is not relivant.
42125 * <script type="text/javascript">
42130 * @class Roo.bootstrap.SplitBar
42131 * @extends Roo.util.Observable
42132 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
42136 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
42137 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
42138 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
42139 split.minSize = 100;
42140 split.maxSize = 600;
42141 split.animate = true;
42142 split.on('moved', splitterMoved);
42145 * Create a new SplitBar
42146 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
42147 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
42148 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42149 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
42150 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
42151 position of the SplitBar).
42153 Roo.bootstrap.SplitBar = function(cfg){
42158 // dragElement : elm
42159 // resizingElement: el,
42161 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
42162 // placement : Roo.bootstrap.SplitBar.LEFT ,
42163 // existingProxy ???
42166 this.el = Roo.get(cfg.dragElement, true);
42167 this.el.dom.unselectable = "on";
42169 this.resizingEl = Roo.get(cfg.resizingElement, true);
42173 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
42174 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
42177 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
42180 * The minimum size of the resizing element. (Defaults to 0)
42186 * The maximum size of the resizing element. (Defaults to 2000)
42189 this.maxSize = 2000;
42192 * Whether to animate the transition to the new size
42195 this.animate = false;
42198 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
42201 this.useShim = false;
42206 if(!cfg.existingProxy){
42208 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
42210 this.proxy = Roo.get(cfg.existingProxy).dom;
42213 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
42216 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
42219 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
42222 this.dragSpecs = {};
42225 * @private The adapter to use to positon and resize elements
42227 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42228 this.adapter.init(this);
42230 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42232 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
42233 this.el.addClass("roo-splitbar-h");
42236 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
42237 this.el.addClass("roo-splitbar-v");
42243 * Fires when the splitter is moved (alias for {@link #event-moved})
42244 * @param {Roo.bootstrap.SplitBar} this
42245 * @param {Number} newSize the new width or height
42250 * Fires when the splitter is moved
42251 * @param {Roo.bootstrap.SplitBar} this
42252 * @param {Number} newSize the new width or height
42256 * @event beforeresize
42257 * Fires before the splitter is dragged
42258 * @param {Roo.bootstrap.SplitBar} this
42260 "beforeresize" : true,
42262 "beforeapply" : true
42265 Roo.util.Observable.call(this);
42268 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
42269 onStartProxyDrag : function(x, y){
42270 this.fireEvent("beforeresize", this);
42272 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
42274 o.enableDisplayMode("block");
42275 // all splitbars share the same overlay
42276 Roo.bootstrap.SplitBar.prototype.overlay = o;
42278 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
42279 this.overlay.show();
42280 Roo.get(this.proxy).setDisplayed("block");
42281 var size = this.adapter.getElementSize(this);
42282 this.activeMinSize = this.getMinimumSize();;
42283 this.activeMaxSize = this.getMaximumSize();;
42284 var c1 = size - this.activeMinSize;
42285 var c2 = Math.max(this.activeMaxSize - size, 0);
42286 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42287 this.dd.resetConstraints();
42288 this.dd.setXConstraint(
42289 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
42290 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
42292 this.dd.setYConstraint(0, 0);
42294 this.dd.resetConstraints();
42295 this.dd.setXConstraint(0, 0);
42296 this.dd.setYConstraint(
42297 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
42298 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
42301 this.dragSpecs.startSize = size;
42302 this.dragSpecs.startPoint = [x, y];
42303 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
42307 * @private Called after the drag operation by the DDProxy
42309 onEndProxyDrag : function(e){
42310 Roo.get(this.proxy).setDisplayed(false);
42311 var endPoint = Roo.lib.Event.getXY(e);
42313 this.overlay.hide();
42316 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42317 newSize = this.dragSpecs.startSize +
42318 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
42319 endPoint[0] - this.dragSpecs.startPoint[0] :
42320 this.dragSpecs.startPoint[0] - endPoint[0]
42323 newSize = this.dragSpecs.startSize +
42324 (this.placement == Roo.bootstrap.SplitBar.TOP ?
42325 endPoint[1] - this.dragSpecs.startPoint[1] :
42326 this.dragSpecs.startPoint[1] - endPoint[1]
42329 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
42330 if(newSize != this.dragSpecs.startSize){
42331 if(this.fireEvent('beforeapply', this, newSize) !== false){
42332 this.adapter.setElementSize(this, newSize);
42333 this.fireEvent("moved", this, newSize);
42334 this.fireEvent("resize", this, newSize);
42340 * Get the adapter this SplitBar uses
42341 * @return The adapter object
42343 getAdapter : function(){
42344 return this.adapter;
42348 * Set the adapter this SplitBar uses
42349 * @param {Object} adapter A SplitBar adapter object
42351 setAdapter : function(adapter){
42352 this.adapter = adapter;
42353 this.adapter.init(this);
42357 * Gets the minimum size for the resizing element
42358 * @return {Number} The minimum size
42360 getMinimumSize : function(){
42361 return this.minSize;
42365 * Sets the minimum size for the resizing element
42366 * @param {Number} minSize The minimum size
42368 setMinimumSize : function(minSize){
42369 this.minSize = minSize;
42373 * Gets the maximum size for the resizing element
42374 * @return {Number} The maximum size
42376 getMaximumSize : function(){
42377 return this.maxSize;
42381 * Sets the maximum size for the resizing element
42382 * @param {Number} maxSize The maximum size
42384 setMaximumSize : function(maxSize){
42385 this.maxSize = maxSize;
42389 * Sets the initialize size for the resizing element
42390 * @param {Number} size The initial size
42392 setCurrentSize : function(size){
42393 var oldAnimate = this.animate;
42394 this.animate = false;
42395 this.adapter.setElementSize(this, size);
42396 this.animate = oldAnimate;
42400 * Destroy this splitbar.
42401 * @param {Boolean} removeEl True to remove the element
42403 destroy : function(removeEl){
42405 this.shim.remove();
42408 this.proxy.parentNode.removeChild(this.proxy);
42416 * @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.
42418 Roo.bootstrap.SplitBar.createProxy = function(dir){
42419 var proxy = new Roo.Element(document.createElement("div"));
42420 proxy.unselectable();
42421 var cls = 'roo-splitbar-proxy';
42422 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
42423 document.body.appendChild(proxy.dom);
42428 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
42429 * Default Adapter. It assumes the splitter and resizing element are not positioned
42430 * elements and only gets/sets the width of the element. Generally used for table based layouts.
42432 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
42435 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
42436 // do nothing for now
42437 init : function(s){
42441 * Called before drag operations to get the current size of the resizing element.
42442 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42444 getElementSize : function(s){
42445 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42446 return s.resizingEl.getWidth();
42448 return s.resizingEl.getHeight();
42453 * Called after drag operations to set the size of the resizing element.
42454 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
42455 * @param {Number} newSize The new size to set
42456 * @param {Function} onComplete A function to be invoked when resizing is complete
42458 setElementSize : function(s, newSize, onComplete){
42459 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
42461 s.resizingEl.setWidth(newSize);
42463 onComplete(s, newSize);
42466 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
42471 s.resizingEl.setHeight(newSize);
42473 onComplete(s, newSize);
42476 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
42483 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
42484 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
42485 * Adapter that moves the splitter element to align with the resized sizing element.
42486 * Used with an absolute positioned SplitBar.
42487 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
42488 * document.body, make sure you assign an id to the body element.
42490 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
42491 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
42492 this.container = Roo.get(container);
42495 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
42496 init : function(s){
42497 this.basic.init(s);
42500 getElementSize : function(s){
42501 return this.basic.getElementSize(s);
42504 setElementSize : function(s, newSize, onComplete){
42505 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
42508 moveSplitter : function(s){
42509 var yes = Roo.bootstrap.SplitBar;
42510 switch(s.placement){
42512 s.el.setX(s.resizingEl.getRight());
42515 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
42518 s.el.setY(s.resizingEl.getBottom());
42521 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
42528 * Orientation constant - Create a vertical SplitBar
42532 Roo.bootstrap.SplitBar.VERTICAL = 1;
42535 * Orientation constant - Create a horizontal SplitBar
42539 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
42542 * Placement constant - The resizing element is to the left of the splitter element
42546 Roo.bootstrap.SplitBar.LEFT = 1;
42549 * Placement constant - The resizing element is to the right of the splitter element
42553 Roo.bootstrap.SplitBar.RIGHT = 2;
42556 * Placement constant - The resizing element is positioned above the splitter element
42560 Roo.bootstrap.SplitBar.TOP = 3;
42563 * Placement constant - The resizing element is positioned under splitter element
42567 Roo.bootstrap.SplitBar.BOTTOM = 4;
42570 * Ext JS Library 1.1.1
42571 * Copyright(c) 2006-2007, Ext JS, LLC.
42573 * Originally Released Under LGPL - original licence link has changed is not relivant.
42576 * <script type="text/javascript">
42580 * @class Roo.bootstrap.layout.Manager
42581 * @extends Roo.bootstrap.Component
42583 * Base class for layout managers.
42585 Roo.bootstrap.layout.Manager = function(config)
42587 this.monitorWindowResize = true; // do this before we apply configuration.
42589 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
42595 /** false to disable window resize monitoring @type Boolean */
42601 * Fires when a layout is performed.
42602 * @param {Roo.LayoutManager} this
42606 * @event regionresized
42607 * Fires when the user resizes a region.
42608 * @param {Roo.LayoutRegion} region The resized region
42609 * @param {Number} newSize The new size (width for east/west, height for north/south)
42611 "regionresized" : true,
42613 * @event regioncollapsed
42614 * Fires when a region is collapsed.
42615 * @param {Roo.LayoutRegion} region The collapsed region
42617 "regioncollapsed" : true,
42619 * @event regionexpanded
42620 * Fires when a region is expanded.
42621 * @param {Roo.LayoutRegion} region The expanded region
42623 "regionexpanded" : true
42625 this.updating = false;
42628 this.el = Roo.get(config.el);
42634 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
42639 monitorWindowResize : true,
42645 onRender : function(ct, position)
42648 this.el = Roo.get(ct);
42651 //this.fireEvent('render',this);
42655 initEvents: function()
42659 // ie scrollbar fix
42660 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42661 document.body.scroll = "no";
42662 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42663 this.el.position('relative');
42665 this.id = this.el.id;
42666 this.el.addClass("roo-layout-container");
42667 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42668 if(this.el.dom != document.body ) {
42669 this.el.on('resize', this.layout,this);
42670 this.el.on('show', this.layout,this);
42676 * Returns true if this layout is currently being updated
42677 * @return {Boolean}
42679 isUpdating : function(){
42680 return this.updating;
42684 * Suspend the LayoutManager from doing auto-layouts while
42685 * making multiple add or remove calls
42687 beginUpdate : function(){
42688 this.updating = true;
42692 * Restore auto-layouts and optionally disable the manager from performing a layout
42693 * @param {Boolean} noLayout true to disable a layout update
42695 endUpdate : function(noLayout){
42696 this.updating = false;
42702 layout: function(){
42706 onRegionResized : function(region, newSize){
42707 this.fireEvent("regionresized", region, newSize);
42711 onRegionCollapsed : function(region){
42712 this.fireEvent("regioncollapsed", region);
42715 onRegionExpanded : function(region){
42716 this.fireEvent("regionexpanded", region);
42720 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42721 * performs box-model adjustments.
42722 * @return {Object} The size as an object {width: (the width), height: (the height)}
42724 getViewSize : function()
42727 if(this.el.dom != document.body){
42728 size = this.el.getSize();
42730 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42732 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42733 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42738 * Returns the Element this layout is bound to.
42739 * @return {Roo.Element}
42741 getEl : function(){
42746 * Returns the specified region.
42747 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42748 * @return {Roo.LayoutRegion}
42750 getRegion : function(target){
42751 return this.regions[target.toLowerCase()];
42754 onWindowResize : function(){
42755 if(this.monitorWindowResize){
42762 * Ext JS Library 1.1.1
42763 * Copyright(c) 2006-2007, Ext JS, LLC.
42765 * Originally Released Under LGPL - original licence link has changed is not relivant.
42768 * <script type="text/javascript">
42771 * @class Roo.bootstrap.layout.Border
42772 * @extends Roo.bootstrap.layout.Manager
42773 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
42774 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
42775 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42776 * please see: examples/bootstrap/nested.html<br><br>
42778 <b>The container the layout is rendered into can be either the body element or any other element.
42779 If it is not the body element, the container needs to either be an absolute positioned element,
42780 or you will need to add "position:relative" to the css of the container. You will also need to specify
42781 the container size if it is not the body element.</b>
42784 * Create a new Border
42785 * @param {Object} config Configuration options
42787 Roo.bootstrap.layout.Border = function(config){
42788 config = config || {};
42789 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
42793 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42794 if(config[region]){
42795 config[region].region = region;
42796 this.addRegion(config[region]);
42802 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
42804 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
42807 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
42810 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
42813 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
42816 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
42819 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
42825 parent : false, // this might point to a 'nest' or a ???
42828 * Creates and adds a new region if it doesn't already exist.
42829 * @param {String} target The target region key (north, south, east, west or center).
42830 * @param {Object} config The regions config object
42831 * @return {BorderLayoutRegion} The new region
42833 addRegion : function(config)
42835 if(!this.regions[config.region]){
42836 var r = this.factory(config);
42837 this.bindRegion(r);
42839 return this.regions[config.region];
42843 bindRegion : function(r){
42844 this.regions[r.config.region] = r;
42846 r.on("visibilitychange", this.layout, this);
42847 r.on("paneladded", this.layout, this);
42848 r.on("panelremoved", this.layout, this);
42849 r.on("invalidated", this.layout, this);
42850 r.on("resized", this.onRegionResized, this);
42851 r.on("collapsed", this.onRegionCollapsed, this);
42852 r.on("expanded", this.onRegionExpanded, this);
42856 * Performs a layout update.
42858 layout : function()
42860 if(this.updating) {
42864 // render all the rebions if they have not been done alreayd?
42865 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
42866 if(this.regions[region] && !this.regions[region].bodyEl){
42867 this.regions[region].onRender(this.el)
42871 var size = this.getViewSize();
42872 var w = size.width;
42873 var h = size.height;
42878 //var x = 0, y = 0;
42880 var rs = this.regions;
42881 var north = rs["north"];
42882 var south = rs["south"];
42883 var west = rs["west"];
42884 var east = rs["east"];
42885 var center = rs["center"];
42886 //if(this.hideOnLayout){ // not supported anymore
42887 //c.el.setStyle("display", "none");
42889 if(north && north.isVisible()){
42890 var b = north.getBox();
42891 var m = north.getMargins();
42892 b.width = w - (m.left+m.right);
42895 centerY = b.height + b.y + m.bottom;
42896 centerH -= centerY;
42897 north.updateBox(this.safeBox(b));
42899 if(south && south.isVisible()){
42900 var b = south.getBox();
42901 var m = south.getMargins();
42902 b.width = w - (m.left+m.right);
42904 var totalHeight = (b.height + m.top + m.bottom);
42905 b.y = h - totalHeight + m.top;
42906 centerH -= totalHeight;
42907 south.updateBox(this.safeBox(b));
42909 if(west && west.isVisible()){
42910 var b = west.getBox();
42911 var m = west.getMargins();
42912 b.height = centerH - (m.top+m.bottom);
42914 b.y = centerY + m.top;
42915 var totalWidth = (b.width + m.left + m.right);
42916 centerX += totalWidth;
42917 centerW -= totalWidth;
42918 west.updateBox(this.safeBox(b));
42920 if(east && east.isVisible()){
42921 var b = east.getBox();
42922 var m = east.getMargins();
42923 b.height = centerH - (m.top+m.bottom);
42924 var totalWidth = (b.width + m.left + m.right);
42925 b.x = w - totalWidth + m.left;
42926 b.y = centerY + m.top;
42927 centerW -= totalWidth;
42928 east.updateBox(this.safeBox(b));
42931 var m = center.getMargins();
42933 x: centerX + m.left,
42934 y: centerY + m.top,
42935 width: centerW - (m.left+m.right),
42936 height: centerH - (m.top+m.bottom)
42938 //if(this.hideOnLayout){
42939 //center.el.setStyle("display", "block");
42941 center.updateBox(this.safeBox(centerBox));
42944 this.fireEvent("layout", this);
42948 safeBox : function(box){
42949 box.width = Math.max(0, box.width);
42950 box.height = Math.max(0, box.height);
42955 * Adds a ContentPanel (or subclass) to this layout.
42956 * @param {String} target The target region key (north, south, east, west or center).
42957 * @param {Roo.ContentPanel} panel The panel to add
42958 * @return {Roo.ContentPanel} The added panel
42960 add : function(target, panel){
42962 target = target.toLowerCase();
42963 return this.regions[target].add(panel);
42967 * Remove a ContentPanel (or subclass) to this layout.
42968 * @param {String} target The target region key (north, south, east, west or center).
42969 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42970 * @return {Roo.ContentPanel} The removed panel
42972 remove : function(target, panel){
42973 target = target.toLowerCase();
42974 return this.regions[target].remove(panel);
42978 * Searches all regions for a panel with the specified id
42979 * @param {String} panelId
42980 * @return {Roo.ContentPanel} The panel or null if it wasn't found
42982 findPanel : function(panelId){
42983 var rs = this.regions;
42984 for(var target in rs){
42985 if(typeof rs[target] != "function"){
42986 var p = rs[target].getPanel(panelId);
42996 * Searches all regions for a panel with the specified id and activates (shows) it.
42997 * @param {String/ContentPanel} panelId The panels id or the panel itself
42998 * @return {Roo.ContentPanel} The shown panel or null
43000 showPanel : function(panelId) {
43001 var rs = this.regions;
43002 for(var target in rs){
43003 var r = rs[target];
43004 if(typeof r != "function"){
43005 if(r.hasPanel(panelId)){
43006 return r.showPanel(panelId);
43014 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43015 * @param {Roo.state.Provider} provider (optional) An alternate state provider
43018 restoreState : function(provider){
43020 provider = Roo.state.Manager;
43022 var sm = new Roo.LayoutStateManager();
43023 sm.init(this, provider);
43029 * Adds a xtype elements to the layout.
43033 xtype : 'ContentPanel',
43040 xtype : 'NestedLayoutPanel',
43046 items : [ ... list of content panels or nested layout panels.. ]
43050 * @param {Object} cfg Xtype definition of item to add.
43052 addxtype : function(cfg)
43054 // basically accepts a pannel...
43055 // can accept a layout region..!?!?
43056 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
43059 // theory? children can only be panels??
43061 //if (!cfg.xtype.match(/Panel$/)) {
43066 if (typeof(cfg.region) == 'undefined') {
43067 Roo.log("Failed to add Panel, region was not set");
43071 var region = cfg.region;
43077 xitems = cfg.items;
43082 if ( region == 'center') {
43083 Roo.log("Center: " + cfg.title);
43089 case 'Content': // ContentPanel (el, cfg)
43090 case 'Scroll': // ContentPanel (el, cfg)
43092 cfg.autoCreate = cfg.autoCreate || true;
43093 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43095 // var el = this.el.createChild();
43096 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43099 this.add(region, ret);
43103 case 'TreePanel': // our new panel!
43104 cfg.el = this.el.createChild();
43105 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43106 this.add(region, ret);
43111 // create a new Layout (which is a Border Layout...
43113 var clayout = cfg.layout;
43114 clayout.el = this.el.createChild();
43115 clayout.items = clayout.items || [];
43119 // replace this exitems with the clayout ones..
43120 xitems = clayout.items;
43122 // force background off if it's in center...
43123 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43124 cfg.background = false;
43126 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
43129 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43130 //console.log('adding nested layout panel ' + cfg.toSource());
43131 this.add(region, ret);
43132 nb = {}; /// find first...
43137 // needs grid and region
43139 //var el = this.getRegion(region).el.createChild();
43141 *var el = this.el.createChild();
43142 // create the grid first...
43143 cfg.grid.container = el;
43144 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
43147 if (region == 'center' && this.active ) {
43148 cfg.background = false;
43151 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
43153 this.add(region, ret);
43155 if (cfg.background) {
43156 // render grid on panel activation (if panel background)
43157 ret.on('activate', function(gp) {
43158 if (!gp.grid.rendered) {
43159 // gp.grid.render(el);
43163 // cfg.grid.render(el);
43169 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
43170 // it was the old xcomponent building that caused this before.
43171 // espeically if border is the top element in the tree.
43181 if (typeof(Roo[cfg.xtype]) != 'undefined') {
43183 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43184 this.add(region, ret);
43188 throw "Can not add '" + cfg.xtype + "' to Border";
43194 this.beginUpdate();
43198 Roo.each(xitems, function(i) {
43199 region = nb && i.region ? i.region : false;
43201 var add = ret.addxtype(i);
43204 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
43205 if (!i.background) {
43206 abn[region] = nb[region] ;
43213 // make the last non-background panel active..
43214 //if (nb) { Roo.log(abn); }
43217 for(var r in abn) {
43218 region = this.getRegion(r);
43220 // tried using nb[r], but it does not work..
43222 region.showPanel(abn[r]);
43233 factory : function(cfg)
43236 var validRegions = Roo.bootstrap.layout.Border.regions;
43238 var target = cfg.region;
43241 var r = Roo.bootstrap.layout;
43245 return new r.North(cfg);
43247 return new r.South(cfg);
43249 return new r.East(cfg);
43251 return new r.West(cfg);
43253 return new r.Center(cfg);
43255 throw 'Layout region "'+target+'" not supported.';
43262 * Ext JS Library 1.1.1
43263 * Copyright(c) 2006-2007, Ext JS, LLC.
43265 * Originally Released Under LGPL - original licence link has changed is not relivant.
43268 * <script type="text/javascript">
43272 * @class Roo.bootstrap.layout.Basic
43273 * @extends Roo.util.Observable
43274 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43275 * and does not have a titlebar, tabs or any other features. All it does is size and position
43276 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43277 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43278 * @cfg {string} region the region that it inhabits..
43279 * @cfg {bool} skipConfig skip config?
43283 Roo.bootstrap.layout.Basic = function(config){
43285 this.mgr = config.mgr;
43287 this.position = config.region;
43289 var skipConfig = config.skipConfig;
43293 * @scope Roo.BasicLayoutRegion
43297 * @event beforeremove
43298 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43299 * @param {Roo.LayoutRegion} this
43300 * @param {Roo.ContentPanel} panel The panel
43301 * @param {Object} e The cancel event object
43303 "beforeremove" : true,
43305 * @event invalidated
43306 * Fires when the layout for this region is changed.
43307 * @param {Roo.LayoutRegion} this
43309 "invalidated" : true,
43311 * @event visibilitychange
43312 * Fires when this region is shown or hidden
43313 * @param {Roo.LayoutRegion} this
43314 * @param {Boolean} visibility true or false
43316 "visibilitychange" : true,
43318 * @event paneladded
43319 * Fires when a panel is added.
43320 * @param {Roo.LayoutRegion} this
43321 * @param {Roo.ContentPanel} panel The panel
43323 "paneladded" : true,
43325 * @event panelremoved
43326 * Fires when a panel is removed.
43327 * @param {Roo.LayoutRegion} this
43328 * @param {Roo.ContentPanel} panel The panel
43330 "panelremoved" : true,
43332 * @event beforecollapse
43333 * Fires when this region before collapse.
43334 * @param {Roo.LayoutRegion} this
43336 "beforecollapse" : true,
43339 * Fires when this region is collapsed.
43340 * @param {Roo.LayoutRegion} this
43342 "collapsed" : true,
43345 * Fires when this region is expanded.
43346 * @param {Roo.LayoutRegion} this
43351 * Fires when this region is slid into view.
43352 * @param {Roo.LayoutRegion} this
43354 "slideshow" : true,
43357 * Fires when this region slides out of view.
43358 * @param {Roo.LayoutRegion} this
43360 "slidehide" : true,
43362 * @event panelactivated
43363 * Fires when a panel is activated.
43364 * @param {Roo.LayoutRegion} this
43365 * @param {Roo.ContentPanel} panel The activated panel
43367 "panelactivated" : true,
43370 * Fires when the user resizes this region.
43371 * @param {Roo.LayoutRegion} this
43372 * @param {Number} newSize The new size (width for east/west, height for north/south)
43376 /** A collection of panels in this region. @type Roo.util.MixedCollection */
43377 this.panels = new Roo.util.MixedCollection();
43378 this.panels.getKey = this.getPanelId.createDelegate(this);
43380 this.activePanel = null;
43381 // ensure listeners are added...
43383 if (config.listeners || config.events) {
43384 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
43385 listeners : config.listeners || {},
43386 events : config.events || {}
43390 if(skipConfig !== true){
43391 this.applyConfig(config);
43395 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
43397 getPanelId : function(p){
43401 applyConfig : function(config){
43402 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43403 this.config = config;
43408 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
43409 * the width, for horizontal (north, south) the height.
43410 * @param {Number} newSize The new width or height
43412 resizeTo : function(newSize){
43413 var el = this.el ? this.el :
43414 (this.activePanel ? this.activePanel.getEl() : null);
43416 switch(this.position){
43419 el.setWidth(newSize);
43420 this.fireEvent("resized", this, newSize);
43424 el.setHeight(newSize);
43425 this.fireEvent("resized", this, newSize);
43431 getBox : function(){
43432 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43435 getMargins : function(){
43436 return this.margins;
43439 updateBox : function(box){
43441 var el = this.activePanel.getEl();
43442 el.dom.style.left = box.x + "px";
43443 el.dom.style.top = box.y + "px";
43444 this.activePanel.setSize(box.width, box.height);
43448 * Returns the container element for this region.
43449 * @return {Roo.Element}
43451 getEl : function(){
43452 return this.activePanel;
43456 * Returns true if this region is currently visible.
43457 * @return {Boolean}
43459 isVisible : function(){
43460 return this.activePanel ? true : false;
43463 setActivePanel : function(panel){
43464 panel = this.getPanel(panel);
43465 if(this.activePanel && this.activePanel != panel){
43466 this.activePanel.setActiveState(false);
43467 this.activePanel.getEl().setLeftTop(-10000,-10000);
43469 this.activePanel = panel;
43470 panel.setActiveState(true);
43472 panel.setSize(this.box.width, this.box.height);
43474 this.fireEvent("panelactivated", this, panel);
43475 this.fireEvent("invalidated");
43479 * Show the specified panel.
43480 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43481 * @return {Roo.ContentPanel} The shown panel or null
43483 showPanel : function(panel){
43484 panel = this.getPanel(panel);
43486 this.setActivePanel(panel);
43492 * Get the active panel for this region.
43493 * @return {Roo.ContentPanel} The active panel or null
43495 getActivePanel : function(){
43496 return this.activePanel;
43500 * Add the passed ContentPanel(s)
43501 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43502 * @return {Roo.ContentPanel} The panel added (if only one was added)
43504 add : function(panel){
43505 if(arguments.length > 1){
43506 for(var i = 0, len = arguments.length; i < len; i++) {
43507 this.add(arguments[i]);
43511 if(this.hasPanel(panel)){
43512 this.showPanel(panel);
43515 var el = panel.getEl();
43516 if(el.dom.parentNode != this.mgr.el.dom){
43517 this.mgr.el.dom.appendChild(el.dom);
43519 if(panel.setRegion){
43520 panel.setRegion(this);
43522 this.panels.add(panel);
43523 el.setStyle("position", "absolute");
43524 if(!panel.background){
43525 this.setActivePanel(panel);
43526 if(this.config.initialSize && this.panels.getCount()==1){
43527 this.resizeTo(this.config.initialSize);
43530 this.fireEvent("paneladded", this, panel);
43535 * Returns true if the panel is in this region.
43536 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43537 * @return {Boolean}
43539 hasPanel : function(panel){
43540 if(typeof panel == "object"){ // must be panel obj
43541 panel = panel.getId();
43543 return this.getPanel(panel) ? true : false;
43547 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43548 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43549 * @param {Boolean} preservePanel Overrides the config preservePanel option
43550 * @return {Roo.ContentPanel} The panel that was removed
43552 remove : function(panel, preservePanel){
43553 panel = this.getPanel(panel);
43558 this.fireEvent("beforeremove", this, panel, e);
43559 if(e.cancel === true){
43562 var panelId = panel.getId();
43563 this.panels.removeKey(panelId);
43568 * Returns the panel specified or null if it's not in this region.
43569 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43570 * @return {Roo.ContentPanel}
43572 getPanel : function(id){
43573 if(typeof id == "object"){ // must be panel obj
43576 return this.panels.get(id);
43580 * Returns this regions position (north/south/east/west/center).
43583 getPosition: function(){
43584 return this.position;
43588 * Ext JS Library 1.1.1
43589 * Copyright(c) 2006-2007, Ext JS, LLC.
43591 * Originally Released Under LGPL - original licence link has changed is not relivant.
43594 * <script type="text/javascript">
43598 * @class Roo.bootstrap.layout.Region
43599 * @extends Roo.bootstrap.layout.Basic
43600 * This class represents a region in a layout manager.
43602 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43603 * @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})
43604 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
43605 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43606 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43607 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43608 * @cfg {String} title The title for the region (overrides panel titles)
43609 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43610 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43611 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43612 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43613 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43614 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43615 * the space available, similar to FireFox 1.5 tabs (defaults to false)
43616 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43617 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43618 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
43620 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43621 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43622 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43623 * @cfg {Number} width For East/West panels
43624 * @cfg {Number} height For North/South panels
43625 * @cfg {Boolean} split To show the splitter
43626 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
43628 * @cfg {string} cls Extra CSS classes to add to region
43630 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
43631 * @cfg {string} region the region that it inhabits..
43634 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
43635 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
43637 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
43638 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
43639 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
43641 Roo.bootstrap.layout.Region = function(config)
43643 this.applyConfig(config);
43645 var mgr = config.mgr;
43646 var pos = config.region;
43647 config.skipConfig = true;
43648 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
43651 this.onRender(mgr.el);
43654 this.visible = true;
43655 this.collapsed = false;
43656 this.unrendered_panels = [];
43659 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
43661 position: '', // set by wrapper (eg. north/south etc..)
43662 unrendered_panels : null, // unrendered panels.
43664 tabPosition : false,
43666 mgr: false, // points to 'Border'
43669 createBody : function(){
43670 /** This region's body element
43671 * @type Roo.Element */
43672 this.bodyEl = this.el.createChild({
43674 cls: "roo-layout-panel-body tab-content" // bootstrap added...
43678 onRender: function(ctr, pos)
43680 var dh = Roo.DomHelper;
43681 /** This region's container element
43682 * @type Roo.Element */
43683 this.el = dh.append(ctr.dom, {
43685 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
43687 /** This region's title element
43688 * @type Roo.Element */
43690 this.titleEl = dh.append(this.el.dom, {
43692 unselectable: "on",
43693 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
43695 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
43696 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
43700 this.titleEl.enableDisplayMode();
43701 /** This region's title text element
43702 * @type HTMLElement */
43703 this.titleTextEl = this.titleEl.dom.firstChild;
43704 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43706 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
43707 this.closeBtn.enableDisplayMode();
43708 this.closeBtn.on("click", this.closeClicked, this);
43709 this.closeBtn.hide();
43711 this.createBody(this.config);
43712 if(this.config.hideWhenEmpty){
43714 this.on("paneladded", this.validateVisibility, this);
43715 this.on("panelremoved", this.validateVisibility, this);
43717 if(this.autoScroll){
43718 this.bodyEl.setStyle("overflow", "auto");
43720 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
43722 //if(c.titlebar !== false){
43723 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
43724 this.titleEl.hide();
43726 this.titleEl.show();
43727 if(this.config.title){
43728 this.titleTextEl.innerHTML = this.config.title;
43732 if(this.config.collapsed){
43733 this.collapse(true);
43735 if(this.config.hidden){
43739 if (this.unrendered_panels && this.unrendered_panels.length) {
43740 for (var i =0;i< this.unrendered_panels.length; i++) {
43741 this.add(this.unrendered_panels[i]);
43743 this.unrendered_panels = null;
43749 applyConfig : function(c)
43752 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
43753 var dh = Roo.DomHelper;
43754 if(c.titlebar !== false){
43755 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
43756 this.collapseBtn.on("click", this.collapse, this);
43757 this.collapseBtn.enableDisplayMode();
43759 if(c.showPin === true || this.showPin){
43760 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
43761 this.stickBtn.enableDisplayMode();
43762 this.stickBtn.on("click", this.expand, this);
43763 this.stickBtn.hide();
43768 /** This region's collapsed element
43769 * @type Roo.Element */
43772 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43773 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43776 if(c.floatable !== false){
43777 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43778 this.collapsedEl.on("click", this.collapseClick, this);
43781 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43782 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43783 id: "message", unselectable: "on", style:{"float":"left"}});
43784 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43786 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43787 this.expandBtn.on("click", this.expand, this);
43791 if(this.collapseBtn){
43792 this.collapseBtn.setVisible(c.collapsible == true);
43795 this.cmargins = c.cmargins || this.cmargins ||
43796 (this.position == "west" || this.position == "east" ?
43797 {top: 0, left: 2, right:2, bottom: 0} :
43798 {top: 2, left: 0, right:0, bottom: 2});
43800 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43803 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
43805 this.autoScroll = c.autoScroll || false;
43810 this.duration = c.duration || .30;
43811 this.slideDuration = c.slideDuration || .45;
43816 * Returns true if this region is currently visible.
43817 * @return {Boolean}
43819 isVisible : function(){
43820 return this.visible;
43824 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43825 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
43827 //setCollapsedTitle : function(title){
43828 // title = title || " ";
43829 // if(this.collapsedTitleTextEl){
43830 // this.collapsedTitleTextEl.innerHTML = title;
43834 getBox : function(){
43836 // if(!this.collapsed){
43837 b = this.el.getBox(false, true);
43839 // b = this.collapsedEl.getBox(false, true);
43844 getMargins : function(){
43845 return this.margins;
43846 //return this.collapsed ? this.cmargins : this.margins;
43849 highlight : function(){
43850 this.el.addClass("x-layout-panel-dragover");
43853 unhighlight : function(){
43854 this.el.removeClass("x-layout-panel-dragover");
43857 updateBox : function(box)
43859 if (!this.bodyEl) {
43860 return; // not rendered yet..
43864 if(!this.collapsed){
43865 this.el.dom.style.left = box.x + "px";
43866 this.el.dom.style.top = box.y + "px";
43867 this.updateBody(box.width, box.height);
43869 this.collapsedEl.dom.style.left = box.x + "px";
43870 this.collapsedEl.dom.style.top = box.y + "px";
43871 this.collapsedEl.setSize(box.width, box.height);
43874 this.tabs.autoSizeTabs();
43878 updateBody : function(w, h)
43881 this.el.setWidth(w);
43882 w -= this.el.getBorderWidth("rl");
43883 if(this.config.adjustments){
43884 w += this.config.adjustments[0];
43887 if(h !== null && h > 0){
43888 this.el.setHeight(h);
43889 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43890 h -= this.el.getBorderWidth("tb");
43891 if(this.config.adjustments){
43892 h += this.config.adjustments[1];
43894 this.bodyEl.setHeight(h);
43896 h = this.tabs.syncHeight(h);
43899 if(this.panelSize){
43900 w = w !== null ? w : this.panelSize.width;
43901 h = h !== null ? h : this.panelSize.height;
43903 if(this.activePanel){
43904 var el = this.activePanel.getEl();
43905 w = w !== null ? w : el.getWidth();
43906 h = h !== null ? h : el.getHeight();
43907 this.panelSize = {width: w, height: h};
43908 this.activePanel.setSize(w, h);
43910 if(Roo.isIE && this.tabs){
43911 this.tabs.el.repaint();
43916 * Returns the container element for this region.
43917 * @return {Roo.Element}
43919 getEl : function(){
43924 * Hides this region.
43927 //if(!this.collapsed){
43928 this.el.dom.style.left = "-2000px";
43931 // this.collapsedEl.dom.style.left = "-2000px";
43932 // this.collapsedEl.hide();
43934 this.visible = false;
43935 this.fireEvent("visibilitychange", this, false);
43939 * Shows this region if it was previously hidden.
43942 //if(!this.collapsed){
43945 // this.collapsedEl.show();
43947 this.visible = true;
43948 this.fireEvent("visibilitychange", this, true);
43951 closeClicked : function(){
43952 if(this.activePanel){
43953 this.remove(this.activePanel);
43957 collapseClick : function(e){
43959 e.stopPropagation();
43962 e.stopPropagation();
43968 * Collapses this region.
43969 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43972 collapse : function(skipAnim, skipCheck = false){
43973 if(this.collapsed) {
43977 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
43979 this.collapsed = true;
43981 this.split.el.hide();
43983 if(this.config.animate && skipAnim !== true){
43984 this.fireEvent("invalidated", this);
43985 this.animateCollapse();
43987 this.el.setLocation(-20000,-20000);
43989 this.collapsedEl.show();
43990 this.fireEvent("collapsed", this);
43991 this.fireEvent("invalidated", this);
43997 animateCollapse : function(){
44002 * Expands this region if it was previously collapsed.
44003 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44004 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44007 expand : function(e, skipAnim){
44009 e.stopPropagation();
44011 if(!this.collapsed || this.el.hasActiveFx()) {
44015 this.afterSlideIn();
44018 this.collapsed = false;
44019 if(this.config.animate && skipAnim !== true){
44020 this.animateExpand();
44024 this.split.el.show();
44026 this.collapsedEl.setLocation(-2000,-2000);
44027 this.collapsedEl.hide();
44028 this.fireEvent("invalidated", this);
44029 this.fireEvent("expanded", this);
44033 animateExpand : function(){
44037 initTabs : function()
44039 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
44041 var ts = new Roo.bootstrap.panel.Tabs({
44042 el: this.bodyEl.dom,
44044 tabPosition: this.tabPosition ? this.tabPosition : 'top',
44045 disableTooltips: this.config.disableTabTips,
44046 toolbar : this.config.toolbar
44049 if(this.config.hideTabs){
44050 ts.stripWrap.setDisplayed(false);
44053 ts.resizeTabs = this.config.resizeTabs === true;
44054 ts.minTabWidth = this.config.minTabWidth || 40;
44055 ts.maxTabWidth = this.config.maxTabWidth || 250;
44056 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44057 ts.monitorResize = false;
44058 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
44059 ts.bodyEl.addClass('roo-layout-tabs-body');
44060 this.panels.each(this.initPanelAsTab, this);
44063 initPanelAsTab : function(panel){
44064 var ti = this.tabs.addTab(
44068 this.config.closeOnTab && panel.isClosable(),
44071 if(panel.tabTip !== undefined){
44072 ti.setTooltip(panel.tabTip);
44074 ti.on("activate", function(){
44075 this.setActivePanel(panel);
44078 if(this.config.closeOnTab){
44079 ti.on("beforeclose", function(t, e){
44081 this.remove(panel);
44085 panel.tabItem = ti;
44090 updatePanelTitle : function(panel, title)
44092 if(this.activePanel == panel){
44093 this.updateTitle(title);
44096 var ti = this.tabs.getTab(panel.getEl().id);
44098 if(panel.tabTip !== undefined){
44099 ti.setTooltip(panel.tabTip);
44104 updateTitle : function(title){
44105 if(this.titleTextEl && !this.config.title){
44106 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
44110 setActivePanel : function(panel)
44112 panel = this.getPanel(panel);
44113 if(this.activePanel && this.activePanel != panel){
44114 if(this.activePanel.setActiveState(false) === false){
44118 this.activePanel = panel;
44119 panel.setActiveState(true);
44120 if(this.panelSize){
44121 panel.setSize(this.panelSize.width, this.panelSize.height);
44124 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44126 this.updateTitle(panel.getTitle());
44128 this.fireEvent("invalidated", this);
44130 this.fireEvent("panelactivated", this, panel);
44134 * Shows the specified panel.
44135 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44136 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44138 showPanel : function(panel)
44140 panel = this.getPanel(panel);
44143 var tab = this.tabs.getTab(panel.getEl().id);
44144 if(tab.isHidden()){
44145 this.tabs.unhideTab(tab.id);
44149 this.setActivePanel(panel);
44156 * Get the active panel for this region.
44157 * @return {Roo.ContentPanel} The active panel or null
44159 getActivePanel : function(){
44160 return this.activePanel;
44163 validateVisibility : function(){
44164 if(this.panels.getCount() < 1){
44165 this.updateTitle(" ");
44166 this.closeBtn.hide();
44169 if(!this.isVisible()){
44176 * Adds the passed ContentPanel(s) to this region.
44177 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44178 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44180 add : function(panel)
44182 if(arguments.length > 1){
44183 for(var i = 0, len = arguments.length; i < len; i++) {
44184 this.add(arguments[i]);
44189 // if we have not been rendered yet, then we can not really do much of this..
44190 if (!this.bodyEl) {
44191 this.unrendered_panels.push(panel);
44198 if(this.hasPanel(panel)){
44199 this.showPanel(panel);
44202 panel.setRegion(this);
44203 this.panels.add(panel);
44204 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44205 // sinle panel - no tab...?? would it not be better to render it with the tabs,
44206 // and hide them... ???
44207 this.bodyEl.dom.appendChild(panel.getEl().dom);
44208 if(panel.background !== true){
44209 this.setActivePanel(panel);
44211 this.fireEvent("paneladded", this, panel);
44218 this.initPanelAsTab(panel);
44222 if(panel.background !== true){
44223 this.tabs.activate(panel.getEl().id);
44225 this.fireEvent("paneladded", this, panel);
44230 * Hides the tab for the specified panel.
44231 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44233 hidePanel : function(panel){
44234 if(this.tabs && (panel = this.getPanel(panel))){
44235 this.tabs.hideTab(panel.getEl().id);
44240 * Unhides the tab for a previously hidden panel.
44241 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44243 unhidePanel : function(panel){
44244 if(this.tabs && (panel = this.getPanel(panel))){
44245 this.tabs.unhideTab(panel.getEl().id);
44249 clearPanels : function(){
44250 while(this.panels.getCount() > 0){
44251 this.remove(this.panels.first());
44256 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44257 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44258 * @param {Boolean} preservePanel Overrides the config preservePanel option
44259 * @return {Roo.ContentPanel} The panel that was removed
44261 remove : function(panel, preservePanel)
44263 panel = this.getPanel(panel);
44268 this.fireEvent("beforeremove", this, panel, e);
44269 if(e.cancel === true){
44272 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44273 var panelId = panel.getId();
44274 this.panels.removeKey(panelId);
44276 document.body.appendChild(panel.getEl().dom);
44279 this.tabs.removeTab(panel.getEl().id);
44280 }else if (!preservePanel){
44281 this.bodyEl.dom.removeChild(panel.getEl().dom);
44283 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44284 var p = this.panels.first();
44285 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44286 tempEl.appendChild(p.getEl().dom);
44287 this.bodyEl.update("");
44288 this.bodyEl.dom.appendChild(p.getEl().dom);
44290 this.updateTitle(p.getTitle());
44292 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44293 this.setActivePanel(p);
44295 panel.setRegion(null);
44296 if(this.activePanel == panel){
44297 this.activePanel = null;
44299 if(this.config.autoDestroy !== false && preservePanel !== true){
44300 try{panel.destroy();}catch(e){}
44302 this.fireEvent("panelremoved", this, panel);
44307 * Returns the TabPanel component used by this region
44308 * @return {Roo.TabPanel}
44310 getTabs : function(){
44314 createTool : function(parentEl, className){
44315 var btn = Roo.DomHelper.append(parentEl, {
44317 cls: "x-layout-tools-button",
44320 cls: "roo-layout-tools-button-inner " + className,
44324 btn.addClassOnOver("roo-layout-tools-button-over");
44329 * Ext JS Library 1.1.1
44330 * Copyright(c) 2006-2007, Ext JS, LLC.
44332 * Originally Released Under LGPL - original licence link has changed is not relivant.
44335 * <script type="text/javascript">
44341 * @class Roo.SplitLayoutRegion
44342 * @extends Roo.LayoutRegion
44343 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44345 Roo.bootstrap.layout.Split = function(config){
44346 this.cursor = config.cursor;
44347 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
44350 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
44352 splitTip : "Drag to resize.",
44353 collapsibleSplitTip : "Drag to resize. Double click to hide.",
44354 useSplitTips : false,
44356 applyConfig : function(config){
44357 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
44360 onRender : function(ctr,pos) {
44362 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
44363 if(!this.config.split){
44368 var splitEl = Roo.DomHelper.append(ctr.dom, {
44370 id: this.el.id + "-split",
44371 cls: "roo-layout-split roo-layout-split-"+this.position,
44374 /** The SplitBar for this region
44375 * @type Roo.SplitBar */
44376 // does not exist yet...
44377 Roo.log([this.position, this.orientation]);
44379 this.split = new Roo.bootstrap.SplitBar({
44380 dragElement : splitEl,
44381 resizingElement: this.el,
44382 orientation : this.orientation
44385 this.split.on("moved", this.onSplitMove, this);
44386 this.split.useShim = this.config.useShim === true;
44387 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44388 if(this.useSplitTips){
44389 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44391 //if(config.collapsible){
44392 // this.split.el.on("dblclick", this.collapse, this);
44395 if(typeof this.config.minSize != "undefined"){
44396 this.split.minSize = this.config.minSize;
44398 if(typeof this.config.maxSize != "undefined"){
44399 this.split.maxSize = this.config.maxSize;
44401 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
44402 this.hideSplitter();
44407 getHMaxSize : function(){
44408 var cmax = this.config.maxSize || 10000;
44409 var center = this.mgr.getRegion("center");
44410 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44413 getVMaxSize : function(){
44414 var cmax = this.config.maxSize || 10000;
44415 var center = this.mgr.getRegion("center");
44416 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44419 onSplitMove : function(split, newSize){
44420 this.fireEvent("resized", this, newSize);
44424 * Returns the {@link Roo.SplitBar} for this region.
44425 * @return {Roo.SplitBar}
44427 getSplitBar : function(){
44432 this.hideSplitter();
44433 Roo.bootstrap.layout.Split.superclass.hide.call(this);
44436 hideSplitter : function(){
44438 this.split.el.setLocation(-2000,-2000);
44439 this.split.el.hide();
44445 this.split.el.show();
44447 Roo.bootstrap.layout.Split.superclass.show.call(this);
44450 beforeSlide: function(){
44451 if(Roo.isGecko){// firefox overflow auto bug workaround
44452 this.bodyEl.clip();
44454 this.tabs.bodyEl.clip();
44456 if(this.activePanel){
44457 this.activePanel.getEl().clip();
44459 if(this.activePanel.beforeSlide){
44460 this.activePanel.beforeSlide();
44466 afterSlide : function(){
44467 if(Roo.isGecko){// firefox overflow auto bug workaround
44468 this.bodyEl.unclip();
44470 this.tabs.bodyEl.unclip();
44472 if(this.activePanel){
44473 this.activePanel.getEl().unclip();
44474 if(this.activePanel.afterSlide){
44475 this.activePanel.afterSlide();
44481 initAutoHide : function(){
44482 if(this.autoHide !== false){
44483 if(!this.autoHideHd){
44484 var st = new Roo.util.DelayedTask(this.slideIn, this);
44485 this.autoHideHd = {
44486 "mouseout": function(e){
44487 if(!e.within(this.el, true)){
44491 "mouseover" : function(e){
44497 this.el.on(this.autoHideHd);
44501 clearAutoHide : function(){
44502 if(this.autoHide !== false){
44503 this.el.un("mouseout", this.autoHideHd.mouseout);
44504 this.el.un("mouseover", this.autoHideHd.mouseover);
44508 clearMonitor : function(){
44509 Roo.get(document).un("click", this.slideInIf, this);
44512 // these names are backwards but not changed for compat
44513 slideOut : function(){
44514 if(this.isSlid || this.el.hasActiveFx()){
44517 this.isSlid = true;
44518 if(this.collapseBtn){
44519 this.collapseBtn.hide();
44521 this.closeBtnState = this.closeBtn.getStyle('display');
44522 this.closeBtn.hide();
44524 this.stickBtn.show();
44527 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44528 this.beforeSlide();
44529 this.el.setStyle("z-index", 10001);
44530 this.el.slideIn(this.getSlideAnchor(), {
44531 callback: function(){
44533 this.initAutoHide();
44534 Roo.get(document).on("click", this.slideInIf, this);
44535 this.fireEvent("slideshow", this);
44542 afterSlideIn : function(){
44543 this.clearAutoHide();
44544 this.isSlid = false;
44545 this.clearMonitor();
44546 this.el.setStyle("z-index", "");
44547 if(this.collapseBtn){
44548 this.collapseBtn.show();
44550 this.closeBtn.setStyle('display', this.closeBtnState);
44552 this.stickBtn.hide();
44554 this.fireEvent("slidehide", this);
44557 slideIn : function(cb){
44558 if(!this.isSlid || this.el.hasActiveFx()){
44562 this.isSlid = false;
44563 this.beforeSlide();
44564 this.el.slideOut(this.getSlideAnchor(), {
44565 callback: function(){
44566 this.el.setLeftTop(-10000, -10000);
44568 this.afterSlideIn();
44576 slideInIf : function(e){
44577 if(!e.within(this.el)){
44582 animateCollapse : function(){
44583 this.beforeSlide();
44584 this.el.setStyle("z-index", 20000);
44585 var anchor = this.getSlideAnchor();
44586 this.el.slideOut(anchor, {
44587 callback : function(){
44588 this.el.setStyle("z-index", "");
44589 this.collapsedEl.slideIn(anchor, {duration:.3});
44591 this.el.setLocation(-10000,-10000);
44593 this.fireEvent("collapsed", this);
44600 animateExpand : function(){
44601 this.beforeSlide();
44602 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44603 this.el.setStyle("z-index", 20000);
44604 this.collapsedEl.hide({
44607 this.el.slideIn(this.getSlideAnchor(), {
44608 callback : function(){
44609 this.el.setStyle("z-index", "");
44612 this.split.el.show();
44614 this.fireEvent("invalidated", this);
44615 this.fireEvent("expanded", this);
44643 getAnchor : function(){
44644 return this.anchors[this.position];
44647 getCollapseAnchor : function(){
44648 return this.canchors[this.position];
44651 getSlideAnchor : function(){
44652 return this.sanchors[this.position];
44655 getAlignAdj : function(){
44656 var cm = this.cmargins;
44657 switch(this.position){
44673 getExpandAdj : function(){
44674 var c = this.collapsedEl, cm = this.cmargins;
44675 switch(this.position){
44677 return [-(cm.right+c.getWidth()+cm.left), 0];
44680 return [cm.right+c.getWidth()+cm.left, 0];
44683 return [0, -(cm.top+cm.bottom+c.getHeight())];
44686 return [0, cm.top+cm.bottom+c.getHeight()];
44692 * Ext JS Library 1.1.1
44693 * Copyright(c) 2006-2007, Ext JS, LLC.
44695 * Originally Released Under LGPL - original licence link has changed is not relivant.
44698 * <script type="text/javascript">
44701 * These classes are private internal classes
44703 Roo.bootstrap.layout.Center = function(config){
44704 config.region = "center";
44705 Roo.bootstrap.layout.Region.call(this, config);
44706 this.visible = true;
44707 this.minWidth = config.minWidth || 20;
44708 this.minHeight = config.minHeight || 20;
44711 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
44713 // center panel can't be hidden
44717 // center panel can't be hidden
44720 getMinWidth: function(){
44721 return this.minWidth;
44724 getMinHeight: function(){
44725 return this.minHeight;
44739 Roo.bootstrap.layout.North = function(config)
44741 config.region = 'north';
44742 config.cursor = 'n-resize';
44744 Roo.bootstrap.layout.Split.call(this, config);
44748 this.split.placement = Roo.bootstrap.SplitBar.TOP;
44749 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44750 this.split.el.addClass("roo-layout-split-v");
44752 //var size = config.initialSize || config.height;
44753 //if(this.el && typeof size != "undefined"){
44754 // this.el.setHeight(size);
44757 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
44759 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44762 onRender : function(ctr, pos)
44764 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44765 var size = this.config.initialSize || this.config.height;
44766 if(this.el && typeof size != "undefined"){
44767 this.el.setHeight(size);
44772 getBox : function(){
44773 if(this.collapsed){
44774 return this.collapsedEl.getBox();
44776 var box = this.el.getBox();
44778 box.height += this.split.el.getHeight();
44783 updateBox : function(box){
44784 if(this.split && !this.collapsed){
44785 box.height -= this.split.el.getHeight();
44786 this.split.el.setLeft(box.x);
44787 this.split.el.setTop(box.y+box.height);
44788 this.split.el.setWidth(box.width);
44790 if(this.collapsed){
44791 this.updateBody(box.width, null);
44793 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44801 Roo.bootstrap.layout.South = function(config){
44802 config.region = 'south';
44803 config.cursor = 's-resize';
44804 Roo.bootstrap.layout.Split.call(this, config);
44806 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
44807 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
44808 this.split.el.addClass("roo-layout-split-v");
44813 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
44814 orientation: Roo.bootstrap.SplitBar.VERTICAL,
44816 onRender : function(ctr, pos)
44818 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44819 var size = this.config.initialSize || this.config.height;
44820 if(this.el && typeof size != "undefined"){
44821 this.el.setHeight(size);
44826 getBox : function(){
44827 if(this.collapsed){
44828 return this.collapsedEl.getBox();
44830 var box = this.el.getBox();
44832 var sh = this.split.el.getHeight();
44839 updateBox : function(box){
44840 if(this.split && !this.collapsed){
44841 var sh = this.split.el.getHeight();
44844 this.split.el.setLeft(box.x);
44845 this.split.el.setTop(box.y-sh);
44846 this.split.el.setWidth(box.width);
44848 if(this.collapsed){
44849 this.updateBody(box.width, null);
44851 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44855 Roo.bootstrap.layout.East = function(config){
44856 config.region = "east";
44857 config.cursor = "e-resize";
44858 Roo.bootstrap.layout.Split.call(this, config);
44860 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
44861 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44862 this.split.el.addClass("roo-layout-split-h");
44866 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
44867 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44869 onRender : function(ctr, pos)
44871 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
44872 var size = this.config.initialSize || this.config.width;
44873 if(this.el && typeof size != "undefined"){
44874 this.el.setWidth(size);
44879 getBox : function(){
44880 if(this.collapsed){
44881 return this.collapsedEl.getBox();
44883 var box = this.el.getBox();
44885 var sw = this.split.el.getWidth();
44892 updateBox : function(box){
44893 if(this.split && !this.collapsed){
44894 var sw = this.split.el.getWidth();
44896 this.split.el.setLeft(box.x);
44897 this.split.el.setTop(box.y);
44898 this.split.el.setHeight(box.height);
44901 if(this.collapsed){
44902 this.updateBody(null, box.height);
44904 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44908 Roo.bootstrap.layout.West = function(config){
44909 config.region = "west";
44910 config.cursor = "w-resize";
44912 Roo.bootstrap.layout.Split.call(this, config);
44914 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
44915 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
44916 this.split.el.addClass("roo-layout-split-h");
44920 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
44921 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
44923 onRender: function(ctr, pos)
44925 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
44926 var size = this.config.initialSize || this.config.width;
44927 if(typeof size != "undefined"){
44928 this.el.setWidth(size);
44932 getBox : function(){
44933 if(this.collapsed){
44934 return this.collapsedEl.getBox();
44936 var box = this.el.getBox();
44937 if (box.width == 0) {
44938 box.width = this.config.width; // kludge?
44941 box.width += this.split.el.getWidth();
44946 updateBox : function(box){
44947 if(this.split && !this.collapsed){
44948 var sw = this.split.el.getWidth();
44950 this.split.el.setLeft(box.x+box.width);
44951 this.split.el.setTop(box.y);
44952 this.split.el.setHeight(box.height);
44954 if(this.collapsed){
44955 this.updateBody(null, box.height);
44957 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
44961 * Ext JS Library 1.1.1
44962 * Copyright(c) 2006-2007, Ext JS, LLC.
44964 * Originally Released Under LGPL - original licence link has changed is not relivant.
44967 * <script type="text/javascript">
44970 * @class Roo.bootstrap.paenl.Content
44971 * @extends Roo.util.Observable
44972 * @children Roo.bootstrap.Component
44973 * @parent builder Roo.bootstrap.layout.Border
44974 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
44975 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
44976 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
44977 * @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
44978 * @cfg {Boolean} closable True if the panel can be closed/removed
44979 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44980 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44981 * @cfg {Toolbar} toolbar A toolbar for this panel
44982 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44983 * @cfg {String} title The title for this panel
44984 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44985 * @cfg {String} url Calls {@link #setUrl} with this value
44986 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44987 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44988 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44989 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
44990 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
44991 * @cfg {Boolean} badges render the badges
44992 * @cfg {String} cls extra classes to use
44993 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
44996 * Create a new ContentPanel.
44997 * @param {String/Object} config A string to set only the title or a config object
45000 Roo.bootstrap.panel.Content = function( config){
45002 this.tpl = config.tpl || false;
45004 var el = config.el;
45005 var content = config.content;
45007 if(config.autoCreate){ // xtype is available if this is called from factory
45010 this.el = Roo.get(el);
45011 if(!this.el && config && config.autoCreate){
45012 if(typeof config.autoCreate == "object"){
45013 if(!config.autoCreate.id){
45014 config.autoCreate.id = config.id||el;
45016 this.el = Roo.DomHelper.append(document.body,
45017 config.autoCreate, true);
45021 cls: (config.cls || '') +
45022 (config.background ? ' bg-' + config.background : '') +
45023 " roo-layout-inactive-content",
45026 if (config.iframe) {
45030 style : 'border: 0px',
45031 src : 'about:blank'
45037 elcfg.html = config.html;
45041 this.el = Roo.DomHelper.append(document.body, elcfg , true);
45042 if (config.iframe) {
45043 this.iframeEl = this.el.select('iframe',true).first();
45048 this.closable = false;
45049 this.loaded = false;
45050 this.active = false;
45053 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
45055 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
45057 this.wrapEl = this.el; //this.el.wrap();
45059 if (config.toolbar.items) {
45060 ti = config.toolbar.items ;
45061 delete config.toolbar.items ;
45065 this.toolbar.render(this.wrapEl, 'before');
45066 for(var i =0;i < ti.length;i++) {
45067 // Roo.log(['add child', items[i]]);
45068 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45070 this.toolbar.items = nitems;
45071 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
45072 delete config.toolbar;
45076 // xtype created footer. - not sure if will work as we normally have to render first..
45077 if (this.footer && !this.footer.el && this.footer.xtype) {
45078 if (!this.wrapEl) {
45079 this.wrapEl = this.el.wrap();
45082 this.footer.container = this.wrapEl.createChild();
45084 this.footer = Roo.factory(this.footer, Roo);
45089 if(typeof config == "string"){
45090 this.title = config;
45092 Roo.apply(this, config);
45096 this.resizeEl = Roo.get(this.resizeEl, true);
45098 this.resizeEl = this.el;
45100 // handle view.xtype
45108 * Fires when this panel is activated.
45109 * @param {Roo.ContentPanel} this
45113 * @event deactivate
45114 * Fires when this panel is activated.
45115 * @param {Roo.ContentPanel} this
45117 "deactivate" : true,
45121 * Fires when this panel is resized if fitToFrame is true.
45122 * @param {Roo.ContentPanel} this
45123 * @param {Number} width The width after any component adjustments
45124 * @param {Number} height The height after any component adjustments
45130 * Fires when this tab is created
45131 * @param {Roo.ContentPanel} this
45137 * Fires when this content is scrolled
45138 * @param {Roo.ContentPanel} this
45139 * @param {Event} scrollEvent
45150 if(this.autoScroll && !this.iframe){
45151 this.resizeEl.setStyle("overflow", "auto");
45152 this.resizeEl.on('scroll', this.onScroll, this);
45154 // fix randome scrolling
45155 //this.el.on('scroll', function() {
45156 // Roo.log('fix random scolling');
45157 // this.scrollTo('top',0);
45160 content = content || this.content;
45162 this.setContent(content);
45164 if(config && config.url){
45165 this.setUrl(this.url, this.params, this.loadOnce);
45170 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
45172 if (this.view && typeof(this.view.xtype) != 'undefined') {
45173 this.view.el = this.el.appendChild(document.createElement("div"));
45174 this.view = Roo.factory(this.view);
45175 this.view.render && this.view.render(false, '');
45179 this.fireEvent('render', this);
45182 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
45192 /* Resize Element - use this to work out scroll etc. */
45195 setRegion : function(region){
45196 this.region = region;
45197 this.setActiveClass(region && !this.background);
45201 setActiveClass: function(state)
45204 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
45205 this.el.setStyle('position','relative');
45207 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
45208 this.el.setStyle('position', 'absolute');
45213 * Returns the toolbar for this Panel if one was configured.
45214 * @return {Roo.Toolbar}
45216 getToolbar : function(){
45217 return this.toolbar;
45220 setActiveState : function(active)
45222 this.active = active;
45223 this.setActiveClass(active);
45225 if(this.fireEvent("deactivate", this) === false){
45230 this.fireEvent("activate", this);
45234 * Updates this panel's element (not for iframe)
45235 * @param {String} content The new content
45236 * @param {Boolean} loadScripts (optional) true to look for and process scripts
45238 setContent : function(content, loadScripts){
45243 this.el.update(content, loadScripts);
45246 ignoreResize : function(w, h)
45248 //return false; // always resize?
45249 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45252 this.lastSize = {width: w, height: h};
45257 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45258 * @return {Roo.UpdateManager} The UpdateManager
45260 getUpdateManager : function(){
45264 return this.el.getUpdateManager();
45267 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45268 * Does not work with IFRAME contents
45269 * @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:
45272 url: "your-url.php",
45273 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45274 callback: yourFunction,
45275 scope: yourObject, //(optional scope)
45278 text: "Loading...",
45284 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45285 * 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.
45286 * @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}
45287 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45288 * @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.
45289 * @return {Roo.ContentPanel} this
45297 var um = this.el.getUpdateManager();
45298 um.update.apply(um, arguments);
45304 * 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.
45305 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45306 * @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)
45307 * @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)
45308 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
45310 setUrl : function(url, params, loadOnce){
45312 this.iframeEl.dom.src = url;
45316 if(this.refreshDelegate){
45317 this.removeListener("activate", this.refreshDelegate);
45319 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45320 this.on("activate", this.refreshDelegate);
45321 return this.el.getUpdateManager();
45324 _handleRefresh : function(url, params, loadOnce){
45325 if(!loadOnce || !this.loaded){
45326 var updater = this.el.getUpdateManager();
45327 updater.update(url, params, this._setLoaded.createDelegate(this));
45331 _setLoaded : function(){
45332 this.loaded = true;
45336 * Returns this panel's id
45339 getId : function(){
45344 * Returns this panel's element - used by regiosn to add.
45345 * @return {Roo.Element}
45347 getEl : function(){
45348 return this.wrapEl || this.el;
45353 adjustForComponents : function(width, height)
45355 //Roo.log('adjustForComponents ');
45356 if(this.resizeEl != this.el){
45357 width -= this.el.getFrameWidth('lr');
45358 height -= this.el.getFrameWidth('tb');
45361 var te = this.toolbar.getEl();
45362 te.setWidth(width);
45363 height -= te.getHeight();
45366 var te = this.footer.getEl();
45367 te.setWidth(width);
45368 height -= te.getHeight();
45372 if(this.adjustments){
45373 width += this.adjustments[0];
45374 height += this.adjustments[1];
45376 return {"width": width, "height": height};
45379 setSize : function(width, height){
45380 if(this.fitToFrame && !this.ignoreResize(width, height)){
45381 if(this.fitContainer && this.resizeEl != this.el){
45382 this.el.setSize(width, height);
45384 var size = this.adjustForComponents(width, height);
45386 this.iframeEl.setSize(width,height);
45389 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45390 this.fireEvent('resize', this, size.width, size.height);
45397 * Returns this panel's title
45400 getTitle : function(){
45402 if (typeof(this.title) != 'object') {
45407 for (var k in this.title) {
45408 if (!this.title.hasOwnProperty(k)) {
45412 if (k.indexOf('-') >= 0) {
45413 var s = k.split('-');
45414 for (var i = 0; i<s.length; i++) {
45415 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
45418 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
45425 * Set this panel's title
45426 * @param {String} title
45428 setTitle : function(title){
45429 this.title = title;
45431 this.region.updatePanelTitle(this, title);
45436 * Returns true is this panel was configured to be closable
45437 * @return {Boolean}
45439 isClosable : function(){
45440 return this.closable;
45443 beforeSlide : function(){
45445 this.resizeEl.clip();
45448 afterSlide : function(){
45450 this.resizeEl.unclip();
45454 * Force a content refresh from the URL specified in the {@link #setUrl} method.
45455 * Will fail silently if the {@link #setUrl} method has not been called.
45456 * This does not activate the panel, just updates its content.
45458 refresh : function(){
45459 if(this.refreshDelegate){
45460 this.loaded = false;
45461 this.refreshDelegate();
45466 * Destroys this panel
45468 destroy : function(){
45469 this.el.removeAllListeners();
45470 var tempEl = document.createElement("span");
45471 tempEl.appendChild(this.el.dom);
45472 tempEl.innerHTML = "";
45478 * form - if the content panel contains a form - this is a reference to it.
45479 * @type {Roo.form.Form}
45483 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
45484 * This contains a reference to it.
45490 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45500 * @param {Object} cfg Xtype definition of item to add.
45504 getChildContainer: function () {
45505 return this.getEl();
45509 onScroll : function(e)
45511 this.fireEvent('scroll', this, e);
45516 var ret = new Roo.factory(cfg);
45521 if (cfg.xtype.match(/^Form$/)) {
45524 //if (this.footer) {
45525 // el = this.footer.container.insertSibling(false, 'before');
45527 el = this.el.createChild();
45530 this.form = new Roo.form.Form(cfg);
45533 if ( this.form.allItems.length) {
45534 this.form.render(el.dom);
45538 // should only have one of theses..
45539 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
45540 // views.. should not be just added - used named prop 'view''
45542 cfg.el = this.el.appendChild(document.createElement("div"));
45545 var ret = new Roo.factory(cfg);
45547 ret.render && ret.render(false, ''); // render blank..
45557 * @class Roo.bootstrap.panel.Grid
45558 * @extends Roo.bootstrap.panel.Content
45560 * Create a new GridPanel.
45561 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
45562 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
45563 * @param {Object} config A the config object
45569 Roo.bootstrap.panel.Grid = function(config)
45573 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45574 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
45576 config.el = this.wrapper;
45577 //this.el = this.wrapper;
45579 if (config.container) {
45580 // ctor'ed from a Border/panel.grid
45583 this.wrapper.setStyle("overflow", "hidden");
45584 this.wrapper.addClass('roo-grid-container');
45589 if(config.toolbar){
45590 var tool_el = this.wrapper.createChild();
45591 this.toolbar = Roo.factory(config.toolbar);
45593 if (config.toolbar.items) {
45594 ti = config.toolbar.items ;
45595 delete config.toolbar.items ;
45599 this.toolbar.render(tool_el);
45600 for(var i =0;i < ti.length;i++) {
45601 // Roo.log(['add child', items[i]]);
45602 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
45604 this.toolbar.items = nitems;
45606 delete config.toolbar;
45609 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
45610 config.grid.scrollBody = true;;
45611 config.grid.monitorWindowResize = false; // turn off autosizing
45612 config.grid.autoHeight = false;
45613 config.grid.autoWidth = false;
45615 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
45617 if (config.background) {
45618 // render grid on panel activation (if panel background)
45619 this.on('activate', function(gp) {
45620 if (!gp.grid.rendered) {
45621 gp.grid.render(this.wrapper);
45622 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45627 this.grid.render(this.wrapper);
45628 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
45631 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
45632 // ??? needed ??? config.el = this.wrapper;
45637 // xtype created footer. - not sure if will work as we normally have to render first..
45638 if (this.footer && !this.footer.el && this.footer.xtype) {
45640 var ctr = this.grid.getView().getFooterPanel(true);
45641 this.footer.dataSource = this.grid.dataSource;
45642 this.footer = Roo.factory(this.footer, Roo);
45643 this.footer.render(ctr);
45653 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
45656 getId : function(){
45657 return this.grid.id;
45661 * Returns the grid for this panel
45662 * @return {Roo.bootstrap.Table}
45664 getGrid : function(){
45668 setSize : function(width, height)
45671 //if(!this.ignoreResize(width, height)){
45672 var grid = this.grid;
45673 var size = this.adjustForComponents(width, height);
45674 // tfoot is not a footer?
45677 var gridel = grid.getGridEl();
45678 gridel.setSize(size.width, size.height);
45680 var tbd = grid.getGridEl().select('tbody', true).first();
45681 var thd = grid.getGridEl().select('thead',true).first();
45682 var tbf= grid.getGridEl().select('tfoot', true).first();
45685 size.height -= tbf.getHeight();
45688 size.height -= thd.getHeight();
45691 tbd.setSize(size.width, size.height );
45692 // this is for the account management tab -seems to work there.
45693 var thd = grid.getGridEl().select('thead',true).first();
45695 // tbd.setSize(size.width, size.height - thd.getHeight());
45705 beforeSlide : function(){
45706 this.grid.getView().scroller.clip();
45709 afterSlide : function(){
45710 this.grid.getView().scroller.unclip();
45713 destroy : function(){
45714 this.grid.destroy();
45716 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
45721 * @class Roo.bootstrap.panel.Nest
45722 * @extends Roo.bootstrap.panel.Content
45724 * Create a new Panel, that can contain a layout.Border.
45727 * @param {String/Object} config A string to set only the title or a config object
45729 Roo.bootstrap.panel.Nest = function(config)
45731 // construct with only one argument..
45732 /* FIXME - implement nicer consturctors
45733 if (layout.layout) {
45735 layout = config.layout;
45736 delete config.layout;
45738 if (layout.xtype && !layout.getEl) {
45739 // then layout needs constructing..
45740 layout = Roo.factory(layout, Roo);
45744 config.el = config.layout.getEl();
45746 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
45748 config.layout.monitorWindowResize = false; // turn off autosizing
45749 this.layout = config.layout;
45750 this.layout.getEl().addClass("roo-layout-nested-layout");
45751 this.layout.parent = this;
45758 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
45760 * @cfg {Roo.BorderLayout} layout The layout for this panel
45764 setSize : function(width, height){
45765 if(!this.ignoreResize(width, height)){
45766 var size = this.adjustForComponents(width, height);
45767 var el = this.layout.getEl();
45768 if (size.height < 1) {
45769 el.setWidth(size.width);
45771 el.setSize(size.width, size.height);
45773 var touch = el.dom.offsetWidth;
45774 this.layout.layout();
45775 // ie requires a double layout on the first pass
45776 if(Roo.isIE && !this.initialized){
45777 this.initialized = true;
45778 this.layout.layout();
45783 // activate all subpanels if not currently active..
45785 setActiveState : function(active){
45786 this.active = active;
45787 this.setActiveClass(active);
45790 this.fireEvent("deactivate", this);
45794 this.fireEvent("activate", this);
45795 // not sure if this should happen before or after..
45796 if (!this.layout) {
45797 return; // should not happen..
45800 for (var r in this.layout.regions) {
45801 reg = this.layout.getRegion(r);
45802 if (reg.getActivePanel()) {
45803 //reg.showPanel(reg.getActivePanel()); // force it to activate..
45804 reg.setActivePanel(reg.getActivePanel());
45807 if (!reg.panels.length) {
45810 reg.showPanel(reg.getPanel(0));
45819 * Returns the nested BorderLayout for this panel
45820 * @return {Roo.BorderLayout}
45822 getLayout : function(){
45823 return this.layout;
45827 * Adds a xtype elements to the layout of the nested panel
45831 xtype : 'ContentPanel',
45838 xtype : 'NestedLayoutPanel',
45844 items : [ ... list of content panels or nested layout panels.. ]
45848 * @param {Object} cfg Xtype definition of item to add.
45850 addxtype : function(cfg) {
45851 return this.layout.addxtype(cfg);
45856 * Ext JS Library 1.1.1
45857 * Copyright(c) 2006-2007, Ext JS, LLC.
45859 * Originally Released Under LGPL - original licence link has changed is not relivant.
45862 * <script type="text/javascript">
45865 * @class Roo.TabPanel
45866 * @extends Roo.util.Observable
45867 * A lightweight tab container.
45871 // basic tabs 1, built from existing content
45872 var tabs = new Roo.TabPanel("tabs1");
45873 tabs.addTab("script", "View Script");
45874 tabs.addTab("markup", "View Markup");
45875 tabs.activate("script");
45877 // more advanced tabs, built from javascript
45878 var jtabs = new Roo.TabPanel("jtabs");
45879 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
45881 // set up the UpdateManager
45882 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
45883 var updater = tab2.getUpdateManager();
45884 updater.setDefaultUrl("ajax1.htm");
45885 tab2.on('activate', updater.refresh, updater, true);
45887 // Use setUrl for Ajax loading
45888 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
45889 tab3.setUrl("ajax2.htm", null, true);
45892 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
45895 jtabs.activate("jtabs-1");
45898 * Create a new TabPanel.
45899 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
45900 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
45902 Roo.bootstrap.panel.Tabs = function(config){
45904 * The container element for this TabPanel.
45905 * @type Roo.Element
45907 this.el = Roo.get(config.el);
45910 if(typeof config == "boolean"){
45911 this.tabPosition = config ? "bottom" : "top";
45913 Roo.apply(this, config);
45917 if(this.tabPosition == "bottom"){
45918 // if tabs are at the bottom = create the body first.
45919 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45920 this.el.addClass("roo-tabs-bottom");
45922 // next create the tabs holders
45924 if (this.tabPosition == "west"){
45926 var reg = this.region; // fake it..
45928 if (!reg.mgr.parent) {
45931 reg = reg.mgr.parent.region;
45933 Roo.log("got nest?");
45935 if (reg.mgr.getRegion('west')) {
45936 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
45937 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
45938 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45939 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45940 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45948 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
45949 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
45950 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
45951 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
45956 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
45959 // finally - if tabs are at the top, then create the body last..
45960 if(this.tabPosition != "bottom"){
45961 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
45962 * @type Roo.Element
45964 this.bodyEl = Roo.get(this.createBody(this.el.dom));
45965 this.el.addClass("roo-tabs-top");
45969 this.bodyEl.setStyle("position", "relative");
45971 this.active = null;
45972 this.activateDelegate = this.activate.createDelegate(this);
45977 * Fires when the active tab changes
45978 * @param {Roo.TabPanel} this
45979 * @param {Roo.TabPanelItem} activePanel The new active tab
45983 * @event beforetabchange
45984 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
45985 * @param {Roo.TabPanel} this
45986 * @param {Object} e Set cancel to true on this object to cancel the tab change
45987 * @param {Roo.TabPanelItem} tab The tab being changed to
45989 "beforetabchange" : true
45992 Roo.EventManager.onWindowResize(this.onResize, this);
45993 this.cpad = this.el.getPadding("lr");
45994 this.hiddenCount = 0;
45997 // toolbar on the tabbar support...
45998 if (this.toolbar) {
45999 alert("no toolbar support yet");
46000 this.toolbar = false;
46002 var tcfg = this.toolbar;
46003 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
46004 this.toolbar = new Roo.Toolbar(tcfg);
46005 if (Roo.isSafari) {
46006 var tbl = tcfg.container.child('table', true);
46007 tbl.setAttribute('width', '100%');
46015 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
46018 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
46020 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
46022 tabPosition : "top",
46024 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
46026 currentTabWidth : 0,
46028 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
46032 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
46036 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
46038 preferredTabWidth : 175,
46040 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
46042 resizeTabs : false,
46044 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
46046 monitorResize : true,
46048 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
46050 toolbar : false, // set by caller..
46052 region : false, /// set by caller
46054 disableTooltips : true, // not used yet...
46057 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
46058 * @param {String} id The id of the div to use <b>or create</b>
46059 * @param {String} text The text for the tab
46060 * @param {String} content (optional) Content to put in the TabPanelItem body
46061 * @param {Boolean} closable (optional) True to create a close icon on the tab
46062 * @return {Roo.TabPanelItem} The created TabPanelItem
46064 addTab : function(id, text, content, closable, tpl)
46066 var item = new Roo.bootstrap.panel.TabItem({
46070 closable : closable,
46073 this.addTabItem(item);
46075 item.setContent(content);
46081 * Returns the {@link Roo.TabPanelItem} with the specified id/index
46082 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
46083 * @return {Roo.TabPanelItem}
46085 getTab : function(id){
46086 return this.items[id];
46090 * Hides the {@link Roo.TabPanelItem} with the specified id/index
46091 * @param {String/Number} id The id or index of the TabPanelItem to hide.
46093 hideTab : function(id){
46094 var t = this.items[id];
46097 this.hiddenCount++;
46098 this.autoSizeTabs();
46103 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
46104 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
46106 unhideTab : function(id){
46107 var t = this.items[id];
46109 t.setHidden(false);
46110 this.hiddenCount--;
46111 this.autoSizeTabs();
46116 * Adds an existing {@link Roo.TabPanelItem}.
46117 * @param {Roo.TabPanelItem} item The TabPanelItem to add
46119 addTabItem : function(item)
46121 this.items[item.id] = item;
46122 this.items.push(item);
46123 this.autoSizeTabs();
46124 // if(this.resizeTabs){
46125 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
46126 // this.autoSizeTabs();
46128 // item.autoSize();
46133 * Removes a {@link Roo.TabPanelItem}.
46134 * @param {String/Number} id The id or index of the TabPanelItem to remove.
46136 removeTab : function(id){
46137 var items = this.items;
46138 var tab = items[id];
46139 if(!tab) { return; }
46140 var index = items.indexOf(tab);
46141 if(this.active == tab && items.length > 1){
46142 var newTab = this.getNextAvailable(index);
46147 this.stripEl.dom.removeChild(tab.pnode.dom);
46148 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
46149 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
46151 items.splice(index, 1);
46152 delete this.items[tab.id];
46153 tab.fireEvent("close", tab);
46154 tab.purgeListeners();
46155 this.autoSizeTabs();
46158 getNextAvailable : function(start){
46159 var items = this.items;
46161 // look for a next tab that will slide over to
46162 // replace the one being removed
46163 while(index < items.length){
46164 var item = items[++index];
46165 if(item && !item.isHidden()){
46169 // if one isn't found select the previous tab (on the left)
46172 var item = items[--index];
46173 if(item && !item.isHidden()){
46181 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
46182 * @param {String/Number} id The id or index of the TabPanelItem to disable.
46184 disableTab : function(id){
46185 var tab = this.items[id];
46186 if(tab && this.active != tab){
46192 * Enables a {@link Roo.TabPanelItem} that is disabled.
46193 * @param {String/Number} id The id or index of the TabPanelItem to enable.
46195 enableTab : function(id){
46196 var tab = this.items[id];
46201 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
46202 * @param {String/Number} id The id or index of the TabPanelItem to activate.
46203 * @return {Roo.TabPanelItem} The TabPanelItem.
46205 activate : function(id)
46207 //Roo.log('activite:' + id);
46209 var tab = this.items[id];
46213 if(tab == this.active || tab.disabled){
46217 this.fireEvent("beforetabchange", this, e, tab);
46218 if(e.cancel !== true && !tab.disabled){
46220 this.active.hide();
46222 this.active = this.items[id];
46223 this.active.show();
46224 this.fireEvent("tabchange", this, this.active);
46230 * Gets the active {@link Roo.TabPanelItem}.
46231 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
46233 getActiveTab : function(){
46234 return this.active;
46238 * Updates the tab body element to fit the height of the container element
46239 * for overflow scrolling
46240 * @param {Number} targetHeight (optional) Override the starting height from the elements height
46242 syncHeight : function(targetHeight){
46243 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
46244 var bm = this.bodyEl.getMargins();
46245 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
46246 this.bodyEl.setHeight(newHeight);
46250 onResize : function(){
46251 if(this.monitorResize){
46252 this.autoSizeTabs();
46257 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
46259 beginUpdate : function(){
46260 this.updating = true;
46264 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
46266 endUpdate : function(){
46267 this.updating = false;
46268 this.autoSizeTabs();
46272 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
46274 autoSizeTabs : function()
46276 var count = this.items.length;
46277 var vcount = count - this.hiddenCount;
46280 this.stripEl.hide();
46282 this.stripEl.show();
46285 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
46290 var w = Math.max(this.el.getWidth() - this.cpad, 10);
46291 var availWidth = Math.floor(w / vcount);
46292 var b = this.stripBody;
46293 if(b.getWidth() > w){
46294 var tabs = this.items;
46295 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
46296 if(availWidth < this.minTabWidth){
46297 /*if(!this.sleft){ // incomplete scrolling code
46298 this.createScrollButtons();
46301 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
46304 if(this.currentTabWidth < this.preferredTabWidth){
46305 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
46311 * Returns the number of tabs in this TabPanel.
46314 getCount : function(){
46315 return this.items.length;
46319 * Resizes all the tabs to the passed width
46320 * @param {Number} The new width
46322 setTabWidth : function(width){
46323 this.currentTabWidth = width;
46324 for(var i = 0, len = this.items.length; i < len; i++) {
46325 if(!this.items[i].isHidden()) {
46326 this.items[i].setWidth(width);
46332 * Destroys this TabPanel
46333 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
46335 destroy : function(removeEl){
46336 Roo.EventManager.removeResizeListener(this.onResize, this);
46337 for(var i = 0, len = this.items.length; i < len; i++){
46338 this.items[i].purgeListeners();
46340 if(removeEl === true){
46341 this.el.update("");
46346 createStrip : function(container)
46348 var strip = document.createElement("nav");
46349 strip.className = Roo.bootstrap.version == 4 ?
46350 "navbar-light bg-light" :
46351 "navbar navbar-default"; //"x-tabs-wrap";
46352 container.appendChild(strip);
46356 createStripList : function(strip)
46358 // div wrapper for retard IE
46359 // returns the "tr" element.
46360 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
46361 //'<div class="x-tabs-strip-wrap">'+
46362 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
46363 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
46364 return strip.firstChild; //.firstChild.firstChild.firstChild;
46366 createBody : function(container)
46368 var body = document.createElement("div");
46369 Roo.id(body, "tab-body");
46370 //Roo.fly(body).addClass("x-tabs-body");
46371 Roo.fly(body).addClass("tab-content");
46372 container.appendChild(body);
46375 createItemBody :function(bodyEl, id){
46376 var body = Roo.getDom(id);
46378 body = document.createElement("div");
46381 //Roo.fly(body).addClass("x-tabs-item-body");
46382 Roo.fly(body).addClass("tab-pane");
46383 bodyEl.insertBefore(body, bodyEl.firstChild);
46387 createStripElements : function(stripEl, text, closable, tpl)
46389 var td = document.createElement("li"); // was td..
46390 td.className = 'nav-item';
46392 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
46395 stripEl.appendChild(td);
46397 td.className = "x-tabs-closable";
46398 if(!this.closeTpl){
46399 this.closeTpl = new Roo.Template(
46400 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46401 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
46402 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
46405 var el = this.closeTpl.overwrite(td, {"text": text});
46406 var close = el.getElementsByTagName("div")[0];
46407 var inner = el.getElementsByTagName("em")[0];
46408 return {"el": el, "close": close, "inner": inner};
46411 // not sure what this is..
46412 // if(!this.tabTpl){
46413 //this.tabTpl = new Roo.Template(
46414 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
46415 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
46417 // this.tabTpl = new Roo.Template(
46418 // '<a href="#">' +
46419 // '<span unselectable="on"' +
46420 // (this.disableTooltips ? '' : ' title="{text}"') +
46421 // ' >{text}</span></a>'
46427 var template = tpl || this.tabTpl || false;
46430 template = new Roo.Template(
46431 Roo.bootstrap.version == 4 ?
46433 '<a class="nav-link" href="#" unselectable="on"' +
46434 (this.disableTooltips ? '' : ' title="{text}"') +
46437 '<a class="nav-link" href="#">' +
46438 '<span unselectable="on"' +
46439 (this.disableTooltips ? '' : ' title="{text}"') +
46440 ' >{text}</span></a>'
46445 switch (typeof(template)) {
46449 template = new Roo.Template(template);
46455 var el = template.overwrite(td, {"text": text});
46457 var inner = el.getElementsByTagName("span")[0];
46459 return {"el": el, "inner": inner};
46467 * @class Roo.TabPanelItem
46468 * @extends Roo.util.Observable
46469 * Represents an individual item (tab plus body) in a TabPanel.
46470 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
46471 * @param {String} id The id of this TabPanelItem
46472 * @param {String} text The text for the tab of this TabPanelItem
46473 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
46475 Roo.bootstrap.panel.TabItem = function(config){
46477 * The {@link Roo.TabPanel} this TabPanelItem belongs to
46478 * @type Roo.TabPanel
46480 this.tabPanel = config.panel;
46482 * The id for this TabPanelItem
46485 this.id = config.id;
46487 this.disabled = false;
46489 this.text = config.text;
46491 this.loaded = false;
46492 this.closable = config.closable;
46495 * The body element for this TabPanelItem.
46496 * @type Roo.Element
46498 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
46499 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
46500 this.bodyEl.setStyle("display", "block");
46501 this.bodyEl.setStyle("zoom", "1");
46502 //this.hideAction();
46504 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
46506 this.el = Roo.get(els.el);
46507 this.inner = Roo.get(els.inner, true);
46508 this.textEl = Roo.bootstrap.version == 4 ?
46509 this.el : Roo.get(this.el.dom.firstChild, true);
46511 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
46512 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
46515 // this.el.on("mousedown", this.onTabMouseDown, this);
46516 this.el.on("click", this.onTabClick, this);
46518 if(config.closable){
46519 var c = Roo.get(els.close, true);
46520 c.dom.title = this.closeText;
46521 c.addClassOnOver("close-over");
46522 c.on("click", this.closeClick, this);
46528 * Fires when this tab becomes the active tab.
46529 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46530 * @param {Roo.TabPanelItem} this
46534 * @event beforeclose
46535 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
46536 * @param {Roo.TabPanelItem} this
46537 * @param {Object} e Set cancel to true on this object to cancel the close.
46539 "beforeclose": true,
46542 * Fires when this tab is closed.
46543 * @param {Roo.TabPanelItem} this
46547 * @event deactivate
46548 * Fires when this tab is no longer the active tab.
46549 * @param {Roo.TabPanel} tabPanel The parent TabPanel
46550 * @param {Roo.TabPanelItem} this
46552 "deactivate" : true
46554 this.hidden = false;
46556 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
46559 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
46561 purgeListeners : function(){
46562 Roo.util.Observable.prototype.purgeListeners.call(this);
46563 this.el.removeAllListeners();
46566 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
46569 this.status_node.addClass("active");
46572 this.tabPanel.stripWrap.repaint();
46574 this.fireEvent("activate", this.tabPanel, this);
46578 * Returns true if this tab is the active tab.
46579 * @return {Boolean}
46581 isActive : function(){
46582 return this.tabPanel.getActiveTab() == this;
46586 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
46589 this.status_node.removeClass("active");
46591 this.fireEvent("deactivate", this.tabPanel, this);
46594 hideAction : function(){
46595 this.bodyEl.hide();
46596 this.bodyEl.setStyle("position", "absolute");
46597 this.bodyEl.setLeft("-20000px");
46598 this.bodyEl.setTop("-20000px");
46601 showAction : function(){
46602 this.bodyEl.setStyle("position", "relative");
46603 this.bodyEl.setTop("");
46604 this.bodyEl.setLeft("");
46605 this.bodyEl.show();
46609 * Set the tooltip for the tab.
46610 * @param {String} tooltip The tab's tooltip
46612 setTooltip : function(text){
46613 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
46614 this.textEl.dom.qtip = text;
46615 this.textEl.dom.removeAttribute('title');
46617 this.textEl.dom.title = text;
46621 onTabClick : function(e){
46622 e.preventDefault();
46623 this.tabPanel.activate(this.id);
46626 onTabMouseDown : function(e){
46627 e.preventDefault();
46628 this.tabPanel.activate(this.id);
46631 getWidth : function(){
46632 return this.inner.getWidth();
46635 setWidth : function(width){
46636 var iwidth = width - this.linode.getPadding("lr");
46637 this.inner.setWidth(iwidth);
46638 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
46639 this.linode.setWidth(width);
46643 * Show or hide the tab
46644 * @param {Boolean} hidden True to hide or false to show.
46646 setHidden : function(hidden){
46647 this.hidden = hidden;
46648 this.linode.setStyle("display", hidden ? "none" : "");
46652 * Returns true if this tab is "hidden"
46653 * @return {Boolean}
46655 isHidden : function(){
46656 return this.hidden;
46660 * Returns the text for this tab
46663 getText : function(){
46667 autoSize : function(){
46668 //this.el.beginMeasure();
46669 this.textEl.setWidth(1);
46671 * #2804 [new] Tabs in Roojs
46672 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
46674 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
46675 //this.el.endMeasure();
46679 * Sets the text for the tab (Note: this also sets the tooltip text)
46680 * @param {String} text The tab's text and tooltip
46682 setText : function(text){
46684 this.textEl.update(text);
46685 this.setTooltip(text);
46686 //if(!this.tabPanel.resizeTabs){
46687 // this.autoSize();
46691 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
46693 activate : function(){
46694 this.tabPanel.activate(this.id);
46698 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
46700 disable : function(){
46701 if(this.tabPanel.active != this){
46702 this.disabled = true;
46703 this.status_node.addClass("disabled");
46708 * Enables this TabPanelItem if it was previously disabled.
46710 enable : function(){
46711 this.disabled = false;
46712 this.status_node.removeClass("disabled");
46716 * Sets the content for this TabPanelItem.
46717 * @param {String} content The content
46718 * @param {Boolean} loadScripts true to look for and load scripts
46720 setContent : function(content, loadScripts){
46721 this.bodyEl.update(content, loadScripts);
46725 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
46726 * @return {Roo.UpdateManager} The UpdateManager
46728 getUpdateManager : function(){
46729 return this.bodyEl.getUpdateManager();
46733 * Set a URL to be used to load the content for this TabPanelItem.
46734 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
46735 * @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)
46736 * @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)
46737 * @return {Roo.UpdateManager} The UpdateManager
46739 setUrl : function(url, params, loadOnce){
46740 if(this.refreshDelegate){
46741 this.un('activate', this.refreshDelegate);
46743 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
46744 this.on("activate", this.refreshDelegate);
46745 return this.bodyEl.getUpdateManager();
46749 _handleRefresh : function(url, params, loadOnce){
46750 if(!loadOnce || !this.loaded){
46751 var updater = this.bodyEl.getUpdateManager();
46752 updater.update(url, params, this._setLoaded.createDelegate(this));
46757 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
46758 * Will fail silently if the setUrl method has not been called.
46759 * This does not activate the panel, just updates its content.
46761 refresh : function(){
46762 if(this.refreshDelegate){
46763 this.loaded = false;
46764 this.refreshDelegate();
46769 _setLoaded : function(){
46770 this.loaded = true;
46774 closeClick : function(e){
46777 this.fireEvent("beforeclose", this, o);
46778 if(o.cancel !== true){
46779 this.tabPanel.removeTab(this.id);
46783 * The text displayed in the tooltip for the close icon.
46786 closeText : "Close this tab"
46789 * This script refer to:
46790 * Title: International Telephone Input
46791 * Author: Jack O'Connor
46792 * Code version: v12.1.12
46793 * Availability: https://github.com/jackocnr/intl-tel-input.git
46796 Roo.bootstrap.form.PhoneInputData = function() {
46799 "Afghanistan (افغانستان)",
46804 "Albania (Shqipëri)",
46809 "Algeria (الجزائر)",
46834 "Antigua and Barbuda",
46844 "Armenia (Հայաստան)",
46860 "Austria (Österreich)",
46865 "Azerbaijan (Azərbaycan)",
46875 "Bahrain (البحرين)",
46880 "Bangladesh (বাংলাদেশ)",
46890 "Belarus (Беларусь)",
46895 "Belgium (België)",
46925 "Bosnia and Herzegovina (Босна и Херцеговина)",
46940 "British Indian Ocean Territory",
46945 "British Virgin Islands",
46955 "Bulgaria (България)",
46965 "Burundi (Uburundi)",
46970 "Cambodia (កម្ពុជា)",
46975 "Cameroon (Cameroun)",
46984 ["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"]
46987 "Cape Verde (Kabu Verdi)",
46992 "Caribbean Netherlands",
47003 "Central African Republic (République centrafricaine)",
47023 "Christmas Island",
47029 "Cocos (Keeling) Islands",
47040 "Comoros (جزر القمر)",
47045 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
47050 "Congo (Republic) (Congo-Brazzaville)",
47070 "Croatia (Hrvatska)",
47091 "Czech Republic (Česká republika)",
47096 "Denmark (Danmark)",
47111 "Dominican Republic (República Dominicana)",
47115 ["809", "829", "849"]
47133 "Equatorial Guinea (Guinea Ecuatorial)",
47153 "Falkland Islands (Islas Malvinas)",
47158 "Faroe Islands (Føroyar)",
47179 "French Guiana (Guyane française)",
47184 "French Polynesia (Polynésie française)",
47199 "Georgia (საქართველო)",
47204 "Germany (Deutschland)",
47224 "Greenland (Kalaallit Nunaat)",
47261 "Guinea-Bissau (Guiné Bissau)",
47286 "Hungary (Magyarország)",
47291 "Iceland (Ísland)",
47311 "Iraq (العراق)",
47327 "Israel (ישראל)",
47354 "Jordan (الأردن)",
47359 "Kazakhstan (Казахстан)",
47380 "Kuwait (الكويت)",
47385 "Kyrgyzstan (Кыргызстан)",
47395 "Latvia (Latvija)",
47400 "Lebanon (لبنان)",
47415 "Libya (ليبيا)",
47425 "Lithuania (Lietuva)",
47440 "Macedonia (FYROM) (Македонија)",
47445 "Madagascar (Madagasikara)",
47475 "Marshall Islands",
47485 "Mauritania (موريتانيا)",
47490 "Mauritius (Moris)",
47511 "Moldova (Republica Moldova)",
47521 "Mongolia (Монгол)",
47526 "Montenegro (Crna Gora)",
47536 "Morocco (المغرب)",
47542 "Mozambique (Moçambique)",
47547 "Myanmar (Burma) (မြန်မာ)",
47552 "Namibia (Namibië)",
47567 "Netherlands (Nederland)",
47572 "New Caledonia (Nouvelle-Calédonie)",
47607 "North Korea (조선 민주주의 인민 공화국)",
47612 "Northern Mariana Islands",
47628 "Pakistan (پاکستان)",
47638 "Palestine (فلسطين)",
47648 "Papua New Guinea",
47690 "Réunion (La Réunion)",
47696 "Romania (România)",
47712 "Saint Barthélemy",
47723 "Saint Kitts and Nevis",
47733 "Saint Martin (Saint-Martin (partie française))",
47739 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
47744 "Saint Vincent and the Grenadines",
47759 "São Tomé and Príncipe (São Tomé e Príncipe)",
47764 "Saudi Arabia (المملكة العربية السعودية)",
47769 "Senegal (Sénégal)",
47799 "Slovakia (Slovensko)",
47804 "Slovenia (Slovenija)",
47814 "Somalia (Soomaaliya)",
47824 "South Korea (대한민국)",
47829 "South Sudan (جنوب السودان)",
47839 "Sri Lanka (ශ්රී ලංකාව)",
47844 "Sudan (السودان)",
47854 "Svalbard and Jan Mayen",
47865 "Sweden (Sverige)",
47870 "Switzerland (Schweiz)",
47875 "Syria (سوريا)",
47920 "Trinidad and Tobago",
47925 "Tunisia (تونس)",
47930 "Turkey (Türkiye)",
47940 "Turks and Caicos Islands",
47950 "U.S. Virgin Islands",
47960 "Ukraine (Україна)",
47965 "United Arab Emirates (الإمارات العربية المتحدة)",
47987 "Uzbekistan (Oʻzbekiston)",
47997 "Vatican City (Città del Vaticano)",
48008 "Vietnam (Việt Nam)",
48013 "Wallis and Futuna (Wallis-et-Futuna)",
48018 "Western Sahara (الصحراء الغربية)",
48024 "Yemen (اليمن)",
48048 * This script refer to:
48049 * Title: International Telephone Input
48050 * Author: Jack O'Connor
48051 * Code version: v12.1.12
48052 * Availability: https://github.com/jackocnr/intl-tel-input.git
48056 * @class Roo.bootstrap.form.PhoneInput
48057 * @extends Roo.bootstrap.form.TriggerField
48058 * An input with International dial-code selection
48060 * @cfg {String} defaultDialCode default '+852'
48061 * @cfg {Array} preferedCountries default []
48064 * Create a new PhoneInput.
48065 * @param {Object} config Configuration options
48068 Roo.bootstrap.form.PhoneInput = function(config) {
48069 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
48072 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
48074 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
48076 listWidth: undefined,
48078 selectedClass: 'active',
48080 invalidClass : "has-warning",
48082 validClass: 'has-success',
48084 allowed: '0123456789',
48089 * @cfg {String} defaultDialCode The default dial code when initializing the input
48091 defaultDialCode: '+852',
48094 * @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
48096 preferedCountries: false,
48098 getAutoCreate : function()
48100 var data = Roo.bootstrap.form.PhoneInputData();
48101 var align = this.labelAlign || this.parentLabelAlign();
48104 this.allCountries = [];
48105 this.dialCodeMapping = [];
48107 for (var i = 0; i < data.length; i++) {
48109 this.allCountries[i] = {
48113 priority: c[3] || 0,
48114 areaCodes: c[4] || null
48116 this.dialCodeMapping[c[2]] = {
48119 priority: c[3] || 0,
48120 areaCodes: c[4] || null
48132 // type: 'number', -- do not use number - we get the flaky up/down arrows.
48133 maxlength: this.max_length,
48134 cls : 'form-control tel-input',
48135 autocomplete: 'new-password'
48138 var hiddenInput = {
48141 cls: 'hidden-tel-input'
48145 hiddenInput.name = this.name;
48148 if (this.disabled) {
48149 input.disabled = true;
48152 var flag_container = {
48169 cls: this.hasFeedback ? 'has-feedback' : '',
48175 cls: 'dial-code-holder',
48182 cls: 'roo-select2-container input-group',
48189 if (this.fieldLabel.length) {
48192 tooltip: 'This field is required'
48198 cls: 'control-label',
48204 html: this.fieldLabel
48207 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48213 if(this.indicatorpos == 'right') {
48214 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48221 if(align == 'left') {
48229 if(this.labelWidth > 12){
48230 label.style = "width: " + this.labelWidth + 'px';
48232 if(this.labelWidth < 13 && this.labelmd == 0){
48233 this.labelmd = this.labelWidth;
48235 if(this.labellg > 0){
48236 label.cls += ' col-lg-' + this.labellg;
48237 input.cls += ' col-lg-' + (12 - this.labellg);
48239 if(this.labelmd > 0){
48240 label.cls += ' col-md-' + this.labelmd;
48241 container.cls += ' col-md-' + (12 - this.labelmd);
48243 if(this.labelsm > 0){
48244 label.cls += ' col-sm-' + this.labelsm;
48245 container.cls += ' col-sm-' + (12 - this.labelsm);
48247 if(this.labelxs > 0){
48248 label.cls += ' col-xs-' + this.labelxs;
48249 container.cls += ' col-xs-' + (12 - this.labelxs);
48259 var settings = this;
48261 ['xs','sm','md','lg'].map(function(size){
48262 if (settings[size]) {
48263 cfg.cls += ' col-' + size + '-' + settings[size];
48267 this.store = new Roo.data.Store({
48268 proxy : new Roo.data.MemoryProxy({}),
48269 reader : new Roo.data.JsonReader({
48280 'name' : 'dialCode',
48284 'name' : 'priority',
48288 'name' : 'areaCodes',
48295 if(!this.preferedCountries) {
48296 this.preferedCountries = [
48303 var p = this.preferedCountries.reverse();
48306 for (var i = 0; i < p.length; i++) {
48307 for (var j = 0; j < this.allCountries.length; j++) {
48308 if(this.allCountries[j].iso2 == p[i]) {
48309 var t = this.allCountries[j];
48310 this.allCountries.splice(j,1);
48311 this.allCountries.unshift(t);
48317 this.store.proxy.data = {
48319 data: this.allCountries
48325 initEvents : function()
48328 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
48330 this.indicator = this.indicatorEl();
48331 this.flag = this.flagEl();
48332 this.dialCodeHolder = this.dialCodeHolderEl();
48334 this.trigger = this.el.select('div.flag-box',true).first();
48335 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
48340 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48341 _this.list.setWidth(lw);
48344 this.list.on('mouseover', this.onViewOver, this);
48345 this.list.on('mousemove', this.onViewMove, this);
48346 this.inputEl().on("keyup", this.onKeyUp, this);
48347 this.inputEl().on("keypress", this.onKeyPress, this);
48349 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
48351 this.view = new Roo.View(this.list, this.tpl, {
48352 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48355 this.view.on('click', this.onViewClick, this);
48356 this.setValue(this.defaultDialCode);
48359 onTriggerClick : function(e)
48361 Roo.log('trigger click');
48366 if(this.isExpanded()){
48368 this.hasFocus = false;
48370 this.store.load({});
48371 this.hasFocus = true;
48376 isExpanded : function()
48378 return this.list.isVisible();
48381 collapse : function()
48383 if(!this.isExpanded()){
48387 Roo.get(document).un('mousedown', this.collapseIf, this);
48388 Roo.get(document).un('mousewheel', this.collapseIf, this);
48389 this.fireEvent('collapse', this);
48393 expand : function()
48397 if(this.isExpanded() || !this.hasFocus){
48401 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
48402 this.list.setWidth(lw);
48405 this.restrictHeight();
48407 Roo.get(document).on('mousedown', this.collapseIf, this);
48408 Roo.get(document).on('mousewheel', this.collapseIf, this);
48410 this.fireEvent('expand', this);
48413 restrictHeight : function()
48415 this.list.alignTo(this.inputEl(), this.listAlign);
48416 this.list.alignTo(this.inputEl(), this.listAlign);
48419 onViewOver : function(e, t)
48421 if(this.inKeyMode){
48424 var item = this.view.findItemFromChild(t);
48427 var index = this.view.indexOf(item);
48428 this.select(index, false);
48433 onViewClick : function(view, doFocus, el, e)
48435 var index = this.view.getSelectedIndexes()[0];
48437 var r = this.store.getAt(index);
48440 this.onSelect(r, index);
48442 if(doFocus !== false && !this.blockFocus){
48443 this.inputEl().focus();
48447 onViewMove : function(e, t)
48449 this.inKeyMode = false;
48452 select : function(index, scrollIntoView)
48454 this.selectedIndex = index;
48455 this.view.select(index);
48456 if(scrollIntoView !== false){
48457 var el = this.view.getNode(index);
48459 this.list.scrollChildIntoView(el, false);
48464 createList : function()
48466 this.list = Roo.get(document.body).createChild({
48468 cls: 'typeahead typeahead-long dropdown-menu tel-list',
48469 style: 'display:none'
48472 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
48475 collapseIf : function(e)
48477 var in_combo = e.within(this.el);
48478 var in_list = e.within(this.list);
48479 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
48481 if (in_combo || in_list || is_list) {
48487 onSelect : function(record, index)
48489 if(this.fireEvent('beforeselect', this, record, index) !== false){
48491 this.setFlagClass(record.data.iso2);
48492 this.setDialCode(record.data.dialCode);
48493 this.hasFocus = false;
48495 this.fireEvent('select', this, record, index);
48499 flagEl : function()
48501 var flag = this.el.select('div.flag',true).first();
48508 dialCodeHolderEl : function()
48510 var d = this.el.select('input.dial-code-holder',true).first();
48517 setDialCode : function(v)
48519 this.dialCodeHolder.dom.value = '+'+v;
48522 setFlagClass : function(n)
48524 this.flag.dom.className = 'flag '+n;
48527 getValue : function()
48529 var v = this.inputEl().getValue();
48530 if(this.dialCodeHolder) {
48531 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
48536 setValue : function(v)
48538 var d = this.getDialCode(v);
48540 //invalid dial code
48541 if(v.length == 0 || !d || d.length == 0) {
48543 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
48544 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
48550 this.setFlagClass(this.dialCodeMapping[d].iso2);
48551 this.setDialCode(d);
48552 this.inputEl().dom.value = v.replace('+'+d,'');
48553 this.hiddenEl().dom.value = this.getValue();
48558 getDialCode : function(v)
48562 if (v.length == 0) {
48563 return this.dialCodeHolder.dom.value;
48567 if (v.charAt(0) != "+") {
48570 var numericChars = "";
48571 for (var i = 1; i < v.length; i++) {
48572 var c = v.charAt(i);
48575 if (this.dialCodeMapping[numericChars]) {
48576 dialCode = v.substr(1, i);
48578 if (numericChars.length == 4) {
48588 this.setValue(this.defaultDialCode);
48592 hiddenEl : function()
48594 return this.el.select('input.hidden-tel-input',true).first();
48597 // after setting val
48598 onKeyUp : function(e){
48599 this.setValue(this.getValue());
48602 onKeyPress : function(e){
48603 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
48610 * @class Roo.bootstrap.form.MoneyField
48611 * @extends Roo.bootstrap.form.ComboBox
48612 * Bootstrap MoneyField class
48615 * Create a new MoneyField.
48616 * @param {Object} config Configuration options
48619 Roo.bootstrap.form.MoneyField = function(config) {
48621 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
48625 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
48628 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
48630 allowDecimals : true,
48632 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
48634 decimalSeparator : ".",
48636 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
48638 decimalPrecision : 0,
48640 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
48642 allowNegative : true,
48644 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
48648 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
48650 minValue : Number.NEGATIVE_INFINITY,
48652 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
48654 maxValue : Number.MAX_VALUE,
48656 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
48658 minText : "The minimum value for this field is {0}",
48660 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
48662 maxText : "The maximum value for this field is {0}",
48664 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
48665 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
48667 nanText : "{0} is not a valid number",
48669 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
48673 * @cfg {String} defaults currency of the MoneyField
48674 * value should be in lkey
48676 defaultCurrency : false,
48678 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
48680 thousandsDelimiter : false,
48682 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
48691 * @cfg {Roo.data.Store} store Store to lookup currency??
48695 getAutoCreate : function()
48697 var align = this.labelAlign || this.parentLabelAlign();
48709 cls : 'form-control roo-money-amount-input',
48710 autocomplete: 'new-password'
48713 var hiddenInput = {
48717 cls: 'hidden-number-input'
48720 if(this.max_length) {
48721 input.maxlength = this.max_length;
48725 hiddenInput.name = this.name;
48728 if (this.disabled) {
48729 input.disabled = true;
48732 var clg = 12 - this.inputlg;
48733 var cmd = 12 - this.inputmd;
48734 var csm = 12 - this.inputsm;
48735 var cxs = 12 - this.inputxs;
48739 cls : 'row roo-money-field',
48743 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
48747 cls: 'roo-select2-container input-group',
48751 cls : 'form-control roo-money-currency-input',
48752 autocomplete: 'new-password',
48754 name : this.currencyName
48758 cls : 'input-group-addon',
48772 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
48776 cls: this.hasFeedback ? 'has-feedback' : '',
48787 if (this.fieldLabel.length) {
48790 tooltip: 'This field is required'
48796 cls: 'control-label',
48802 html: this.fieldLabel
48805 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
48811 if(this.indicatorpos == 'right') {
48812 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
48819 if(align == 'left') {
48827 if(this.labelWidth > 12){
48828 label.style = "width: " + this.labelWidth + 'px';
48830 if(this.labelWidth < 13 && this.labelmd == 0){
48831 this.labelmd = this.labelWidth;
48833 if(this.labellg > 0){
48834 label.cls += ' col-lg-' + this.labellg;
48835 input.cls += ' col-lg-' + (12 - this.labellg);
48837 if(this.labelmd > 0){
48838 label.cls += ' col-md-' + this.labelmd;
48839 container.cls += ' col-md-' + (12 - this.labelmd);
48841 if(this.labelsm > 0){
48842 label.cls += ' col-sm-' + this.labelsm;
48843 container.cls += ' col-sm-' + (12 - this.labelsm);
48845 if(this.labelxs > 0){
48846 label.cls += ' col-xs-' + this.labelxs;
48847 container.cls += ' col-xs-' + (12 - this.labelxs);
48858 var settings = this;
48860 ['xs','sm','md','lg'].map(function(size){
48861 if (settings[size]) {
48862 cfg.cls += ' col-' + size + '-' + settings[size];
48869 initEvents : function()
48871 this.indicator = this.indicatorEl();
48873 this.initCurrencyEvent();
48875 this.initNumberEvent();
48878 initCurrencyEvent : function()
48881 throw "can not find store for combo";
48884 this.store = Roo.factory(this.store, Roo.data);
48885 this.store.parent = this;
48889 this.triggerEl = this.el.select('.input-group-addon', true).first();
48891 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
48896 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
48897 _this.list.setWidth(lw);
48900 this.list.on('mouseover', this.onViewOver, this);
48901 this.list.on('mousemove', this.onViewMove, this);
48902 this.list.on('scroll', this.onViewScroll, this);
48905 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
48908 this.view = new Roo.View(this.list, this.tpl, {
48909 singleSelect:true, store: this.store, selectedClass: this.selectedClass
48912 this.view.on('click', this.onViewClick, this);
48914 this.store.on('beforeload', this.onBeforeLoad, this);
48915 this.store.on('load', this.onLoad, this);
48916 this.store.on('loadexception', this.onLoadException, this);
48918 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
48919 "up" : function(e){
48920 this.inKeyMode = true;
48924 "down" : function(e){
48925 if(!this.isExpanded()){
48926 this.onTriggerClick();
48928 this.inKeyMode = true;
48933 "enter" : function(e){
48936 if(this.fireEvent("specialkey", this, e)){
48937 this.onViewClick(false);
48943 "esc" : function(e){
48947 "tab" : function(e){
48950 if(this.fireEvent("specialkey", this, e)){
48951 this.onViewClick(false);
48959 doRelay : function(foo, bar, hname){
48960 if(hname == 'down' || this.scope.isExpanded()){
48961 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48969 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
48973 initNumberEvent : function(e)
48975 this.inputEl().on("keydown" , this.fireKey, this);
48976 this.inputEl().on("focus", this.onFocus, this);
48977 this.inputEl().on("blur", this.onBlur, this);
48979 this.inputEl().relayEvent('keyup', this);
48981 if(this.indicator){
48982 this.indicator.addClass('invisible');
48985 this.originalValue = this.getValue();
48987 if(this.validationEvent == 'keyup'){
48988 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
48989 this.inputEl().on('keyup', this.filterValidation, this);
48991 else if(this.validationEvent !== false){
48992 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
48995 if(this.selectOnFocus){
48996 this.on("focus", this.preFocus, this);
48999 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
49000 this.inputEl().on("keypress", this.filterKeys, this);
49002 this.inputEl().relayEvent('keypress', this);
49005 var allowed = "0123456789";
49007 if(this.allowDecimals){
49008 allowed += this.decimalSeparator;
49011 if(this.allowNegative){
49015 if(this.thousandsDelimiter) {
49019 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
49021 var keyPress = function(e){
49023 var k = e.getKey();
49025 var c = e.getCharCode();
49028 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
49029 allowed.indexOf(String.fromCharCode(c)) === -1
49035 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
49039 if(allowed.indexOf(String.fromCharCode(c)) === -1){
49044 this.inputEl().on("keypress", keyPress, this);
49048 onTriggerClick : function(e)
49055 this.loadNext = false;
49057 if(this.isExpanded()){
49062 this.hasFocus = true;
49064 if(this.triggerAction == 'all') {
49065 this.doQuery(this.allQuery, true);
49069 this.doQuery(this.getRawValue());
49072 getCurrency : function()
49074 var v = this.currencyEl().getValue();
49079 restrictHeight : function()
49081 this.list.alignTo(this.currencyEl(), this.listAlign);
49082 this.list.alignTo(this.currencyEl(), this.listAlign);
49085 onViewClick : function(view, doFocus, el, e)
49087 var index = this.view.getSelectedIndexes()[0];
49089 var r = this.store.getAt(index);
49092 this.onSelect(r, index);
49096 onSelect : function(record, index){
49098 if(this.fireEvent('beforeselect', this, record, index) !== false){
49100 this.setFromCurrencyData(index > -1 ? record.data : false);
49104 this.fireEvent('select', this, record, index);
49108 setFromCurrencyData : function(o)
49112 this.lastCurrency = o;
49114 if (this.currencyField) {
49115 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
49117 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
49120 this.lastSelectionText = currency;
49122 //setting default currency
49123 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
49124 this.setCurrency(this.defaultCurrency);
49128 this.setCurrency(currency);
49131 setFromData : function(o)
49135 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
49137 this.setFromCurrencyData(c);
49142 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
49144 Roo.log('no value set for '+ (this.name ? this.name : this.id));
49147 this.setValue(value);
49151 setCurrency : function(v)
49153 this.currencyValue = v;
49156 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
49161 setValue : function(v)
49163 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
49169 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
49171 this.inputEl().dom.value = (v == '') ? '' :
49172 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
49174 if(!this.allowZero && v === '0') {
49175 this.hiddenEl().dom.value = '';
49176 this.inputEl().dom.value = '';
49183 getRawValue : function()
49185 var v = this.inputEl().getValue();
49190 getValue : function()
49192 return this.fixPrecision(this.parseValue(this.getRawValue()));
49195 parseValue : function(value)
49197 if(this.thousandsDelimiter) {
49199 r = new RegExp(",", "g");
49200 value = value.replace(r, "");
49203 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
49204 return isNaN(value) ? '' : value;
49208 fixPrecision : function(value)
49210 if(this.thousandsDelimiter) {
49212 r = new RegExp(",", "g");
49213 value = value.replace(r, "");
49216 var nan = isNaN(value);
49218 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
49219 return nan ? '' : value;
49221 return parseFloat(value).toFixed(this.decimalPrecision);
49224 decimalPrecisionFcn : function(v)
49226 return Math.floor(v);
49229 validateValue : function(value)
49231 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
49235 var num = this.parseValue(value);
49238 this.markInvalid(String.format(this.nanText, value));
49242 if(num < this.minValue){
49243 this.markInvalid(String.format(this.minText, this.minValue));
49247 if(num > this.maxValue){
49248 this.markInvalid(String.format(this.maxText, this.maxValue));
49255 validate : function()
49257 if(this.disabled || this.allowBlank){
49262 var currency = this.getCurrency();
49264 if(this.validateValue(this.getRawValue()) && currency.length){
49269 this.markInvalid();
49273 getName: function()
49278 beforeBlur : function()
49284 var v = this.parseValue(this.getRawValue());
49291 onBlur : function()
49295 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
49296 //this.el.removeClass(this.focusClass);
49299 this.hasFocus = false;
49301 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
49305 var v = this.getValue();
49307 if(String(v) !== String(this.startValue)){
49308 this.fireEvent('change', this, v, this.startValue);
49311 this.fireEvent("blur", this);
49314 inputEl : function()
49316 return this.el.select('.roo-money-amount-input', true).first();
49319 currencyEl : function()
49321 return this.el.select('.roo-money-currency-input', true).first();
49324 hiddenEl : function()
49326 return this.el.select('input.hidden-number-input',true).first();
49330 * @class Roo.bootstrap.BezierSignature
49331 * @extends Roo.bootstrap.Component
49332 * Bootstrap BezierSignature class
49333 * This script refer to:
49334 * Title: Signature Pad
49336 * Availability: https://github.com/szimek/signature_pad
49339 * Create a new BezierSignature
49340 * @param {Object} config The config object
49343 Roo.bootstrap.BezierSignature = function(config){
49344 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
49350 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
49357 mouse_btn_down: true,
49360 * @cfg {int} canvas height
49362 canvas_height: '200px',
49365 * @cfg {float|function} Radius of a single dot.
49370 * @cfg {float} Minimum width of a line. Defaults to 0.5.
49375 * @cfg {float} Maximum width of a line. Defaults to 2.5.
49380 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
49385 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
49390 * @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.
49392 bg_color: 'rgba(0, 0, 0, 0)',
49395 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
49397 dot_color: 'black',
49400 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
49402 velocity_filter_weight: 0.7,
49405 * @cfg {function} Callback when stroke begin.
49410 * @cfg {function} Callback when stroke end.
49414 getAutoCreate : function()
49416 var cls = 'roo-signature column';
49419 cls += ' ' + this.cls;
49429 for(var i = 0; i < col_sizes.length; i++) {
49430 if(this[col_sizes[i]]) {
49431 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
49441 cls: 'roo-signature-body',
49445 cls: 'roo-signature-body-canvas',
49446 height: this.canvas_height,
49447 width: this.canvas_width
49454 style: 'display: none'
49462 initEvents: function()
49464 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
49466 var canvas = this.canvasEl();
49468 // mouse && touch event swapping...
49469 canvas.dom.style.touchAction = 'none';
49470 canvas.dom.style.msTouchAction = 'none';
49472 this.mouse_btn_down = false;
49473 canvas.on('mousedown', this._handleMouseDown, this);
49474 canvas.on('mousemove', this._handleMouseMove, this);
49475 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
49477 if (window.PointerEvent) {
49478 canvas.on('pointerdown', this._handleMouseDown, this);
49479 canvas.on('pointermove', this._handleMouseMove, this);
49480 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
49483 if ('ontouchstart' in window) {
49484 canvas.on('touchstart', this._handleTouchStart, this);
49485 canvas.on('touchmove', this._handleTouchMove, this);
49486 canvas.on('touchend', this._handleTouchEnd, this);
49489 Roo.EventManager.onWindowResize(this.resize, this, true);
49491 // file input event
49492 this.fileEl().on('change', this.uploadImage, this);
49499 resize: function(){
49501 var canvas = this.canvasEl().dom;
49502 var ctx = this.canvasElCtx();
49503 var img_data = false;
49505 if(canvas.width > 0) {
49506 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
49508 // setting canvas width will clean img data
49511 var style = window.getComputedStyle ?
49512 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
49514 var padding_left = parseInt(style.paddingLeft) || 0;
49515 var padding_right = parseInt(style.paddingRight) || 0;
49517 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
49520 ctx.putImageData(img_data, 0, 0);
49524 _handleMouseDown: function(e)
49526 if (e.browserEvent.which === 1) {
49527 this.mouse_btn_down = true;
49528 this.strokeBegin(e);
49532 _handleMouseMove: function (e)
49534 if (this.mouse_btn_down) {
49535 this.strokeMoveUpdate(e);
49539 _handleMouseUp: function (e)
49541 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
49542 this.mouse_btn_down = false;
49547 _handleTouchStart: function (e) {
49549 e.preventDefault();
49550 if (e.browserEvent.targetTouches.length === 1) {
49551 // var touch = e.browserEvent.changedTouches[0];
49552 // this.strokeBegin(touch);
49554 this.strokeBegin(e); // assume e catching the correct xy...
49558 _handleTouchMove: function (e) {
49559 e.preventDefault();
49560 // var touch = event.targetTouches[0];
49561 // _this._strokeMoveUpdate(touch);
49562 this.strokeMoveUpdate(e);
49565 _handleTouchEnd: function (e) {
49566 var wasCanvasTouched = e.target === this.canvasEl().dom;
49567 if (wasCanvasTouched) {
49568 e.preventDefault();
49569 // var touch = event.changedTouches[0];
49570 // _this._strokeEnd(touch);
49575 reset: function () {
49576 this._lastPoints = [];
49577 this._lastVelocity = 0;
49578 this._lastWidth = (this.min_width + this.max_width) / 2;
49579 this.canvasElCtx().fillStyle = this.dot_color;
49582 strokeMoveUpdate: function(e)
49584 this.strokeUpdate(e);
49586 if (this.throttle) {
49587 this.throttleStroke(this.strokeUpdate, this.throttle);
49590 this.strokeUpdate(e);
49594 strokeBegin: function(e)
49596 var newPointGroup = {
49597 color: this.dot_color,
49601 if (typeof this.onBegin === 'function') {
49605 this.curve_data.push(newPointGroup);
49607 this.strokeUpdate(e);
49610 strokeUpdate: function(e)
49612 var rect = this.canvasEl().dom.getBoundingClientRect();
49613 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
49614 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
49615 var lastPoints = lastPointGroup.points;
49616 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
49617 var isLastPointTooClose = lastPoint
49618 ? point.distanceTo(lastPoint) <= this.min_distance
49620 var color = lastPointGroup.color;
49621 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
49622 var curve = this.addPoint(point);
49624 this.drawDot({color: color, point: point});
49627 this.drawCurve({color: color, curve: curve});
49637 strokeEnd: function(e)
49639 this.strokeUpdate(e);
49640 if (typeof this.onEnd === 'function') {
49645 addPoint: function (point) {
49646 var _lastPoints = this._lastPoints;
49647 _lastPoints.push(point);
49648 if (_lastPoints.length > 2) {
49649 if (_lastPoints.length === 3) {
49650 _lastPoints.unshift(_lastPoints[0]);
49652 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
49653 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
49654 _lastPoints.shift();
49660 calculateCurveWidths: function (startPoint, endPoint) {
49661 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
49662 (1 - this.velocity_filter_weight) * this._lastVelocity;
49664 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
49667 start: this._lastWidth
49670 this._lastVelocity = velocity;
49671 this._lastWidth = newWidth;
49675 drawDot: function (_a) {
49676 var color = _a.color, point = _a.point;
49677 var ctx = this.canvasElCtx();
49678 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
49680 this.drawCurveSegment(point.x, point.y, width);
49682 ctx.fillStyle = color;
49686 drawCurve: function (_a) {
49687 var color = _a.color, curve = _a.curve;
49688 var ctx = this.canvasElCtx();
49689 var widthDelta = curve.endWidth - curve.startWidth;
49690 var drawSteps = Math.floor(curve.length()) * 2;
49692 ctx.fillStyle = color;
49693 for (var i = 0; i < drawSteps; i += 1) {
49694 var t = i / drawSteps;
49700 var x = uuu * curve.startPoint.x;
49701 x += 3 * uu * t * curve.control1.x;
49702 x += 3 * u * tt * curve.control2.x;
49703 x += ttt * curve.endPoint.x;
49704 var y = uuu * curve.startPoint.y;
49705 y += 3 * uu * t * curve.control1.y;
49706 y += 3 * u * tt * curve.control2.y;
49707 y += ttt * curve.endPoint.y;
49708 var width = curve.startWidth + ttt * widthDelta;
49709 this.drawCurveSegment(x, y, width);
49715 drawCurveSegment: function (x, y, width) {
49716 var ctx = this.canvasElCtx();
49718 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
49719 this.is_empty = false;
49724 var ctx = this.canvasElCtx();
49725 var canvas = this.canvasEl().dom;
49726 ctx.fillStyle = this.bg_color;
49727 ctx.clearRect(0, 0, canvas.width, canvas.height);
49728 ctx.fillRect(0, 0, canvas.width, canvas.height);
49729 this.curve_data = [];
49731 this.is_empty = true;
49736 return this.el.select('input',true).first();
49739 canvasEl: function()
49741 return this.el.select('canvas',true).first();
49744 canvasElCtx: function()
49746 return this.el.select('canvas',true).first().dom.getContext('2d');
49749 getImage: function(type)
49751 if(this.is_empty) {
49756 return this.canvasEl().dom.toDataURL('image/'+type, 1);
49759 drawFromImage: function(img_src)
49761 var img = new Image();
49763 img.onload = function(){
49764 this.canvasElCtx().drawImage(img, 0, 0);
49769 this.is_empty = false;
49772 selectImage: function()
49774 this.fileEl().dom.click();
49777 uploadImage: function(e)
49779 var reader = new FileReader();
49781 reader.onload = function(e){
49782 var img = new Image();
49783 img.onload = function(){
49785 this.canvasElCtx().drawImage(img, 0, 0);
49787 img.src = e.target.result;
49790 reader.readAsDataURL(e.target.files[0]);
49793 // Bezier Point Constructor
49794 Point: (function () {
49795 function Point(x, y, time) {
49798 this.time = time || Date.now();
49800 Point.prototype.distanceTo = function (start) {
49801 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
49803 Point.prototype.equals = function (other) {
49804 return this.x === other.x && this.y === other.y && this.time === other.time;
49806 Point.prototype.velocityFrom = function (start) {
49807 return this.time !== start.time
49808 ? this.distanceTo(start) / (this.time - start.time)
49815 // Bezier Constructor
49816 Bezier: (function () {
49817 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
49818 this.startPoint = startPoint;
49819 this.control2 = control2;
49820 this.control1 = control1;
49821 this.endPoint = endPoint;
49822 this.startWidth = startWidth;
49823 this.endWidth = endWidth;
49825 Bezier.fromPoints = function (points, widths, scope) {
49826 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
49827 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
49828 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
49830 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
49831 var dx1 = s1.x - s2.x;
49832 var dy1 = s1.y - s2.y;
49833 var dx2 = s2.x - s3.x;
49834 var dy2 = s2.y - s3.y;
49835 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
49836 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
49837 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
49838 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
49839 var dxm = m1.x - m2.x;
49840 var dym = m1.y - m2.y;
49841 var k = l2 / (l1 + l2);
49842 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
49843 var tx = s2.x - cm.x;
49844 var ty = s2.y - cm.y;
49846 c1: new scope.Point(m1.x + tx, m1.y + ty),
49847 c2: new scope.Point(m2.x + tx, m2.y + ty)
49850 Bezier.prototype.length = function () {
49855 for (var i = 0; i <= steps; i += 1) {
49857 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
49858 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
49860 var xdiff = cx - px;
49861 var ydiff = cy - py;
49862 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
49869 Bezier.prototype.point = function (t, start, c1, c2, end) {
49870 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
49871 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
49872 + (3.0 * c2 * (1.0 - t) * t * t)
49873 + (end * t * t * t);
49878 throttleStroke: function(fn, wait) {
49879 if (wait === void 0) { wait = 250; }
49881 var timeout = null;
49885 var later = function () {
49886 previous = Date.now();
49888 result = fn.apply(storedContext, storedArgs);
49890 storedContext = null;
49894 return function wrapper() {
49896 for (var _i = 0; _i < arguments.length; _i++) {
49897 args[_i] = arguments[_i];
49899 var now = Date.now();
49900 var remaining = wait - (now - previous);
49901 storedContext = this;
49903 if (remaining <= 0 || remaining > wait) {
49905 clearTimeout(timeout);
49909 result = fn.apply(storedContext, storedArgs);
49911 storedContext = null;
49915 else if (!timeout) {
49916 timeout = window.setTimeout(later, remaining);
49926 // old names for form elements
49927 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
49928 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
49929 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
49930 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
49931 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
49932 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
49933 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
49934 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
49935 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
49936 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
49937 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
49938 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
49939 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
49940 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
49941 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
49942 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
49943 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
49944 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
49945 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
49946 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
49947 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
49948 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
49949 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
49950 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
49951 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
49952 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
49954 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
49955 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
49957 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
49958 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
49960 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
49961 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
49962 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
49963 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator