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 console.log("BOOSTRAP COMPONENT CONSTRUCTOR");
251 Roo.bootstrap.Component.superclass.constructor.call(this, config);
255 * @event childrenrendered
256 * Fires when the children have been rendered..
257 * @param {Roo.bootstrap.Component} this
259 "childrenrendered" : true
268 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
271 allowDomMove : false, // to stop relocations in parent onRender...
281 * Initialize Events for the element
283 initEvents : function() { },
289 can_build_overlaid : true,
291 container_method : false,
298 // returns the parent component..
299 return Roo.ComponentMgr.get(this.parentId)
305 onRender : function(ct, position)
307 // Roo.log("Call onRender: " + this.xtype);
309 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
312 if (this.el.attr('xtype')) {
313 this.el.attr('xtypex', this.el.attr('xtype'));
314 this.el.dom.removeAttribute('xtype');
324 var cfg = Roo.apply({}, this.getAutoCreate());
326 cfg.id = this.id || Roo.id();
328 // fill in the extra attributes
329 if (this.xattr && typeof(this.xattr) =='object') {
330 for (var i in this.xattr) {
331 cfg[i] = this.xattr[i];
336 cfg.dataId = this.dataId;
340 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
343 if (this.style) { // fixme needs to support more complex style data.
344 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
348 cfg.name = this.name;
351 this.el = ct.createChild(cfg, position);
354 this.tooltipEl().attr('tooltip', this.tooltip);
357 if(this.tabIndex !== undefined){
358 this.el.dom.setAttribute('tabIndex', this.tabIndex);
365 * Fetch the element to add children to
366 * @return {Roo.Element} defaults to this.el
368 getChildContainer : function()
372 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
374 return Roo.get(document.body);
378 * Fetch the element to display the tooltip on.
379 * @return {Roo.Element} defaults to this.el
381 tooltipEl : function()
386 addxtype : function(tree,cntr)
390 cn = Roo.factory(tree);
391 //Roo.log(['addxtype', cn]);
393 cn.parentType = this.xtype; //??
394 cn.parentId = this.id;
396 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
397 if (typeof(cn.container_method) == 'string') {
398 cntr = cn.container_method;
402 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
404 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
406 var build_from_html = Roo.XComponent.build_from_html;
408 var is_body = (tree.xtype == 'Body') ;
410 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
412 var self_cntr_el = Roo.get(this[cntr](false));
414 // do not try and build conditional elements
415 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
419 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
420 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
421 return this.addxtypeChild(tree,cntr, is_body);
424 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
427 return this.addxtypeChild(Roo.apply({}, tree),cntr);
430 Roo.log('skipping render');
436 if (!build_from_html) {
440 // this i think handles overlaying multiple children of the same type
441 // with the sam eelement.. - which might be buggy..
443 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
449 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
453 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
460 addxtypeChild : function (tree, cntr, is_body)
462 Roo.debug && Roo.log('addxtypeChild:' + cntr);
464 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
467 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
468 (typeof(tree['flexy:foreach']) != 'undefined');
472 skip_children = false;
473 // render the element if it's not BODY.
476 // if parent was disabled, then do not try and create the children..
477 if(!this[cntr](true)){
482 cn = Roo.factory(tree);
484 cn.parentType = this.xtype; //??
485 cn.parentId = this.id;
487 var build_from_html = Roo.XComponent.build_from_html;
490 // does the container contain child eleemnts with 'xtype' attributes.
491 // that match this xtype..
492 // note - when we render we create these as well..
493 // so we should check to see if body has xtype set.
494 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
496 var self_cntr_el = Roo.get(this[cntr](false));
497 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
499 //Roo.log(Roo.XComponent.build_from_html);
500 //Roo.log("got echild:");
503 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
504 // and are not displayed -this causes this to use up the wrong element when matching.
505 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
508 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
509 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
515 //echild.dom.removeAttribute('xtype');
517 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
518 Roo.debug && Roo.log(self_cntr_el);
519 Roo.debug && Roo.log(echild);
520 Roo.debug && Roo.log(cn);
526 // if object has flexy:if - then it may or may not be rendered.
527 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
528 // skip a flexy if element.
529 Roo.debug && Roo.log('skipping render');
530 Roo.debug && Roo.log(tree);
532 Roo.debug && Roo.log('skipping all children');
533 skip_children = true;
538 // actually if flexy:foreach is found, we really want to create
539 // multiple copies here...
541 //Roo.log(this[cntr]());
542 // some elements do not have render methods.. like the layouts...
544 if(this[cntr](true) === false){
549 cn.render && cn.render(this[cntr](true));
552 // then add the element..
559 if (typeof (tree.menu) != 'undefined') {
560 tree.menu.parentType = cn.xtype;
561 tree.menu.triggerEl = cn.el;
562 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
566 if (!tree.items || !tree.items.length) {
568 //Roo.log(["no children", this]);
573 var items = tree.items;
576 //Roo.log(items.length);
578 if (!skip_children) {
579 for(var i =0;i < items.length;i++) {
580 // Roo.log(['add child', items[i]]);
581 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
587 //Roo.log("fire childrenrendered");
589 cn.fireEvent('childrenrendered', this);
595 * Set the element that will be used to show or hide
597 setVisibilityEl : function(el)
599 this.visibilityEl = el;
603 * Get the element that will be used to show or hide
605 getVisibilityEl : function()
607 if (typeof(this.visibilityEl) == 'object') {
608 return this.visibilityEl;
611 if (typeof(this.visibilityEl) == 'string') {
612 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
619 * Show a component - removes 'hidden' class
623 if(!this.getVisibilityEl()){
627 this.getVisibilityEl().removeClass(['hidden','d-none']);
629 this.fireEvent('show', this);
634 * Hide a component - adds 'hidden' class
638 if(!this.getVisibilityEl()){
642 this.getVisibilityEl().addClass(['hidden','d-none']);
644 this.fireEvent('hide', this);
657 * @class Roo.bootstrap.Element
658 * @extends Roo.bootstrap.Component
659 * @children Roo.bootstrap.Component
660 * Bootstrap Element class (basically a DIV used to make random stuff )
662 * @cfg {String} html contents of the element
663 * @cfg {String} tag tag of the element
664 * @cfg {String} cls class of the element
665 * @cfg {Boolean} preventDefault (true|false) default false
666 * @cfg {Boolean} clickable (true|false) default false
667 * @cfg {String} role default blank - set to button to force cursor pointer
671 * Create a new Element
672 * @param {Object} config The config object
675 Roo.bootstrap.Element = function(config){
676 Roo.bootstrap.Element.superclass.constructor.call(this, config);
682 * When a element is chick
683 * @param {Roo.bootstrap.Element} this
684 * @param {Roo.EventObject} e
692 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
697 preventDefault: false,
702 getAutoCreate : function(){
706 // cls: this.cls, double assign in parent class Component.js :: onRender
709 if (this.role !== false) {
710 cfg.role = this.role;
716 initEvents: function()
718 Roo.bootstrap.Element.superclass.initEvents.call(this);
721 this.el.on('click', this.onClick, this);
727 onClick : function(e)
729 if(this.preventDefault){
733 this.fireEvent('click', this, e); // why was this double click before?
741 getValue : function()
743 return this.el.dom.innerHTML;
746 setValue : function(value)
748 this.el.dom.innerHTML = value;
763 * @class Roo.bootstrap.DropTarget
764 * @extends Roo.bootstrap.Element
765 * Bootstrap DropTarget class
767 * @cfg {string} name dropable name
770 * Create a new Dropable Area
771 * @param {Object} config The config object
774 Roo.bootstrap.DropTarget = function(config){
775 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
781 * When a element is chick
782 * @param {Roo.bootstrap.Element} this
783 * @param {Roo.EventObject} e
789 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
792 getAutoCreate : function(){
797 initEvents: function()
799 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
800 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
803 drop : this.dragDrop.createDelegate(this),
804 enter : this.dragEnter.createDelegate(this),
805 out : this.dragOut.createDelegate(this),
806 over : this.dragOver.createDelegate(this)
810 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
813 dragDrop : function(source,e,data)
815 // user has to decide how to impliment this.
818 //this.fireEvent('drop', this, source, e ,data);
822 dragEnter : function(n, dd, e, data)
824 // probably want to resize the element to match the dropped element..
826 this.originalSize = this.el.getSize();
827 this.el.setSize( n.el.getSize());
828 this.dropZone.DDM.refreshCache(this.name);
829 Roo.log([n, dd, e, data]);
832 dragOut : function(value)
834 // resize back to normal
836 this.el.setSize(this.originalSize);
837 this.dropZone.resetConstraints();
840 dragOver : function()
857 * @class Roo.bootstrap.Body
858 * @extends Roo.bootstrap.Component
859 * @children Roo.bootstrap.Component
860 * @parent none builder
861 * Bootstrap Body class
865 * @param {Object} config The config object
868 Roo.bootstrap.Body = function(config){
870 config = config || {};
872 Roo.bootstrap.Body.superclass.constructor.call(this, config);
873 this.el = Roo.get(config.el ? config.el : document.body );
874 if (this.cls && this.cls.length) {
875 Roo.get(document.body).addClass(this.cls);
879 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
881 is_body : true,// just to make sure it's constructed?
886 onRender : function(ct, position)
888 /* Roo.log("Roo.bootstrap.Body - onRender");
889 if (this.cls && this.cls.length) {
890 Roo.get(document.body).addClass(this.cls);
909 * @class Roo.bootstrap.ButtonGroup
910 * @extends Roo.bootstrap.Component
911 * Bootstrap ButtonGroup class
912 * @children Roo.bootstrap.Button Roo.bootstrap.form.Form
914 * @cfg {String} size lg | sm | xs (default empty normal)
915 * @cfg {String} align vertical | justified (default none)
916 * @cfg {String} direction up | down (default down)
917 * @cfg {Boolean} toolbar false | true
918 * @cfg {Boolean} btn true | false
923 * @param {Object} config The config object
926 Roo.bootstrap.ButtonGroup = function(config){
927 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
930 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
938 getAutoCreate : function(){
944 cfg.html = this.html || cfg.html;
955 if (['vertical','justified'].indexOf(this.align)!==-1) {
956 cfg.cls = 'btn-group-' + this.align;
958 if (this.align == 'justified') {
959 console.log(this.items);
963 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
964 cfg.cls += ' btn-group-' + this.size;
967 if (this.direction == 'up') {
968 cfg.cls += ' dropup' ;
974 * Add a button to the group (similar to NavItem API.)
976 addItem : function(cfg)
978 var cn = new Roo.bootstrap.Button(cfg);
980 cn.parentId = this.id;
981 cn.onRender(this.el, null);
995 * @class Roo.bootstrap.Button
996 * @extends Roo.bootstrap.Component
997 * Bootstrap Button class
998 * @cfg {String} html The button content
999 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
1000 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
1001 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
1002 * @cfg {String} size (lg|sm|xs)
1003 * @cfg {String} tag (a|input|submit)
1004 * @cfg {String} href empty or href
1005 * @cfg {Boolean} disabled default false;
1006 * @cfg {Boolean} isClose default false;
1007 * @cfg {String} glyphicon depricated - use fa
1008 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
1009 * @cfg {String} badge text for badge
1010 * @cfg {String} theme (default|glow)
1011 * @cfg {Boolean} inverse dark themed version
1012 * @cfg {Boolean} toggle is it a slidy toggle button
1013 * @cfg {Boolean} pressed default null - if the button ahs active state
1014 * @cfg {String} ontext text for on slidy toggle state
1015 * @cfg {String} offtext text for off slidy toggle state
1016 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1017 * @cfg {Boolean} removeClass remove the standard class..
1018 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1019 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1020 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
1023 * Create a new button
1024 * @param {Object} config The config object
1028 Roo.bootstrap.Button = function(config){
1029 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1035 * When a button is pressed
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1042 * When a button is double clicked
1043 * @param {Roo.bootstrap.Button} btn
1044 * @param {Roo.EventObject} e
1049 * After the button has been toggles
1050 * @param {Roo.bootstrap.Button} btn
1051 * @param {Roo.EventObject} e
1052 * @param {boolean} pressed (also available as button.pressed)
1058 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1079 preventDefault: true,
1088 getAutoCreate : function(){
1096 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1097 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1098 this.tag = 'button';
1102 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1104 if (this.toggle == true) {
1107 cls: 'slider-frame roo-button',
1111 'data-on-text':'ON',
1112 'data-off-text':'OFF',
1113 cls: 'slider-button',
1118 // why are we validating the weights?
1119 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1120 cfg.cls += ' ' + this.weight;
1127 cfg.cls += ' close';
1129 cfg["aria-hidden"] = true;
1131 cfg.html = "×";
1137 if (this.theme==='default') {
1138 cfg.cls = 'btn roo-button';
1140 //if (this.parentType != 'Navbar') {
1141 this.weight = this.weight.length ? this.weight : 'default';
1143 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1146 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1147 cfg.cls += ' btn-' + outline + weight;
1148 if (this.weight == 'default') {
1150 cfg.cls += ' btn-' + this.weight;
1153 } else if (this.theme==='glow') {
1156 cfg.cls = 'btn-glow roo-button';
1158 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1160 cfg.cls += ' ' + this.weight;
1166 this.cls += ' inverse';
1170 if (this.active || this.pressed === true) {
1171 cfg.cls += ' active';
1174 if (this.disabled) {
1175 cfg.disabled = 'disabled';
1179 Roo.log('changing to ul' );
1181 this.glyphicon = 'caret';
1182 if (Roo.bootstrap.version == 4) {
1183 this.fa = 'caret-down';
1188 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1190 //gsRoo.log(this.parentType);
1191 if (this.parentType === 'Navbar' && !this.parent().bar) {
1192 Roo.log('changing to li?');
1201 href : this.href || '#'
1204 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1205 cfg.cls += ' dropdown';
1212 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1214 if (this.glyphicon) {
1215 cfg.html = ' ' + cfg.html;
1220 cls: 'glyphicon glyphicon-' + this.glyphicon
1225 cfg.html = ' ' + cfg.html;
1230 cls: 'fa fas fa-' + this.fa
1240 // cfg.cls='btn roo-button';
1244 var value = cfg.html;
1249 cls: 'glyphicon glyphicon-' + this.glyphicon,
1256 cls: 'fa fas fa-' + this.fa,
1261 var bw = this.badge_weight.length ? this.badge_weight :
1262 (this.weight.length ? this.weight : 'secondary');
1263 bw = bw == 'default' ? 'secondary' : bw;
1269 cls: 'badge badge-' + bw,
1278 cfg.cls += ' dropdown';
1279 cfg.html = typeof(cfg.html) != 'undefined' ?
1280 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1283 if (cfg.tag !== 'a' && this.href !== '') {
1284 throw "Tag must be a to set href.";
1285 } else if (this.href.length > 0) {
1286 cfg.href = this.href;
1289 if(this.removeClass){
1294 cfg.target = this.target;
1299 initEvents: function() {
1300 // Roo.log('init events?');
1301 // Roo.log(this.el.dom);
1304 if (typeof (this.menu) != 'undefined') {
1305 this.menu.parentType = this.xtype;
1306 this.menu.triggerEl = this.el;
1307 this.addxtype(Roo.apply({}, this.menu));
1311 if (this.el.hasClass('roo-button')) {
1312 this.el.on('click', this.onClick, this);
1313 this.el.on('dblclick', this.onDblClick, this);
1315 this.el.select('.roo-button').on('click', this.onClick, this);
1316 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1320 if(this.removeClass){
1321 this.el.on('click', this.onClick, this);
1324 if (this.group === true) {
1325 if (this.pressed === false || this.pressed === true) {
1328 this.pressed = false;
1329 this.setActive(this.pressed);
1334 this.el.enableDisplayMode();
1337 onClick : function(e)
1339 if (this.disabled) {
1343 Roo.log('button on click ');
1344 if(this.href === '' || this.preventDefault){
1353 this.setActive(true);
1354 var pi = this.parent().items;
1355 for (var i = 0;i < pi.length;i++) {
1356 if (this == pi[i]) {
1359 if (pi[i].el.hasClass('roo-button')) {
1360 pi[i].setActive(false);
1363 this.fireEvent('click', this, e);
1367 if (this.pressed === true || this.pressed === false) {
1368 this.toggleActive(e);
1372 this.fireEvent('click', this, e);
1374 onDblClick: function(e)
1376 if (this.disabled) {
1379 if(this.preventDefault){
1382 this.fireEvent('dblclick', this, e);
1385 * Enables this button
1389 this.disabled = false;
1390 this.el.removeClass('disabled');
1391 this.el.dom.removeAttribute("disabled");
1395 * Disable this button
1397 disable : function()
1399 this.disabled = true;
1400 this.el.addClass('disabled');
1401 this.el.attr("disabled", "disabled")
1404 * sets the active state on/off,
1405 * @param {Boolean} state (optional) Force a particular state
1407 setActive : function(v) {
1409 this.el[v ? 'addClass' : 'removeClass']('active');
1413 * toggles the current active state
1415 toggleActive : function(e)
1417 this.setActive(!this.pressed); // this modifies pressed...
1418 this.fireEvent('toggle', this, e, this.pressed);
1421 * get the current active state
1422 * @return {boolean} true if it's active
1424 isActive : function()
1426 return this.el.hasClass('active');
1429 * set the text of the first selected button
1431 setText : function(str)
1433 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1436 * get the text of the first selected button
1438 getText : function()
1440 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1443 setWeight : function(str)
1445 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1446 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1448 var outline = this.outline ? 'outline-' : '';
1449 if (str == 'default') {
1450 this.el.addClass('btn-default btn-outline-secondary');
1453 this.el.addClass('btn-' + outline + str);
1458 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1460 Roo.bootstrap.Button.weights = [
1480 * @class Roo.bootstrap.Column
1481 * @extends Roo.bootstrap.Component
1482 * @children Roo.bootstrap.Component
1483 * Bootstrap Column class
1484 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1485 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1486 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1487 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1488 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1489 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1490 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1491 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1494 * @cfg {Boolean} hidden (true|false) hide the element
1495 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1496 * @cfg {String} fa (ban|check|...) font awesome icon
1497 * @cfg {Number} fasize (1|2|....) font awsome size
1499 * @cfg {String} icon (info-sign|check|...) glyphicon name
1501 * @cfg {String} html content of column.
1504 * Create a new Column
1505 * @param {Object} config The config object
1508 Roo.bootstrap.Column = function(config){
1509 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1512 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1530 getAutoCreate : function(){
1531 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1539 var sizes = ['xs','sm','md','lg'];
1540 sizes.map(function(size ,ix){
1541 //Roo.log( size + ':' + settings[size]);
1543 if (settings[size+'off'] !== false) {
1544 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1547 if (settings[size] === false) {
1551 if (!settings[size]) { // 0 = hidden
1552 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1554 for (var i = ix; i > -1; i--) {
1555 cfg.cls += ' d-' + sizes[i] + '-none';
1561 cfg.cls += ' col-' + size + '-' + settings[size] + (
1562 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1568 cfg.cls += ' hidden';
1571 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1572 cfg.cls +=' alert alert-' + this.alert;
1576 if (this.html.length) {
1577 cfg.html = this.html;
1581 if (this.fasize > 1) {
1582 fasize = ' fa-' + this.fasize + 'x';
1584 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1589 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1608 * @class Roo.bootstrap.Container
1609 * @extends Roo.bootstrap.Component
1610 * @children Roo.bootstrap.Component
1612 * Bootstrap Container class
1613 * @cfg {Boolean} jumbotron is it a jumbotron element
1614 * @cfg {String} html content of element
1615 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1616 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1617 * @cfg {String} header content of header (for panel)
1618 * @cfg {String} footer content of footer (for panel)
1619 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1620 * @cfg {String} tag (header|aside|section) type of HTML tag.
1621 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1622 * @cfg {String} fa font awesome icon
1623 * @cfg {String} icon (info-sign|check|...) glyphicon name
1624 * @cfg {Boolean} hidden (true|false) hide the element
1625 * @cfg {Boolean} expandable (true|false) default false
1626 * @cfg {Boolean} expanded (true|false) default true
1627 * @cfg {String} rheader contet on the right of header
1628 * @cfg {Boolean} clickable (true|false) default false
1632 * Create a new Container
1633 * @param {Object} config The config object
1636 Roo.bootstrap.Container = function(config){
1637 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1643 * After the panel has been expand
1645 * @param {Roo.bootstrap.Container} this
1650 * After the panel has been collapsed
1652 * @param {Roo.bootstrap.Container} this
1657 * When a element is chick
1658 * @param {Roo.bootstrap.Container} this
1659 * @param {Roo.EventObject} e
1665 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1683 getChildContainer : function() {
1689 if (this.panel.length) {
1690 return this.el.select('.panel-body',true).first();
1697 getAutoCreate : function(){
1700 tag : this.tag || 'div',
1704 if (this.jumbotron) {
1705 cfg.cls = 'jumbotron';
1710 // - this is applied by the parent..
1712 // cfg.cls = this.cls + '';
1715 if (this.sticky.length) {
1717 var bd = Roo.get(document.body);
1718 if (!bd.hasClass('bootstrap-sticky')) {
1719 bd.addClass('bootstrap-sticky');
1720 Roo.select('html',true).setStyle('height', '100%');
1723 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1727 if (this.well.length) {
1728 switch (this.well) {
1731 cfg.cls +=' well well-' +this.well;
1740 cfg.cls += ' hidden';
1744 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1745 cfg.cls +=' alert alert-' + this.alert;
1750 if (this.panel.length) {
1751 cfg.cls += ' panel panel-' + this.panel;
1753 if (this.header.length) {
1757 if(this.expandable){
1759 cfg.cls = cfg.cls + ' expandable';
1763 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1771 cls : 'panel-title',
1772 html : (this.expandable ? ' ' : '') + this.header
1776 cls: 'panel-header-right',
1782 cls : 'panel-heading',
1783 style : this.expandable ? 'cursor: pointer' : '',
1791 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1796 if (this.footer.length) {
1798 cls : 'panel-footer',
1807 body.html = this.html || cfg.html;
1808 // prefix with the icons..
1810 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1813 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1818 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1819 cfg.cls = 'container';
1825 initEvents: function()
1827 if(this.expandable){
1828 var headerEl = this.headerEl();
1831 headerEl.on('click', this.onToggleClick, this);
1836 this.el.on('click', this.onClick, this);
1841 onToggleClick : function()
1843 var headerEl = this.headerEl();
1859 if(this.fireEvent('expand', this)) {
1861 this.expanded = true;
1863 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1865 this.el.select('.panel-body',true).first().removeClass('hide');
1867 var toggleEl = this.toggleEl();
1873 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1878 collapse : function()
1880 if(this.fireEvent('collapse', this)) {
1882 this.expanded = false;
1884 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1885 this.el.select('.panel-body',true).first().addClass('hide');
1887 var toggleEl = this.toggleEl();
1893 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1897 toggleEl : function()
1899 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1903 return this.el.select('.panel-heading .fa',true).first();
1906 headerEl : function()
1908 if(!this.el || !this.panel.length || !this.header.length){
1912 return this.el.select('.panel-heading',true).first()
1917 if(!this.el || !this.panel.length){
1921 return this.el.select('.panel-body',true).first()
1924 titleEl : function()
1926 if(!this.el || !this.panel.length || !this.header.length){
1930 return this.el.select('.panel-title',true).first();
1933 setTitle : function(v)
1935 var titleEl = this.titleEl();
1941 titleEl.dom.innerHTML = v;
1944 getTitle : function()
1947 var titleEl = this.titleEl();
1953 return titleEl.dom.innerHTML;
1956 setRightTitle : function(v)
1958 var t = this.el.select('.panel-header-right',true).first();
1964 t.dom.innerHTML = v;
1967 onClick : function(e)
1971 this.fireEvent('click', this, e);
1976 * @class Roo.bootstrap.Card
1977 * @extends Roo.bootstrap.Component
1978 * @children Roo.bootstrap.Component
1980 * Bootstrap Card class - note this has children as CardHeader/ImageTop/Footer.. - which should really be listed properties?
1983 * possible... may not be implemented..
1984 * @cfg {String} header_image src url of image.
1985 * @cfg {String|Object} header
1986 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1987 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1989 * @cfg {String} title
1990 * @cfg {String} subtitle
1991 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1992 * @cfg {String} footer
1994 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1996 * @cfg {String} margin (0|1|2|3|4|5|auto)
1997 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1998 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1999 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
2000 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
2001 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
2002 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
2004 * @cfg {String} padding (0|1|2|3|4|5)
2005 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
2006 * @cfg {String} padding_bottom (0|1|2|3|4|5)
2007 * @cfg {String} padding_left (0|1|2|3|4|5)
2008 * @cfg {String} padding_right (0|1|2|3|4|5)
2009 * @cfg {String} padding_x (0|1|2|3|4|5)
2010 * @cfg {String} padding_y (0|1|2|3|4|5)
2012 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2013 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2014 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2015 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2016 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2018 * @config {Boolean} dragable if this card can be dragged.
2019 * @config {String} drag_group group for drag
2020 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2021 * @config {String} drop_group group for drag
2023 * @config {Boolean} collapsable can the body be collapsed.
2024 * @config {Boolean} collapsed is the body collapsed when rendered...
2025 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2026 * @config {Boolean} rotated is the body rotated when rendered...
2029 * Create a new Container
2030 * @param {Object} config The config object
2033 Roo.bootstrap.Card = function(config){
2034 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2040 * When a element a card is dropped
2041 * @param {Roo.bootstrap.Card} this
2044 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2045 * @param {String} position 'above' or 'below'
2046 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2052 * When a element a card is rotate
2053 * @param {Roo.bootstrap.Card} this
2054 * @param {Roo.Element} n the node being dropped?
2055 * @param {Boolean} rotate status
2060 * When a card element is dragged over ready to drop (return false to block dropable)
2061 * @param {Roo.bootstrap.Card} this
2062 * @param {Object} data from dragdrop
2070 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2075 margin: '', /// may be better in component?
2105 collapsable : false,
2114 childContainer : false,
2115 dropEl : false, /// the dom placeholde element that indicates drop location.
2116 containerEl: false, // body container
2117 bodyEl: false, // card-body
2118 headerContainerEl : false, //
2120 header_imageEl : false,
2123 layoutCls : function()
2127 Roo.log(this.margin_bottom.length);
2128 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2129 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2131 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2132 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2134 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2135 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2139 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2140 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2141 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2145 // more generic support?
2153 // Roo.log("Call onRender: " + this.xtype);
2154 /* We are looking at something like this.
2156 <img src="..." class="card-img-top" alt="...">
2157 <div class="card-body">
2158 <h5 class="card-title">Card title</h5>
2159 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2161 >> this bit is really the body...
2162 <div> << we will ad dthis in hopefully it will not break shit.
2164 ** card text does not actually have any styling...
2166 <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>
2169 <a href="#" class="card-link">Card link</a>
2172 <div class="card-footer">
2173 <small class="text-muted">Last updated 3 mins ago</small>
2177 getAutoCreate : function(){
2185 if (this.weight.length && this.weight != 'light') {
2186 cfg.cls += ' text-white';
2188 cfg.cls += ' text-dark'; // need as it's nested..
2190 if (this.weight.length) {
2191 cfg.cls += ' bg-' + this.weight;
2194 cfg.cls += ' ' + this.layoutCls();
2197 var hdr_ctr = false;
2198 if (this.header.length) {
2200 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2201 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2209 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2215 if (this.collapsable) {
2218 cls : 'd-block user-select-none',
2222 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2227 hdr.cn.push(hdr_ctr);
2232 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2237 if (this.header_image.length) {
2240 cls : 'card-img-top',
2241 src: this.header_image // escape?
2246 cls : 'card-img-top d-none'
2252 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2256 if (this.collapsable || this.rotateable) {
2259 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2266 if (this.title.length) {
2270 src: this.title // escape?
2274 if (this.subtitle.length) {
2278 src: this.subtitle // escape?
2284 cls : 'roo-card-body-ctr'
2287 if (this.html.length) {
2293 // fixme ? handle objects?
2295 if (this.footer.length) {
2298 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2303 cfg.cn.push({cls : 'card-footer d-none'});
2312 getCardHeader : function()
2314 var ret = this.el.select('.card-header',true).first();
2315 if (ret.hasClass('d-none')) {
2316 ret.removeClass('d-none');
2321 getCardFooter : function()
2323 var ret = this.el.select('.card-footer',true).first();
2324 if (ret.hasClass('d-none')) {
2325 ret.removeClass('d-none');
2330 getCardImageTop : function()
2332 var ret = this.header_imageEl;
2333 if (ret.hasClass('d-none')) {
2334 ret.removeClass('d-none');
2340 getChildContainer : function()
2346 return this.el.select('.roo-card-body-ctr',true).first();
2349 initEvents: function()
2351 this.bodyEl = this.el.select('.card-body',true).first();
2352 this.containerEl = this.getChildContainer();
2354 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2355 containerScroll: true,
2356 ddGroup: this.drag_group || 'default_card_drag_group'
2358 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2360 if (this.dropable) {
2361 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2362 containerScroll: true,
2363 ddGroup: this.drop_group || 'default_card_drag_group'
2365 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2366 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2367 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2368 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2369 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2372 if (this.collapsable) {
2373 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2375 if (this.rotateable) {
2376 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2378 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2380 this.footerEl = this.el.select('.card-footer',true).first();
2381 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2382 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2383 this.headerEl = this.el.select('.card-header',true).first();
2386 this.el.addClass('roo-card-rotated');
2387 this.fireEvent('rotate', this, true);
2389 this.header_imageEl = this.el.select('.card-img-top',true).first();
2390 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2393 getDragData : function(e)
2395 var target = this.getEl();
2397 //this.handleSelection(e);
2402 nodes: this.getEl(),
2407 dragData.ddel = target.dom ; // the div element
2408 Roo.log(target.getWidth( ));
2409 dragData.ddel.style.width = target.getWidth() + 'px';
2416 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2417 * whole Element becomes the target, and this causes the drop gesture to append.
2419 * Returns an object:
2422 position : 'below' or 'above'
2423 card : relateive to card OBJECT (or true for no cards listed)
2424 items_n : relative to nth item in list
2425 card_n : relative to nth card in list
2430 getTargetFromEvent : function(e, dragged_card_el)
2432 var target = e.getTarget();
2433 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2434 target = target.parentNode;
2445 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2446 // see if target is one of the 'cards'...
2449 //Roo.log(this.items.length);
2452 var last_card_n = 0;
2454 for (var i = 0;i< this.items.length;i++) {
2456 if (!this.items[i].el.hasClass('card')) {
2459 pos = this.getDropPoint(e, this.items[i].el.dom);
2461 cards_len = ret.cards.length;
2462 //Roo.log(this.items[i].el.dom.id);
2463 ret.cards.push(this.items[i]);
2465 if (ret.card_n < 0 && pos == 'above') {
2466 ret.position = cards_len > 0 ? 'below' : pos;
2467 ret.items_n = i > 0 ? i - 1 : 0;
2468 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2469 ret.card = ret.cards[ret.card_n];
2472 if (!ret.cards.length) {
2474 ret.position = 'below';
2478 // could not find a card.. stick it at the end..
2479 if (ret.card_n < 0) {
2480 ret.card_n = last_card_n;
2481 ret.card = ret.cards[last_card_n];
2482 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2483 ret.position = 'below';
2486 if (this.items[ret.items_n].el == dragged_card_el) {
2490 if (ret.position == 'below') {
2491 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2493 if (card_after && card_after.el == dragged_card_el) {
2500 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2502 if (card_before && card_before.el == dragged_card_el) {
2509 onNodeEnter : function(n, dd, e, data){
2512 onNodeOver : function(n, dd, e, data)
2515 var target_info = this.getTargetFromEvent(e,data.source.el);
2516 if (target_info === false) {
2517 this.dropPlaceHolder('hide');
2520 Roo.log(['getTargetFromEvent', target_info ]);
2523 if (this.fireEvent('cardover', this, [ data ]) === false) {
2527 this.dropPlaceHolder('show', target_info,data);
2531 onNodeOut : function(n, dd, e, data){
2532 this.dropPlaceHolder('hide');
2535 onNodeDrop : function(n, dd, e, data)
2538 // call drop - return false if
2540 // this could actually fail - if the Network drops..
2541 // we will ignore this at present..- client should probably reload
2542 // the whole set of cards if stuff like that fails.
2545 var info = this.getTargetFromEvent(e,data.source.el);
2546 if (info === false) {
2549 this.dropPlaceHolder('hide');
2553 this.acceptCard(data.source, info.position, info.card, info.items_n);
2557 firstChildCard : function()
2559 for (var i = 0;i< this.items.length;i++) {
2561 if (!this.items[i].el.hasClass('card')) {
2564 return this.items[i];
2566 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2571 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2573 acceptCard : function(move_card, position, next_to_card )
2575 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2579 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2581 move_card.parent().removeCard(move_card);
2584 var dom = move_card.el.dom;
2585 dom.style.width = ''; // clear with - which is set by drag.
2587 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2588 var cardel = next_to_card.el.dom;
2590 if (position == 'above' ) {
2591 cardel.parentNode.insertBefore(dom, cardel);
2592 } else if (cardel.nextSibling) {
2593 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2595 cardel.parentNode.append(dom);
2598 // card container???
2599 this.containerEl.dom.append(dom);
2602 //FIXME HANDLE card = true
2604 // add this to the correct place in items.
2606 // remove Card from items.
2609 if (this.items.length) {
2611 //Roo.log([info.items_n, info.position, this.items.length]);
2612 for (var i =0; i < this.items.length; i++) {
2613 if (i == to_items_n && position == 'above') {
2614 nitems.push(move_card);
2616 nitems.push(this.items[i]);
2617 if (i == to_items_n && position == 'below') {
2618 nitems.push(move_card);
2621 this.items = nitems;
2622 Roo.log(this.items);
2624 this.items.push(move_card);
2627 move_card.parentId = this.id;
2633 removeCard : function(c)
2635 this.items = this.items.filter(function(e) { return e != c });
2638 dom.parentNode.removeChild(dom);
2639 dom.style.width = ''; // clear with - which is set by drag.
2644 /** Decide whether to drop above or below a View node. */
2645 getDropPoint : function(e, n, dd)
2650 if (n == this.containerEl.dom) {
2653 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2654 var c = t + (b - t) / 2;
2655 var y = Roo.lib.Event.getPageY(e);
2662 onToggleCollapse : function(e)
2664 if (this.collapsed) {
2665 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2666 this.collapsableEl.addClass('show');
2667 this.collapsed = false;
2670 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2671 this.collapsableEl.removeClass('show');
2672 this.collapsed = true;
2677 onToggleRotate : function(e)
2679 this.collapsableEl.removeClass('show');
2680 this.footerEl.removeClass('d-none');
2681 this.el.removeClass('roo-card-rotated');
2682 this.el.removeClass('d-none');
2685 this.collapsableEl.addClass('show');
2686 this.rotated = false;
2687 this.fireEvent('rotate', this, this.rotated);
2690 this.el.addClass('roo-card-rotated');
2691 this.footerEl.addClass('d-none');
2692 this.el.select('.roo-collapsable').removeClass('show');
2694 this.rotated = true;
2695 this.fireEvent('rotate', this, this.rotated);
2699 dropPlaceHolder: function (action, info, data)
2701 if (this.dropEl === false) {
2702 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2706 this.dropEl.removeClass(['d-none', 'd-block']);
2707 if (action == 'hide') {
2709 this.dropEl.addClass('d-none');
2712 // FIXME - info.card == true!!!
2713 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2715 if (info.card !== true) {
2716 var cardel = info.card.el.dom;
2718 if (info.position == 'above') {
2719 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2720 } else if (cardel.nextSibling) {
2721 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2723 cardel.parentNode.append(this.dropEl.dom);
2726 // card container???
2727 this.containerEl.dom.append(this.dropEl.dom);
2730 this.dropEl.addClass('d-block roo-card-dropzone');
2732 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2739 setHeaderText: function(html)
2742 if (this.headerContainerEl) {
2743 this.headerContainerEl.dom.innerHTML = html;
2746 onHeaderImageLoad : function(ev, he)
2748 if (!this.header_image_fit_square) {
2752 var hw = he.naturalHeight / he.naturalWidth;
2755 //var w = he.dom.naturalWidth;
2758 he.style.position = 'relative';
2760 var nw = (ww * (1/hw));
2761 Roo.get(he).setSize( ww * (1/hw), ww);
2762 he.style.left = ((ww - nw)/ 2) + 'px';
2763 he.style.position = 'relative';
2774 * Card header - holder for the card header elements.
2779 * @class Roo.bootstrap.CardHeader
2780 * @extends Roo.bootstrap.Element
2781 * @parent Roo.bootstrap.Card
2782 * @children Roo.bootstrap.Component
2783 * Bootstrap CardHeader class
2785 * Create a new Card Header - that you can embed children into
2786 * @param {Object} config The config object
2789 Roo.bootstrap.CardHeader = function(config){
2790 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2793 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2796 container_method : 'getCardHeader'
2809 * Card footer - holder for the card footer elements.
2814 * @class Roo.bootstrap.CardFooter
2815 * @extends Roo.bootstrap.Element
2816 * @parent Roo.bootstrap.Card
2817 * @children Roo.bootstrap.Component
2818 * Bootstrap CardFooter class
2821 * Create a new Card Footer - that you can embed children into
2822 * @param {Object} config The config object
2825 Roo.bootstrap.CardFooter = function(config){
2826 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2829 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2832 container_method : 'getCardFooter'
2845 * Card header - holder for the card header elements.
2850 * @class Roo.bootstrap.CardImageTop
2851 * @extends Roo.bootstrap.Element
2852 * @parent Roo.bootstrap.Card
2853 * @children Roo.bootstrap.Component
2854 * Bootstrap CardImageTop class
2857 * Create a new Card Image Top container
2858 * @param {Object} config The config object
2861 Roo.bootstrap.CardImageTop = function(config){
2862 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2865 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2868 container_method : 'getCardImageTop'
2883 * @class Roo.bootstrap.ButtonUploader
2884 * @extends Roo.bootstrap.Button
2885 * Bootstrap Button Uploader class - it's a button which when you add files to it
2888 * @cfg {Number} errorTimeout default 3000
2889 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2890 * @cfg {Array} html The button text.
2891 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2894 * Create a new CardUploader
2895 * @param {Object} config The config object
2898 Roo.bootstrap.ButtonUploader = function(config){
2902 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2908 * @event beforeselect
2909 * When button is pressed, before show upload files dialog is shown
2910 * @param {Roo.bootstrap.UploaderButton} this
2913 'beforeselect' : true,
2915 * @event fired when files have been selected,
2916 * When a the download link is clicked
2917 * @param {Roo.bootstrap.UploaderButton} this
2918 * @param {Array} Array of files that have been uploaded
2925 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2928 errorTimeout : 3000,
2932 fileCollection : false,
2937 getAutoCreate : function()
2944 Roo.bootstrap.Button.prototype.getAutoCreate.call(this)
2952 initEvents : function()
2955 Roo.bootstrap.Button.prototype.initEvents.call(this);
2961 this.urlAPI = (window.createObjectURL && window) ||
2962 (window.URL && URL.revokeObjectURL && URL) ||
2963 (window.webkitURL && webkitURL);
2968 cls : 'd-none roo-card-upload-selector'
2971 if (this.multiple) {
2972 im.multiple = 'multiple';
2974 this.selectorEl = Roo.get(document.body).createChild(im); // so it does not capture click event for navitem.
2976 //this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2978 this.selectorEl.on('change', this.onFileSelected, this);
2985 onClick : function(e)
2989 if ( this.fireEvent('beforeselect', this) === false) {
2993 this.selectorEl.dom.click();
2997 onFileSelected : function(e)
3001 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
3004 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
3005 this.selectorEl.dom.value = '';// hopefully reset..
3007 this.fireEvent('uploaded', this, files );
3015 * addCard - add an Attachment to the uploader
3016 * @param data - the data about the image to upload
3020 title : "Title of file",
3021 is_uploaded : false,
3022 src : "http://.....",
3023 srcfile : { the File upload object },
3024 mimetype : file.type,
3027 .. any other data...
3052 * @class Roo.bootstrap.Img
3053 * @extends Roo.bootstrap.Component
3054 * Bootstrap Img class
3055 * @cfg {Boolean} imgResponsive false | true
3056 * @cfg {String} border rounded | circle | thumbnail
3057 * @cfg {String} src image source
3058 * @cfg {String} alt image alternative text
3059 * @cfg {String} href a tag href
3060 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3061 * @cfg {String} xsUrl xs image source
3062 * @cfg {String} smUrl sm image source
3063 * @cfg {String} mdUrl md image source
3064 * @cfg {String} lgUrl lg image source
3065 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3068 * Create a new Input
3069 * @param {Object} config The config object
3072 Roo.bootstrap.Img = function(config){
3073 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3079 * The img click event for the img.
3080 * @param {Roo.EventObject} e
3085 * The when any image loads
3086 * @param {Roo.EventObject} e
3092 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3094 imgResponsive: true,
3103 backgroundContain : false,
3105 getAutoCreate : function()
3107 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3108 return this.createSingleImg();
3113 cls: 'roo-image-responsive-group',
3118 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3120 if(!_this[size + 'Url']){
3126 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3127 html: _this.html || cfg.html,
3128 src: _this[size + 'Url']
3131 img.cls += ' roo-image-responsive-' + size;
3133 var s = ['xs', 'sm', 'md', 'lg'];
3135 s.splice(s.indexOf(size), 1);
3137 Roo.each(s, function(ss){
3138 img.cls += ' hidden-' + ss;
3141 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3142 cfg.cls += ' img-' + _this.border;
3146 cfg.alt = _this.alt;
3159 a.target = _this.target;
3163 cfg.cn.push((_this.href) ? a : img);
3170 createSingleImg : function()
3174 cls: (this.imgResponsive) ? 'img-responsive' : '',
3176 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3179 if (this.backgroundContain) {
3180 cfg.cls += ' background-contain';
3183 cfg.html = this.html || cfg.html;
3185 if (this.backgroundContain) {
3186 cfg.style="background-image: url(" + this.src + ')';
3188 cfg.src = this.src || cfg.src;
3191 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3192 cfg.cls += ' img-' + this.border;
3209 a.target = this.target;
3214 return (this.href) ? a : cfg;
3217 initEvents: function()
3220 this.el.on('click', this.onClick, this);
3222 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3223 this.el.on('load', this.onImageLoad, this);
3225 // not sure if this works.. not tested
3226 this.el.select('img', true).on('load', this.onImageLoad, this);
3231 onClick : function(e)
3233 Roo.log('img onclick');
3234 this.fireEvent('click', this, e);
3236 onImageLoad: function(e)
3238 Roo.log('img load');
3239 this.fireEvent('load', this, e);
3243 * Sets the url of the image - used to update it
3244 * @param {String} url the url of the image
3247 setSrc : function(url)
3251 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3252 if (this.backgroundContain) {
3253 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3255 this.el.dom.src = url;
3260 this.el.select('img', true).first().dom.src = url;
3276 * @class Roo.bootstrap.Link
3277 * @extends Roo.bootstrap.Component
3278 * @children Roo.bootstrap.Component
3279 * Bootstrap Link Class (eg. '<a href>')
3281 * @cfg {String} alt image alternative text
3282 * @cfg {String} href a tag href
3283 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3284 * @cfg {String} html the content of the link.
3285 * @cfg {String} anchor name for the anchor link
3286 * @cfg {String} fa - favicon
3288 * @cfg {Boolean} preventDefault (true | false) default false
3292 * Create a new Input
3293 * @param {Object} config The config object
3296 Roo.bootstrap.Link = function(config){
3297 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3303 * The img click event for the img.
3304 * @param {Roo.EventObject} e
3310 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3314 preventDefault: false,
3320 getAutoCreate : function()
3322 var html = this.html || '';
3324 if (this.fa !== false) {
3325 html = '<i class="fa fa-' + this.fa + '"></i>';
3330 // anchor's do not require html/href...
3331 if (this.anchor === false) {
3333 cfg.href = this.href || '#';
3335 cfg.name = this.anchor;
3336 if (this.html !== false || this.fa !== false) {
3339 if (this.href !== false) {
3340 cfg.href = this.href;
3344 if(this.alt !== false){
3349 if(this.target !== false) {
3350 cfg.target = this.target;
3356 initEvents: function() {
3358 if(!this.href || this.preventDefault){
3359 this.el.on('click', this.onClick, this);
3363 onClick : function(e)
3365 if(this.preventDefault){
3368 //Roo.log('img onclick');
3369 this.fireEvent('click', this, e);
3382 * @class Roo.bootstrap.Header
3383 * @extends Roo.bootstrap.Component
3384 * @children Roo.bootstrap.Component
3385 * Bootstrap Header class
3388 * @cfg {String} html content of header
3389 * @cfg {Number} level (1|2|3|4|5|6) default 1
3392 * Create a new Header
3393 * @param {Object} config The config object
3397 Roo.bootstrap.Header = function(config){
3398 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3401 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3409 getAutoCreate : function(){
3414 tag: 'h' + (1 *this.level),
3415 html: this.html || ''
3426 * @class Roo.bootstrap.MenuMgr
3428 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3431 Roo.bootstrap.menu.Manager = function(){
3432 var menus, active, groups = {}, attached = false, lastShow = new Date();
3434 // private - called when first menu is created
3437 active = new Roo.util.MixedCollection();
3438 Roo.get(document).addKeyListener(27, function(){
3439 if(active.length > 0){
3447 if(active && active.length > 0){
3448 var c = active.clone();
3458 if(active.length < 1){
3459 Roo.get(document).un("mouseup", onMouseDown);
3467 var last = active.last();
3468 lastShow = new Date();
3471 Roo.get(document).on("mouseup", onMouseDown);
3476 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3477 m.parentMenu.activeChild = m;
3478 }else if(last && last.isVisible()){
3479 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3484 function onBeforeHide(m){
3486 m.activeChild.hide();
3488 if(m.autoHideTimer){
3489 clearTimeout(m.autoHideTimer);
3490 delete m.autoHideTimer;
3495 function onBeforeShow(m){
3496 var pm = m.parentMenu;
3497 if(!pm && !m.allowOtherMenus){
3499 }else if(pm && pm.activeChild && active != m){
3500 pm.activeChild.hide();
3504 // private this should really trigger on mouseup..
3505 function onMouseDown(e){
3506 Roo.log("on Mouse Up");
3508 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3509 Roo.log("MenuManager hideAll");
3518 function onBeforeCheck(mi, state){
3520 var g = groups[mi.group];
3521 for(var i = 0, l = g.length; i < l; i++){
3523 g[i].setChecked(false);
3532 * Hides all menus that are currently visible
3534 hideAll : function(){
3539 register : function(menu){
3543 menus[menu.id] = menu;
3544 menu.on("beforehide", onBeforeHide);
3545 menu.on("hide", onHide);
3546 menu.on("beforeshow", onBeforeShow);
3547 menu.on("show", onShow);
3549 if(g && menu.events["checkchange"]){
3553 groups[g].push(menu);
3554 menu.on("checkchange", onCheck);
3559 * Returns a {@link Roo.menu.Menu} object
3560 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3561 * be used to generate and return a new Menu instance.
3563 get : function(menu){
3564 if(typeof menu == "string"){ // menu id
3566 }else if(menu.events){ // menu instance
3569 /*else if(typeof menu.length == 'number'){ // array of menu items?
3570 return new Roo.bootstrap.Menu({items:menu});
3571 }else{ // otherwise, must be a config
3572 return new Roo.bootstrap.Menu(menu);
3579 unregister : function(menu){
3580 delete menus[menu.id];
3581 menu.un("beforehide", onBeforeHide);
3582 menu.un("hide", onHide);
3583 menu.un("beforeshow", onBeforeShow);
3584 menu.un("show", onShow);
3586 if(g && menu.events["checkchange"]){
3587 groups[g].remove(menu);
3588 menu.un("checkchange", onCheck);
3593 registerCheckable : function(menuItem){
3594 var g = menuItem.group;
3599 groups[g].push(menuItem);
3600 menuItem.on("beforecheckchange", onBeforeCheck);
3605 unregisterCheckable : function(menuItem){
3606 var g = menuItem.group;
3608 groups[g].remove(menuItem);
3609 menuItem.un("beforecheckchange", onBeforeCheck);
3615 * @class Roo.bootstrap.menu.Menu
3616 * @extends Roo.bootstrap.Component
3618 * @children Roo.bootstrap.menu.Item Roo.bootstrap.menu.Separator
3620 * Bootstrap Menu class - container for MenuItems - normally has to be added to a object that supports the menu property
3622 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3623 * @cfg {bool} hidden if the menu should be hidden when rendered.
3624 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3625 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3626 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3627 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3631 * @param {Object} config The config objectQ
3635 Roo.bootstrap.menu.Menu = function(config){
3637 if (config.type == 'treeview') {
3638 // normally menu's are drawn attached to the document to handle layering etc..
3639 // however treeview (used by the docs menu is drawn into the parent element)
3640 this.container_method = 'getChildContainer';
3643 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
3644 if (this.registerMenu && this.type != 'treeview') {
3645 Roo.bootstrap.menu.Manager.register(this);
3652 * Fires before this menu is displayed (return false to block)
3653 * @param {Roo.menu.Menu} this
3658 * Fires before this menu is hidden (return false to block)
3659 * @param {Roo.menu.Menu} this
3664 * Fires after this menu is displayed
3665 * @param {Roo.menu.Menu} this
3670 * Fires after this menu is hidden
3671 * @param {Roo.menu.Menu} this
3676 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3677 * @param {Roo.menu.Menu} this
3678 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3679 * @param {Roo.EventObject} e
3684 * Fires when the mouse is hovering over this menu
3685 * @param {Roo.menu.Menu} this
3686 * @param {Roo.EventObject} e
3687 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3692 * Fires when the mouse exits this menu
3693 * @param {Roo.menu.Menu} this
3694 * @param {Roo.EventObject} e
3695 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3700 * Fires when a menu item contained in this menu is clicked
3701 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3702 * @param {Roo.EventObject} e
3706 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3709 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
3713 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3716 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3718 registerMenu : true,
3720 menuItems :false, // stores the menu items..
3730 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3732 hideTrigger : false,
3737 getChildContainer : function() {
3741 getAutoCreate : function(){
3743 //if (['right'].indexOf(this.align)!==-1) {
3744 // cfg.cn[1].cls += ' pull-right'
3749 cls : 'dropdown-menu shadow' ,
3750 style : 'z-index:1000'
3754 if (this.type === 'submenu') {
3755 cfg.cls = 'submenu active';
3757 if (this.type === 'treeview') {
3758 cfg.cls = 'treeview-menu';
3763 initEvents : function() {
3765 // Roo.log("ADD event");
3766 // Roo.log(this.triggerEl.dom);
3767 if (this.triggerEl) {
3769 this.triggerEl.on('click', this.onTriggerClick, this);
3771 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3773 if (!this.hideTrigger) {
3774 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3775 // dropdown toggle on the 'a' in BS4?
3776 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3778 this.triggerEl.addClass('dropdown-toggle');
3784 this.el.on('touchstart' , this.onTouch, this);
3786 this.el.on('click' , this.onClick, this);
3788 this.el.on("mouseover", this.onMouseOver, this);
3789 this.el.on("mouseout", this.onMouseOut, this);
3793 findTargetItem : function(e)
3795 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3799 //Roo.log(t); Roo.log(t.id);
3801 //Roo.log(this.menuitems);
3802 return this.menuitems.get(t.id);
3804 //return this.items.get(t.menuItemId);
3810 onTouch : function(e)
3812 Roo.log("menu.onTouch");
3813 //e.stopEvent(); this make the user popdown broken
3817 onClick : function(e)
3819 Roo.log("menu.onClick");
3821 var t = this.findTargetItem(e);
3822 if(!t || t.isContainer){
3827 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3828 if(t == this.activeItem && t.shouldDeactivate(e)){
3829 this.activeItem.deactivate();
3830 delete this.activeItem;
3834 this.setActiveItem(t, true);
3842 Roo.log('pass click event');
3846 this.fireEvent("click", this, t, e);
3850 if(!t.href.length || t.href == '#'){
3851 (function() { _this.hide(); }).defer(100);
3856 onMouseOver : function(e){
3857 var t = this.findTargetItem(e);
3860 // if(t.canActivate && !t.disabled){
3861 // this.setActiveItem(t, true);
3865 this.fireEvent("mouseover", this, e, t);
3867 isVisible : function(){
3868 return !this.hidden;
3870 onMouseOut : function(e){
3871 var t = this.findTargetItem(e);
3874 // if(t == this.activeItem && t.shouldDeactivate(e)){
3875 // this.activeItem.deactivate();
3876 // delete this.activeItem;
3879 this.fireEvent("mouseout", this, e, t);
3884 * Displays this menu relative to another element
3885 * @param {String/HTMLElement/Roo.Element} element The element to align to
3886 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3887 * the element (defaults to this.defaultAlign)
3888 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3890 show : function(el, pos, parentMenu)
3892 if (false === this.fireEvent("beforeshow", this)) {
3893 Roo.log("show canceled");
3896 this.parentMenu = parentMenu;
3900 this.el.addClass('show'); // show otherwise we do not know how big we are..
3902 var xy = this.el.getAlignToXY(el, pos);
3904 // bl-tl << left align below
3905 // tl-bl << left align
3907 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3908 // if it goes to far to the right.. -> align left.
3909 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3912 // was left align - go right?
3913 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3916 // goes down the bottom
3917 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3919 var a = this.align.replace('?', '').split('-');
3920 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3924 this.showAt( xy , parentMenu, false);
3927 * Displays this menu at a specific xy position
3928 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3929 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3931 showAt : function(xy, parentMenu, /* private: */_e){
3932 this.parentMenu = parentMenu;
3937 this.fireEvent("beforeshow", this);
3938 //xy = this.el.adjustForConstraints(xy);
3942 this.hideMenuItems();
3943 this.hidden = false;
3944 if (this.triggerEl) {
3945 this.triggerEl.addClass('open');
3948 this.el.addClass('show');
3952 // reassign x when hitting right
3954 // reassign y when hitting bottom
3956 // but the list may align on trigger left or trigger top... should it be a properity?
3958 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3963 this.fireEvent("show", this);
3969 this.doFocus.defer(50, this);
3973 doFocus : function(){
3975 this.focusEl.focus();
3980 * Hides this menu and optionally all parent menus
3981 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3983 hide : function(deep)
3985 if (false === this.fireEvent("beforehide", this)) {
3986 Roo.log("hide canceled");
3989 this.hideMenuItems();
3990 if(this.el && this.isVisible()){
3992 if(this.activeItem){
3993 this.activeItem.deactivate();
3994 this.activeItem = null;
3996 if (this.triggerEl) {
3997 this.triggerEl.removeClass('open');
4000 this.el.removeClass('show');
4002 this.fireEvent("hide", this);
4004 if(deep === true && this.parentMenu){
4005 this.parentMenu.hide(true);
4009 onTriggerClick : function(e)
4011 Roo.log('trigger click');
4013 var target = e.getTarget();
4015 Roo.log(target.nodeName.toLowerCase());
4017 if(target.nodeName.toLowerCase() === 'i'){
4023 onTriggerPress : function(e)
4025 Roo.log('trigger press');
4026 //Roo.log(e.getTarget());
4027 // Roo.log(this.triggerEl.dom);
4029 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4030 var pel = Roo.get(e.getTarget());
4031 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4032 Roo.log('is treeview or dropdown?');
4036 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4040 if (this.isVisible()) {
4046 this.show(this.triggerEl, this.align, false);
4049 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4056 hideMenuItems : function()
4058 Roo.log("hide Menu Items");
4063 this.el.select('.open',true).each(function(aa) {
4065 aa.removeClass('open');
4069 addxtypeChild : function (tree, cntr) {
4070 var comp= Roo.bootstrap.menu.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4072 this.menuitems.add(comp);
4084 this.getEl().dom.innerHTML = '';
4085 this.menuitems.clear();
4091 * @class Roo.bootstrap.menu.Item
4092 * @extends Roo.bootstrap.Component
4093 * @children Roo.bootstrap.Button Roo.bootstrap.ButtonUploader Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Container
4094 * @parent Roo.bootstrap.menu.Menu
4096 * Bootstrap MenuItem class
4098 * @cfg {String} html the menu label
4099 * @cfg {String} href the link
4100 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4101 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4102 * @cfg {Boolean} active used on sidebars to highlight active itesm
4103 * @cfg {String} fa favicon to show on left of menu item.
4104 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4108 * Create a new MenuItem
4109 * @param {Object} config The config object
4113 Roo.bootstrap.menu.Item = function(config){
4114 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
4119 * The raw click event for the entire grid.
4120 * @param {Roo.bootstrap.menu.Item} this
4121 * @param {Roo.EventObject} e
4127 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
4131 preventDefault: false,
4132 isContainer : false,
4136 getAutoCreate : function(){
4138 if(this.isContainer){
4141 cls: 'dropdown-menu-item '
4151 cls : 'dropdown-item',
4156 if (this.fa !== false) {
4159 cls : 'fa fa-' + this.fa
4168 cls: 'dropdown-menu-item',
4171 if (this.parent().type == 'treeview') {
4172 cfg.cls = 'treeview-menu';
4175 cfg.cls += ' active';
4180 anc.href = this.href || cfg.cn[0].href ;
4181 ctag.html = this.html || cfg.cn[0].html ;
4185 initEvents: function()
4187 if (this.parent().type == 'treeview') {
4188 this.el.select('a').on('click', this.onClick, this);
4192 this.menu.parentType = this.xtype;
4193 this.menu.triggerEl = this.el;
4194 this.menu = this.addxtype(Roo.apply({}, this.menu));
4198 onClick : function(e)
4200 //Roo.log('item on click ');
4202 if(this.href === false || this.preventDefault){
4205 //this.parent().hideMenuItems();
4207 this.fireEvent('click', this, e);
4221 * @class Roo.bootstrap.menu.Separator
4222 * @extends Roo.bootstrap.Component
4224 * @parent Roo.bootstrap.menu.Menu
4225 * Bootstrap Separator class
4228 * Create a new Separator
4229 * @param {Object} config The config object
4233 Roo.bootstrap.menu.Separator = function(config){
4234 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
4237 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
4239 getAutoCreate : function(){
4242 cls: 'dropdown-divider divider'
4258 * @class Roo.bootstrap.Modal
4259 * @extends Roo.bootstrap.Component
4260 * @parent none builder
4261 * @children Roo.bootstrap.Component
4262 * Bootstrap Modal class
4263 * @cfg {String} title Title of dialog
4264 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4265 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4266 * @cfg {Boolean} specificTitle default false
4267 * @cfg {Roo.bootstrap.Button} buttons[] Array of buttons or standard button set..
4268 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4269 * @cfg {Boolean} animate default true
4270 * @cfg {Boolean} allow_close default true
4271 * @cfg {Boolean} fitwindow default false
4272 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4273 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4274 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4275 * @cfg {String} size (sm|lg|xl) default empty
4276 * @cfg {Number} max_width set the max width of modal
4277 * @cfg {Boolean} editableTitle can the title be edited
4282 * Create a new Modal Dialog
4283 * @param {Object} config The config object
4286 Roo.bootstrap.Modal = function(config){
4287 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4292 * The raw btnclick event for the button
4293 * @param {Roo.EventObject} e
4298 * Fire when dialog resize
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} e
4304 * @event titlechanged
4305 * Fire when the editable title has been changed
4306 * @param {Roo.bootstrap.Modal} this
4307 * @param {Roo.EventObject} value
4309 "titlechanged" : true
4312 this.buttons = this.buttons || [];
4315 this.tmpl = Roo.factory(this.tmpl);
4320 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4322 title : 'test dialog',
4332 specificTitle: false,
4334 buttonPosition: 'right',
4356 editableTitle : false,
4358 onRender : function(ct, position)
4360 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4363 var cfg = Roo.apply({}, this.getAutoCreate());
4366 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4368 //if (!cfg.name.length) {
4372 cfg.cls += ' ' + this.cls;
4375 cfg.style = this.style;
4377 this.el = Roo.get(document.body).createChild(cfg, position);
4379 //var type = this.el.dom.type;
4382 if(this.tabIndex !== undefined){
4383 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4386 this.dialogEl = this.el.select('.modal-dialog',true).first();
4387 this.bodyEl = this.el.select('.modal-body',true).first();
4388 this.closeEl = this.el.select('.modal-header .close', true).first();
4389 this.headerEl = this.el.select('.modal-header',true).first();
4390 this.titleEl = this.el.select('.modal-title',true).first();
4391 this.footerEl = this.el.select('.modal-footer',true).first();
4393 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4395 //this.el.addClass("x-dlg-modal");
4397 if (this.buttons.length) {
4398 Roo.each(this.buttons, function(bb) {
4399 var b = Roo.apply({}, bb);
4400 b.xns = b.xns || Roo.bootstrap;
4401 b.xtype = b.xtype || 'Button';
4402 if (typeof(b.listeners) == 'undefined') {
4403 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4406 var btn = Roo.factory(b);
4408 btn.render(this.getButtonContainer());
4412 // render the children.
4415 if(typeof(this.items) != 'undefined'){
4416 var items = this.items;
4419 for(var i =0;i < items.length;i++) {
4420 // we force children not to montor widnow resize - as we do that for them.
4421 items[i].monitorWindowResize = false;
4422 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4426 this.items = nitems;
4428 // where are these used - they used to be body/close/footer
4432 //this.el.addClass([this.fieldClass, this.cls]);
4436 getAutoCreate : function()
4438 // we will default to modal-body-overflow - might need to remove or make optional later.
4440 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4441 html : this.html || ''
4446 cls : 'modal-title',
4450 if(this.specificTitle){ // WTF is this?
4455 if (this.allow_close && Roo.bootstrap.version == 3) {
4465 if (this.editableTitle) {
4467 cls: 'form-control roo-editable-title d-none',
4473 if (this.allow_close && Roo.bootstrap.version == 4) {
4483 if(this.size.length){
4484 size = 'modal-' + this.size;
4487 var footer = Roo.bootstrap.version == 3 ?
4489 cls : 'modal-footer',
4493 cls: 'btn-' + this.buttonPosition
4498 { // BS4 uses mr-auto on left buttons....
4499 cls : 'modal-footer'
4510 cls: "modal-dialog " + size,
4513 cls : "modal-content",
4516 cls : 'modal-header',
4531 modal.cls += ' fade';
4537 getChildContainer : function() {
4542 getButtonContainer : function() {
4544 return Roo.bootstrap.version == 4 ?
4545 this.el.select('.modal-footer',true).first()
4546 : this.el.select('.modal-footer div',true).first();
4549 initEvents : function()
4551 if (this.allow_close) {
4552 this.closeEl.on('click', this.hide, this);
4554 Roo.EventManager.onWindowResize(this.resize, this, true);
4555 if (this.editableTitle) {
4556 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4557 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4558 this.headerEditEl.on('keyup', function(e) {
4559 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4560 this.toggleHeaderInput(false)
4563 this.headerEditEl.on('blur', function(e) {
4564 this.toggleHeaderInput(false)
4573 this.maskEl.setSize(
4574 Roo.lib.Dom.getViewWidth(true),
4575 Roo.lib.Dom.getViewHeight(true)
4578 if (this.fitwindow) {
4580 this.dialogEl.setStyle( { 'max-width' : '100%' });
4582 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4583 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4588 if(this.max_width !== 0) {
4590 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4593 this.setSize(w, this.height);
4597 if(this.max_height) {
4598 this.setSize(w,Math.min(
4600 Roo.lib.Dom.getViewportHeight(true) - 60
4606 if(!this.fit_content) {
4607 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4611 this.setSize(w, Math.min(
4613 this.headerEl.getHeight() +
4614 this.footerEl.getHeight() +
4615 this.getChildHeight(this.bodyEl.dom.childNodes),
4616 Roo.lib.Dom.getViewportHeight(true) - 60)
4622 setSize : function(w,h)
4629 // any layout/border etc.. resize..
4631 this.items.forEach( function(e) {
4632 e.layout ? e.layout() : false;
4641 if (!this.rendered) {
4644 this.toggleHeaderInput(false);
4645 //this.el.setStyle('display', 'block');
4646 this.el.removeClass('hideing');
4647 this.el.dom.style.display='block';
4649 Roo.get(document.body).addClass('modal-open');
4651 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4654 this.el.addClass('show');
4655 this.el.addClass('in');
4658 this.el.addClass('show');
4659 this.el.addClass('in');
4662 // not sure how we can show data in here..
4664 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4667 Roo.get(document.body).addClass("x-body-masked");
4669 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4670 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4671 this.maskEl.dom.style.display = 'block';
4672 this.maskEl.addClass('show');
4677 this.fireEvent('show', this);
4679 // set zindex here - otherwise it appears to be ignored...
4680 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4683 // this is for children that are... layout.Border
4685 this.items.forEach( function(e) {
4686 e.layout ? e.layout() : false;
4694 if(this.fireEvent("beforehide", this) !== false){
4696 this.maskEl.removeClass('show');
4698 this.maskEl.dom.style.display = '';
4699 Roo.get(document.body).removeClass("x-body-masked");
4700 this.el.removeClass('in');
4701 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4703 if(this.animate){ // why
4704 this.el.addClass('hideing');
4705 this.el.removeClass('show');
4707 if (!this.el.hasClass('hideing')) {
4708 return; // it's been shown again...
4711 this.el.dom.style.display='';
4713 Roo.get(document.body).removeClass('modal-open');
4714 this.el.removeClass('hideing');
4718 this.el.removeClass('show');
4719 this.el.dom.style.display='';
4720 Roo.get(document.body).removeClass('modal-open');
4723 this.fireEvent('hide', this);
4726 isVisible : function()
4729 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4733 addButton : function(str, cb)
4737 var b = Roo.apply({}, { html : str } );
4738 b.xns = b.xns || Roo.bootstrap;
4739 b.xtype = b.xtype || 'Button';
4740 if (typeof(b.listeners) == 'undefined') {
4741 b.listeners = { click : cb.createDelegate(this) };
4744 var btn = Roo.factory(b);
4746 btn.render(this.getButtonContainer());
4752 setDefaultButton : function(btn)
4754 //this.el.select('.modal-footer').()
4757 resizeTo: function(w,h)
4759 this.dialogEl.setWidth(w);
4761 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4763 this.bodyEl.setHeight(h - diff);
4765 this.fireEvent('resize', this);
4768 setContentSize : function(w, h)
4772 onButtonClick: function(btn,e)
4775 this.fireEvent('btnclick', btn.name, e);
4778 * Set the title of the Dialog
4779 * @param {String} str new Title
4781 setTitle: function(str) {
4782 this.titleEl.dom.innerHTML = str;
4786 * Set the body of the Dialog
4787 * @param {String} str new Title
4789 setBody: function(str) {
4790 this.bodyEl.dom.innerHTML = str;
4793 * Set the body of the Dialog using the template
4794 * @param {Obj} data - apply this data to the template and replace the body contents.
4796 applyBody: function(obj)
4799 Roo.log("Error - using apply Body without a template");
4802 this.tmpl.overwrite(this.bodyEl, obj);
4805 getChildHeight : function(child_nodes)
4809 child_nodes.length == 0
4814 var child_height = 0;
4816 for(var i = 0; i < child_nodes.length; i++) {
4819 * for modal with tabs...
4820 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4822 var layout_childs = child_nodes[i].childNodes;
4824 for(var j = 0; j < layout_childs.length; j++) {
4826 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4828 var layout_body_childs = layout_childs[j].childNodes;
4830 for(var k = 0; k < layout_body_childs.length; k++) {
4832 if(layout_body_childs[k].classList.contains('navbar')) {
4833 child_height += layout_body_childs[k].offsetHeight;
4837 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4839 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4841 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4843 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4844 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4859 child_height += child_nodes[i].offsetHeight;
4860 // Roo.log(child_nodes[i].offsetHeight);
4863 return child_height;
4865 toggleHeaderInput : function(is_edit)
4867 if (!this.editableTitle) {
4868 return; // not editable.
4870 if (is_edit && this.is_header_editing) {
4871 return; // already editing..
4875 this.headerEditEl.dom.value = this.title;
4876 this.headerEditEl.removeClass('d-none');
4877 this.headerEditEl.dom.focus();
4878 this.titleEl.addClass('d-none');
4880 this.is_header_editing = true;
4883 // flip back to not editing.
4884 this.title = this.headerEditEl.dom.value;
4885 this.headerEditEl.addClass('d-none');
4886 this.titleEl.removeClass('d-none');
4887 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4888 this.is_header_editing = false;
4889 this.fireEvent('titlechanged', this, this.title);
4898 Roo.apply(Roo.bootstrap.Modal, {
4900 * Button config that displays a single OK button
4909 * Button config that displays Yes and No buttons
4925 * Button config that displays OK and Cancel buttons
4940 * Button config that displays Yes, No and Cancel buttons
4965 * messagebox - can be used as a replace
4969 * @class Roo.MessageBox
4970 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4974 Roo.Msg.alert('Status', 'Changes saved successfully.');
4976 // Prompt for user data:
4977 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4979 // process text value...
4983 // Show a dialog using config options:
4985 title:'Save Changes?',
4986 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4987 buttons: Roo.Msg.YESNOCANCEL,
4994 Roo.bootstrap.MessageBox = function(){
4995 var dlg, opt, mask, waitTimer;
4996 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4997 var buttons, activeTextEl, bwidth;
5001 var handleButton = function(button){
5003 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
5007 var handleHide = function(){
5009 dlg.el.removeClass(opt.cls);
5012 // Roo.TaskMgr.stop(waitTimer);
5013 // waitTimer = null;
5018 var updateButtons = function(b){
5021 buttons["ok"].hide();
5022 buttons["cancel"].hide();
5023 buttons["yes"].hide();
5024 buttons["no"].hide();
5025 dlg.footerEl.hide();
5029 dlg.footerEl.show();
5030 for(var k in buttons){
5031 if(typeof buttons[k] != "function"){
5034 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5035 width += buttons[k].el.getWidth()+15;
5045 var handleEsc = function(d, k, e){
5046 if(opt && opt.closable !== false){
5056 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5057 * @return {Roo.BasicDialog} The BasicDialog element
5059 getDialog : function(){
5061 dlg = new Roo.bootstrap.Modal( {
5064 //constraintoviewport:false,
5066 //collapsible : false,
5071 //buttonAlign:"center",
5072 closeClick : function(){
5073 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5076 handleButton("cancel");
5081 dlg.on("hide", handleHide);
5083 //dlg.addKeyListener(27, handleEsc);
5085 this.buttons = buttons;
5086 var bt = this.buttonText;
5087 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5088 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5089 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5090 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5092 bodyEl = dlg.bodyEl.createChild({
5094 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5095 '<textarea class="roo-mb-textarea"></textarea>' +
5096 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5098 msgEl = bodyEl.dom.firstChild;
5099 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5100 textboxEl.enableDisplayMode();
5101 textboxEl.addKeyListener([10,13], function(){
5102 if(dlg.isVisible() && opt && opt.buttons){
5105 }else if(opt.buttons.yes){
5106 handleButton("yes");
5110 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5111 textareaEl.enableDisplayMode();
5112 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5113 progressEl.enableDisplayMode();
5115 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5116 var pf = progressEl.dom.firstChild;
5118 pp = Roo.get(pf.firstChild);
5119 pp.setHeight(pf.offsetHeight);
5127 * Updates the message box body text
5128 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5129 * the XHTML-compliant non-breaking space character '&#160;')
5130 * @return {Roo.MessageBox} This message box
5132 updateText : function(text)
5134 if(!dlg.isVisible() && !opt.width){
5135 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5136 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5138 msgEl.innerHTML = text || ' ';
5140 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5141 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5143 Math.min(opt.width || cw , this.maxWidth),
5144 Math.max(opt.minWidth || this.minWidth, bwidth)
5147 activeTextEl.setWidth(w);
5149 if(dlg.isVisible()){
5150 dlg.fixedcenter = false;
5152 // to big, make it scroll. = But as usual stupid IE does not support
5155 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5156 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5157 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5159 bodyEl.dom.style.height = '';
5160 bodyEl.dom.style.overflowY = '';
5163 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5165 bodyEl.dom.style.overflowX = '';
5168 dlg.setContentSize(w, bodyEl.getHeight());
5169 if(dlg.isVisible()){
5170 dlg.fixedcenter = true;
5176 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5177 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5178 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5179 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5180 * @return {Roo.MessageBox} This message box
5182 updateProgress : function(value, text){
5184 this.updateText(text);
5187 if (pp) { // weird bug on my firefox - for some reason this is not defined
5188 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5189 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5195 * Returns true if the message box is currently displayed
5196 * @return {Boolean} True if the message box is visible, else false
5198 isVisible : function(){
5199 return dlg && dlg.isVisible();
5203 * Hides the message box if it is displayed
5206 if(this.isVisible()){
5212 * Displays a new message box, or reinitializes an existing message box, based on the config options
5213 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5214 * The following config object properties are supported:
5216 Property Type Description
5217 ---------- --------------- ------------------------------------------------------------------------------------
5218 animEl String/Element An id or Element from which the message box should animate as it opens and
5219 closes (defaults to undefined)
5220 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5221 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5222 closable Boolean False to hide the top-right close button (defaults to true). Note that
5223 progress and wait dialogs will ignore this property and always hide the
5224 close button as they can only be closed programmatically.
5225 cls String A custom CSS class to apply to the message box element
5226 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5227 displayed (defaults to 75)
5228 fn Function A callback function to execute after closing the dialog. The arguments to the
5229 function will be btn (the name of the button that was clicked, if applicable,
5230 e.g. "ok"), and text (the value of the active text field, if applicable).
5231 Progress and wait dialogs will ignore this option since they do not respond to
5232 user actions and can only be closed programmatically, so any required function
5233 should be called by the same code after it closes the dialog.
5234 icon String A CSS class that provides a background image to be used as an icon for
5235 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5236 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5237 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5238 modal Boolean False to allow user interaction with the page while the message box is
5239 displayed (defaults to true)
5240 msg String A string that will replace the existing message box body text (defaults
5241 to the XHTML-compliant non-breaking space character ' ')
5242 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5243 progress Boolean True to display a progress bar (defaults to false)
5244 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5245 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5246 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5247 title String The title text
5248 value String The string value to set into the active textbox element if displayed
5249 wait Boolean True to display a progress bar (defaults to false)
5250 width Number The width of the dialog in pixels
5257 msg: 'Please enter your address:',
5259 buttons: Roo.MessageBox.OKCANCEL,
5262 animEl: 'addAddressBtn'
5265 * @param {Object} config Configuration options
5266 * @return {Roo.MessageBox} This message box
5268 show : function(options)
5271 // this causes nightmares if you show one dialog after another
5272 // especially on callbacks..
5274 if(this.isVisible()){
5277 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5278 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5279 Roo.log("New Dialog Message:" + options.msg )
5280 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5281 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5284 var d = this.getDialog();
5286 d.setTitle(opt.title || " ");
5287 d.closeEl.setDisplayed(opt.closable !== false);
5288 activeTextEl = textboxEl;
5289 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5294 textareaEl.setHeight(typeof opt.multiline == "number" ?
5295 opt.multiline : this.defaultTextHeight);
5296 activeTextEl = textareaEl;
5305 progressEl.setDisplayed(opt.progress === true);
5307 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5309 this.updateProgress(0);
5310 activeTextEl.dom.value = opt.value || "";
5312 dlg.setDefaultButton(activeTextEl);
5314 var bs = opt.buttons;
5318 }else if(bs && bs.yes){
5319 db = buttons["yes"];
5321 dlg.setDefaultButton(db);
5323 bwidth = updateButtons(opt.buttons);
5324 this.updateText(opt.msg);
5326 d.el.addClass(opt.cls);
5328 d.proxyDrag = opt.proxyDrag === true;
5329 d.modal = opt.modal !== false;
5330 d.mask = opt.modal !== false ? mask : false;
5332 // force it to the end of the z-index stack so it gets a cursor in FF
5333 document.body.appendChild(dlg.el.dom);
5334 d.animateTarget = null;
5335 d.show(options.animEl);
5341 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5342 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5343 * and closing the message box when the process is complete.
5344 * @param {String} title The title bar text
5345 * @param {String} msg The message box body text
5346 * @return {Roo.MessageBox} This message box
5348 progress : function(title, msg){
5355 minWidth: this.minProgressWidth,
5362 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5363 * If a callback function is passed it will be called after the user clicks the button, and the
5364 * id of the button that was clicked will be passed as the only parameter to the callback
5365 * (could also be the top-right close button).
5366 * @param {String} title The title bar text
5367 * @param {String} msg The message box body text
5368 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5369 * @param {Object} scope (optional) The scope of the callback function
5370 * @return {Roo.MessageBox} This message box
5372 alert : function(title, msg, fn, scope)
5387 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5388 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5389 * You are responsible for closing the message box when the process is complete.
5390 * @param {String} msg The message box body text
5391 * @param {String} title (optional) The title bar text
5392 * @return {Roo.MessageBox} This message box
5394 wait : function(msg, title){
5405 waitTimer = Roo.TaskMgr.start({
5407 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5415 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5416 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5417 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5418 * @param {String} title The title bar text
5419 * @param {String} msg The message box body text
5420 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5421 * @param {Object} scope (optional) The scope of the callback function
5422 * @return {Roo.MessageBox} This message box
5424 confirm : function(title, msg, fn, scope){
5428 buttons: this.YESNO,
5437 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5438 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5439 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5440 * (could also be the top-right close button) and the text that was entered will be passed as the two
5441 * parameters to the callback.
5442 * @param {String} title The title bar text
5443 * @param {String} msg The message box body text
5444 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5445 * @param {Object} scope (optional) The scope of the callback function
5446 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5447 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5448 * @return {Roo.MessageBox} This message box
5450 prompt : function(title, msg, fn, scope, multiline){
5454 buttons: this.OKCANCEL,
5459 multiline: multiline,
5466 * Button config that displays a single OK button
5471 * Button config that displays Yes and No buttons
5474 YESNO : {yes:true, no:true},
5476 * Button config that displays OK and Cancel buttons
5479 OKCANCEL : {ok:true, cancel:true},
5481 * Button config that displays Yes, No and Cancel buttons
5484 YESNOCANCEL : {yes:true, no:true, cancel:true},
5487 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5490 defaultTextHeight : 75,
5492 * The maximum width in pixels of the message box (defaults to 600)
5497 * The minimum width in pixels of the message box (defaults to 100)
5502 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5503 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5506 minProgressWidth : 250,
5508 * An object containing the default button text strings that can be overriden for localized language support.
5509 * Supported properties are: ok, cancel, yes and no.
5510 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5523 * Shorthand for {@link Roo.MessageBox}
5525 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5526 Roo.Msg = Roo.Msg || Roo.MessageBox;
5535 * @class Roo.bootstrap.nav.Bar
5536 * @extends Roo.bootstrap.Component
5538 * Bootstrap Navbar class
5541 * Create a new Navbar
5542 * @param {Object} config The config object
5546 Roo.bootstrap.nav.Bar = function(config){
5547 Roo.bootstrap.nav.Bar.superclass.constructor.call(this, config);
5551 * @event beforetoggle
5552 * Fire before toggle the menu
5553 * @param {Roo.EventObject} e
5555 "beforetoggle" : true
5559 Roo.extend(Roo.bootstrap.nav.Bar, Roo.bootstrap.Component, {
5568 getAutoCreate : function(){
5571 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5575 initEvents :function ()
5577 //Roo.log(this.el.select('.navbar-toggle',true));
5578 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5585 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5587 var size = this.el.getSize();
5588 this.maskEl.setSize(size.width, size.height);
5589 this.maskEl.enableDisplayMode("block");
5598 getChildContainer : function()
5600 if (this.el && this.el.select('.collapse').getCount()) {
5601 return this.el.select('.collapse',true).first();
5616 onToggle : function()
5619 if(this.fireEvent('beforetoggle', this) === false){
5622 var ce = this.el.select('.navbar-collapse',true).first();
5624 if (!ce.hasClass('show')) {
5634 * Expand the navbar pulldown
5636 expand : function ()
5639 var ce = this.el.select('.navbar-collapse',true).first();
5640 if (ce.hasClass('collapsing')) {
5643 ce.dom.style.height = '';
5645 ce.addClass('in'); // old...
5646 ce.removeClass('collapse');
5647 ce.addClass('show');
5648 var h = ce.getHeight();
5650 ce.removeClass('show');
5651 // at this point we should be able to see it..
5652 ce.addClass('collapsing');
5654 ce.setHeight(0); // resize it ...
5655 ce.on('transitionend', function() {
5656 //Roo.log('done transition');
5657 ce.removeClass('collapsing');
5658 ce.addClass('show');
5659 ce.removeClass('collapse');
5661 ce.dom.style.height = '';
5662 }, this, { single: true} );
5664 ce.dom.scrollTop = 0;
5667 * Collapse the navbar pulldown
5669 collapse : function()
5671 var ce = this.el.select('.navbar-collapse',true).first();
5673 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5674 // it's collapsed or collapsing..
5677 ce.removeClass('in'); // old...
5678 ce.setHeight(ce.getHeight());
5679 ce.removeClass('show');
5680 ce.addClass('collapsing');
5682 ce.on('transitionend', function() {
5683 ce.dom.style.height = '';
5684 ce.removeClass('collapsing');
5685 ce.addClass('collapse');
5686 }, this, { single: true} );
5706 * @class Roo.bootstrap.nav.Simplebar
5707 * @extends Roo.bootstrap.nav.Bar
5708 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5709 * Bootstrap Sidebar class
5711 * @cfg {Boolean} inverse is inverted color
5713 * @cfg {String} type (nav | pills | tabs)
5714 * @cfg {Boolean} arrangement stacked | justified
5715 * @cfg {String} align (left | right) alignment
5717 * @cfg {Boolean} main (true|false) main nav bar? default false
5718 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5720 * @cfg {String} tag (header|footer|nav|div) default is nav
5722 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5726 * Create a new Sidebar
5727 * @param {Object} config The config object
5731 Roo.bootstrap.nav.Simplebar = function(config){
5732 Roo.bootstrap.nav.Simplebar.superclass.constructor.call(this, config);
5735 Roo.extend(Roo.bootstrap.nav.Simplebar, Roo.bootstrap.nav.Bar, {
5751 getAutoCreate : function(){
5755 tag : this.tag || 'div',
5756 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5758 if (['light','white'].indexOf(this.weight) > -1) {
5759 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5761 cfg.cls += ' bg-' + this.weight;
5764 cfg.cls += ' navbar-inverse';
5768 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5770 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5779 cls: 'nav nav-' + this.xtype,
5785 this.type = this.type || 'nav';
5786 if (['tabs','pills'].indexOf(this.type) != -1) {
5787 cfg.cn[0].cls += ' nav-' + this.type
5791 if (this.type!=='nav') {
5792 Roo.log('nav type must be nav/tabs/pills')
5794 cfg.cn[0].cls += ' navbar-nav'
5800 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5801 cfg.cn[0].cls += ' nav-' + this.arrangement;
5805 if (this.align === 'right') {
5806 cfg.cn[0].cls += ' navbar-right';
5831 * navbar-expand-md fixed-top
5835 * @class Roo.bootstrap.nav.Headerbar
5836 * @extends Roo.bootstrap.nav.Simplebar
5837 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
5838 * Bootstrap Sidebar class
5840 * @cfg {String} brand what is brand
5841 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5842 * @cfg {String} brand_href href of the brand
5843 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5844 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5845 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5846 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5849 * Create a new Sidebar
5850 * @param {Object} config The config object
5854 Roo.bootstrap.nav.Headerbar = function(config){
5855 Roo.bootstrap.nav.Headerbar.superclass.constructor.call(this, config);
5859 Roo.extend(Roo.bootstrap.nav.Headerbar, Roo.bootstrap.nav.Simplebar, {
5866 desktopCenter : false,
5869 getAutoCreate : function(){
5872 tag: this.nav || 'nav',
5873 cls: 'navbar navbar-expand-md',
5879 if (this.desktopCenter) {
5880 cn.push({cls : 'container', cn : []});
5888 cls: 'navbar-toggle navbar-toggler',
5889 'data-toggle': 'collapse',
5894 html: 'Toggle navigation'
5898 cls: 'icon-bar navbar-toggler-icon'
5911 cn.push( Roo.bootstrap.version == 4 ? btn : {
5913 cls: 'navbar-header',
5922 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5926 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5928 if (['light','white'].indexOf(this.weight) > -1) {
5929 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5931 cfg.cls += ' bg-' + this.weight;
5934 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5935 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5937 // tag can override this..
5939 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5942 if (this.brand !== '') {
5943 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5944 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5946 href: this.brand_href ? this.brand_href : '#',
5947 cls: 'navbar-brand',
5955 cfg.cls += ' main-nav';
5963 getHeaderChildContainer : function()
5965 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5966 return this.el.select('.navbar-header',true).first();
5969 return this.getChildContainer();
5972 getChildContainer : function()
5975 return this.el.select('.roo-navbar-collapse',true).first();
5980 initEvents : function()
5982 Roo.bootstrap.nav.Headerbar.superclass.initEvents.call(this);
5984 if (this.autohide) {
5989 Roo.get(document).on('scroll',function(e) {
5990 var ns = Roo.get(document).getScroll().top;
5991 var os = prevScroll;
5995 ft.removeClass('slideDown');
5996 ft.addClass('slideUp');
5999 ft.removeClass('slideUp');
6000 ft.addClass('slideDown');
6021 * @class Roo.bootstrap.nav.Sidebar
6022 * @extends Roo.bootstrap.nav.Bar
6023 * @children Roo.bootstrap.nav.Group Roo.bootstrap.Container Roo.bootstrap.form.Form Roo.bootstrap.Row Roo.bootstrap.Column Roo.bootstrap.Link
6024 * Bootstrap Sidebar class
6027 * Create a new Sidebar
6028 * @param {Object} config The config object
6032 Roo.bootstrap.nav.Sidebar = function(config){
6033 Roo.bootstrap.nav.Sidebar.superclass.constructor.call(this, config);
6036 Roo.extend(Roo.bootstrap.nav.Sidebar, Roo.bootstrap.nav.Bar, {
6038 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6040 getAutoCreate : function(){
6045 cls: 'sidebar sidebar-nav'
6067 * @class Roo.bootstrap.nav.Group
6068 * @extends Roo.bootstrap.Component
6069 * @children Roo.bootstrap.nav.Item
6070 * Bootstrap NavGroup class
6071 * @cfg {String} align (left|right)
6072 * @cfg {Boolean} inverse
6073 * @cfg {String} type (nav|pills|tab) default nav
6074 * @cfg {String} navId - reference Id for navbar.
6075 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6078 * Create a new nav group
6079 * @param {Object} config The config object
6082 Roo.bootstrap.nav.Group = function(config){
6083 Roo.bootstrap.nav.Group.superclass.constructor.call(this, config);
6086 Roo.bootstrap.nav.Group.register(this);
6090 * Fires when the active item changes
6091 * @param {Roo.bootstrap.nav.Group} this
6092 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6093 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6100 Roo.extend(Roo.bootstrap.nav.Group, Roo.bootstrap.Component, {
6112 getAutoCreate : function()
6114 var cfg = Roo.apply({}, Roo.bootstrap.nav.Group.superclass.getAutoCreate.call(this));
6120 if (Roo.bootstrap.version == 4) {
6121 if (['tabs','pills'].indexOf(this.type) != -1) {
6122 cfg.cls += ' nav-' + this.type;
6124 // trying to remove so header bar can right align top?
6125 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6126 // do not use on header bar...
6127 cfg.cls += ' navbar-nav';
6132 if (['tabs','pills'].indexOf(this.type) != -1) {
6133 cfg.cls += ' nav-' + this.type
6135 if (this.type !== 'nav') {
6136 Roo.log('nav type must be nav/tabs/pills')
6138 cfg.cls += ' navbar-nav'
6142 if (this.parent() && this.parent().sidebar) {
6145 cls: 'dashboard-menu sidebar-menu'
6151 if (this.form === true) {
6154 cls: 'navbar-form form-inline'
6156 //nav navbar-right ml-md-auto
6157 if (this.align === 'right') {
6158 cfg.cls += ' navbar-right ml-md-auto';
6160 cfg.cls += ' navbar-left';
6164 if (this.align === 'right') {
6165 cfg.cls += ' navbar-right ml-md-auto';
6167 cfg.cls += ' mr-auto';
6171 cfg.cls += ' navbar-inverse';
6179 * sets the active Navigation item
6180 * @param {Roo.bootstrap.nav.Item} the new current navitem
6182 setActiveItem : function(item)
6185 Roo.each(this.navItems, function(v){
6190 v.setActive(false, true);
6197 item.setActive(true, true);
6198 this.fireEvent('changed', this, item, prev);
6203 * gets the active Navigation item
6204 * @return {Roo.bootstrap.nav.Item} the current navitem
6206 getActive : function()
6210 Roo.each(this.navItems, function(v){
6221 indexOfNav : function()
6225 Roo.each(this.navItems, function(v,i){
6236 * adds a Navigation item
6237 * @param {Roo.bootstrap.nav.Item} the navitem to add
6239 addItem : function(cfg)
6241 if (this.form && Roo.bootstrap.version == 4) {
6244 var cn = new Roo.bootstrap.nav.Item(cfg);
6246 cn.parentId = this.id;
6247 cn.onRender(this.el, null);
6251 * register a Navigation item
6252 * @param {Roo.bootstrap.nav.Item} the navitem to add
6254 register : function(item)
6256 this.navItems.push( item);
6257 item.navId = this.navId;
6262 * clear all the Navigation item
6265 clearAll : function()
6268 this.el.dom.innerHTML = '';
6271 getNavItem: function(tabId)
6274 Roo.each(this.navItems, function(e) {
6275 if (e.tabId == tabId) {
6285 setActiveNext : function()
6287 var i = this.indexOfNav(this.getActive());
6288 if (i > this.navItems.length) {
6291 this.setActiveItem(this.navItems[i+1]);
6293 setActivePrev : function()
6295 var i = this.indexOfNav(this.getActive());
6299 this.setActiveItem(this.navItems[i-1]);
6301 clearWasActive : function(except) {
6302 Roo.each(this.navItems, function(e) {
6303 if (e.tabId != except.tabId && e.was_active) {
6304 e.was_active = false;
6311 getWasActive : function ()
6314 Roo.each(this.navItems, function(e) {
6329 Roo.apply(Roo.bootstrap.nav.Group, {
6333 * register a Navigation Group
6334 * @param {Roo.bootstrap.nav.Group} the navgroup to add
6336 register : function(navgrp)
6338 this.groups[navgrp.navId] = navgrp;
6342 * fetch a Navigation Group based on the navigation ID
6343 * @param {string} the navgroup to add
6344 * @returns {Roo.bootstrap.nav.Group} the navgroup
6346 get: function(navId) {
6347 if (typeof(this.groups[navId]) == 'undefined') {
6349 //this.register(new Roo.bootstrap.nav.Group({ navId : navId }));
6351 return this.groups[navId] ;
6359 * @class Roo.bootstrap.nav.Item
6360 * @extends Roo.bootstrap.Component
6361 * @children Roo.bootstrap.Container Roo.bootstrap.Button
6362 * @parent Roo.bootstrap.nav.Group
6364 * Bootstrap Navbar.NavItem class
6366 * @cfg {String} href link to
6367 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6368 * @cfg {Boolean} button_outline show and outlined button
6369 * @cfg {String} html content of button
6370 * @cfg {String} badge text inside badge
6371 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6372 * @cfg {String} glyphicon DEPRICATED - use fa
6373 * @cfg {String} icon DEPRICATED - use fa
6374 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6375 * @cfg {Boolean} active Is item active
6376 * @cfg {Boolean} disabled Is item disabled
6377 * @cfg {String} linkcls Link Class
6378 * @cfg {Boolean} preventDefault (true | false) default false
6379 * @cfg {String} tabId the tab that this item activates.
6380 * @cfg {String} tagtype (a|span) render as a href or span?
6381 * @cfg {Boolean} animateRef (true|false) link to element default false
6382 * @cfg {Roo.bootstrap.menu.Menu} menu a Menu
6385 * Create a new Navbar Item
6386 * @param {Object} config The config object
6388 Roo.bootstrap.nav.Item = function(config){
6389 Roo.bootstrap.nav.Item.superclass.constructor.call(this, config);
6394 * The raw click event for the entire grid.
6395 * @param {Roo.EventObject} e
6400 * Fires when the active item active state changes
6401 * @param {Roo.bootstrap.nav.Item} this
6402 * @param {boolean} state the new state
6408 * Fires when scroll to element
6409 * @param {Roo.bootstrap.nav.Item} this
6410 * @param {Object} options
6411 * @param {Roo.EventObject} e
6419 Roo.extend(Roo.bootstrap.nav.Item, Roo.bootstrap.Component, {
6428 preventDefault : false,
6436 button_outline : false,
6440 getAutoCreate : function(){
6447 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6450 cfg.cls += ' active' ;
6452 if (this.disabled) {
6453 cfg.cls += ' disabled';
6457 if (this.button_weight.length) {
6458 cfg.tag = this.href ? 'a' : 'button';
6459 cfg.html = this.html || '';
6460 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6462 cfg.href = this.href;
6465 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6467 cfg.cls += " nav-html";
6470 // menu .. should add dropdown-menu class - so no need for carat..
6472 if (this.badge !== '') {
6474 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6479 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6483 href : this.href || "#",
6484 html: this.html || '',
6488 if (this.tagtype == 'a') {
6489 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6493 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6494 } else if (this.fa) {
6495 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6496 } else if(this.glyphicon) {
6497 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6499 cfg.cn[0].cls += " nav-html";
6503 cfg.cn[0].html += " <span class='caret'></span>";
6507 if (this.badge !== '') {
6508 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6516 onRender : function(ct, position)
6518 // Roo.log("Call onRender: " + this.xtype);
6519 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6523 var ret = Roo.bootstrap.nav.Item.superclass.onRender.call(this, ct, position);
6524 this.navLink = this.el.select('.nav-link',true).first();
6525 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6530 initEvents: function()
6532 if (typeof (this.menu) != 'undefined') {
6533 this.menu.parentType = this.xtype;
6534 this.menu.triggerEl = this.el;
6535 this.menu = this.addxtype(Roo.apply({}, this.menu));
6538 this.el.on('click', this.onClick, this);
6540 //if(this.tagtype == 'span'){
6541 // this.el.select('span',true).on('click', this.onClick, this);
6544 // at this point parent should be available..
6545 this.parent().register(this);
6548 onClick : function(e)
6550 if (e.getTarget('.dropdown-menu-item')) {
6551 // did you click on a menu itemm.... - then don't trigger onclick..
6556 this.preventDefault ||
6557 this.href === false ||
6560 //Roo.log("NavItem - prevent Default?");
6564 if (this.disabled) {
6568 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6569 if (tg && tg.transition) {
6570 Roo.log("waiting for the transitionend");
6576 //Roo.log("fire event clicked");
6577 if(this.fireEvent('click', this, e) === false){
6581 if(this.tagtype == 'span'){
6585 //Roo.log(this.href);
6586 var ael = this.el.select('a',true).first();
6589 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6590 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6591 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6592 return; // ignore... - it's a 'hash' to another page.
6594 Roo.log("NavItem - prevent Default?");
6596 this.scrollToElement(e);
6600 var p = this.parent();
6602 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6603 if (typeof(p.setActiveItem) !== 'undefined') {
6604 p.setActiveItem(this);
6608 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6609 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6610 // remove the collapsed menu expand...
6611 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6615 isActive: function () {
6618 setActive : function(state, fire, is_was_active)
6620 if (this.active && !state && this.navId) {
6621 this.was_active = true;
6622 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6624 nv.clearWasActive(this);
6628 this.active = state;
6631 this.el.removeClass('active');
6632 this.navLink ? this.navLink.removeClass('active') : false;
6633 } else if (!this.el.hasClass('active')) {
6635 this.el.addClass('active');
6636 if (Roo.bootstrap.version == 4 && this.navLink ) {
6637 this.navLink.addClass('active');
6642 this.fireEvent('changed', this, state);
6645 // show a panel if it's registered and related..
6647 if (!this.navId || !this.tabId || !state || is_was_active) {
6651 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6655 var pan = tg.getPanelByName(this.tabId);
6659 // if we can not flip to new panel - go back to old nav highlight..
6660 if (false == tg.showPanel(pan)) {
6661 var nv = Roo.bootstrap.nav.Group.get(this.navId);
6663 var onav = nv.getWasActive();
6665 onav.setActive(true, false, true);
6674 // this should not be here...
6675 setDisabled : function(state)
6677 this.disabled = state;
6679 this.el.removeClass('disabled');
6680 } else if (!this.el.hasClass('disabled')) {
6681 this.el.addClass('disabled');
6687 * Fetch the element to display the tooltip on.
6688 * @return {Roo.Element} defaults to this.el
6690 tooltipEl : function()
6692 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6695 scrollToElement : function(e)
6697 var c = document.body;
6700 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6702 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6703 c = document.documentElement;
6706 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6712 var o = target.calcOffsetsTo(c);
6719 this.fireEvent('scrollto', this, options, e);
6721 Roo.get(c).scrollTo('top', options.value, true);
6726 * Set the HTML (text content) of the item
6727 * @param {string} html content for the nav item
6729 setHtml : function(html)
6732 this.htmlEl.dom.innerHTML = html;
6744 * <span> icon </span>
6745 * <span> text </span>
6746 * <span>badge </span>
6750 * @class Roo.bootstrap.nav.SidebarItem
6751 * @extends Roo.bootstrap.nav.Item
6752 * Bootstrap Navbar.NavSidebarItem class
6754 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6755 * {Boolean} open is the menu open
6756 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6757 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6758 * {String} buttonSize (sm|md|lg)the extra classes for the button
6759 * {Boolean} showArrow show arrow next to the text (default true)
6761 * Create a new Navbar Button
6762 * @param {Object} config The config object
6764 Roo.bootstrap.nav.SidebarItem = function(config){
6765 Roo.bootstrap.nav.SidebarItem.superclass.constructor.call(this, config);
6770 * The raw click event for the entire grid.
6771 * @param {Roo.EventObject} e
6776 * Fires when the active item active state changes
6777 * @param {Roo.bootstrap.nav.SidebarItem} this
6778 * @param {boolean} state the new state
6786 Roo.extend(Roo.bootstrap.nav.SidebarItem, Roo.bootstrap.nav.Item, {
6788 badgeWeight : 'default',
6794 buttonWeight : 'default',
6800 getAutoCreate : function(){
6805 href : this.href || '#',
6811 if(this.buttonView){
6814 href : this.href || '#',
6815 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6828 cfg.cls += ' active';
6831 if (this.disabled) {
6832 cfg.cls += ' disabled';
6835 cfg.cls += ' open x-open';
6838 if (this.glyphicon || this.icon) {
6839 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6840 a.cn.push({ tag : 'i', cls : c }) ;
6843 if(!this.buttonView){
6846 html : this.html || ''
6853 if (this.badge !== '') {
6854 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6860 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6863 a.cls += ' dropdown-toggle treeview' ;
6869 initEvents : function()
6871 if (typeof (this.menu) != 'undefined') {
6872 this.menu.parentType = this.xtype;
6873 this.menu.triggerEl = this.el;
6874 this.menu = this.addxtype(Roo.apply({}, this.menu));
6877 this.el.on('click', this.onClick, this);
6879 if(this.badge !== ''){
6880 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6885 onClick : function(e)
6892 if(this.preventDefault){
6896 this.fireEvent('click', this, e);
6899 disable : function()
6901 this.setDisabled(true);
6906 this.setDisabled(false);
6909 setDisabled : function(state)
6911 if(this.disabled == state){
6915 this.disabled = state;
6918 this.el.addClass('disabled');
6922 this.el.removeClass('disabled');
6927 setActive : function(state)
6929 if(this.active == state){
6933 this.active = state;
6936 this.el.addClass('active');
6940 this.el.removeClass('active');
6945 isActive: function ()
6950 setBadge : function(str)
6956 this.badgeEl.dom.innerHTML = str;
6973 * @class Roo.bootstrap.nav.ProgressBar
6974 * @extends Roo.bootstrap.Component
6975 * @children Roo.bootstrap.nav.ProgressBarItem
6976 * Bootstrap NavProgressBar class
6979 * Create a new nav progress bar - a bar indicating step along a process
6980 * @param {Object} config The config object
6983 Roo.bootstrap.nav.ProgressBar = function(config){
6984 Roo.bootstrap.nav.ProgressBar.superclass.constructor.call(this, config);
6986 this.bullets = this.bullets || [];
6988 // Roo.bootstrap.nav.ProgressBar.register(this);
6992 * Fires when the active item changes
6993 * @param {Roo.bootstrap.nav.ProgressBar} this
6994 * @param {Roo.bootstrap.nav.ProgressItem} selected The item selected
6995 * @param {Roo.bootstrap.nav.ProgressItem} prev The previously selected item
7002 Roo.extend(Roo.bootstrap.nav.ProgressBar, Roo.bootstrap.Component, {
7004 * @cfg {Roo.bootstrap.nav.ProgressItem} NavProgressBar:bullets[]
7005 * Bullets for the Nav Progress bar for the toolbar
7010 getAutoCreate : function()
7012 var cfg = Roo.apply({}, Roo.bootstrap.nav.ProgressBar.superclass.getAutoCreate.call(this));
7016 cls : 'roo-navigation-bar-group',
7020 cls : 'roo-navigation-top-bar'
7024 cls : 'roo-navigation-bullets-bar',
7028 cls : 'roo-navigation-bar'
7035 cls : 'roo-navigation-bottom-bar'
7045 initEvents: function()
7050 onRender : function(ct, position)
7052 Roo.bootstrap.nav.ProgressBar.superclass.onRender.call(this, ct, position);
7054 if(this.bullets.length){
7055 Roo.each(this.bullets, function(b){
7064 addItem : function(cfg)
7066 var item = new Roo.bootstrap.nav.ProgressItem(cfg);
7068 item.parentId = this.id;
7069 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
7072 var top = new Roo.bootstrap.Element({
7074 cls : 'roo-navigation-bar-text'
7077 var bottom = new Roo.bootstrap.Element({
7079 cls : 'roo-navigation-bar-text'
7082 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
7083 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
7085 var topText = new Roo.bootstrap.Element({
7087 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
7090 var bottomText = new Roo.bootstrap.Element({
7092 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
7095 topText.onRender(top.el, null);
7096 bottomText.onRender(bottom.el, null);
7099 item.bottomEl = bottom;
7102 this.barItems.push(item);
7107 getActive : function()
7111 Roo.each(this.barItems, function(v){
7113 if (!v.isActive()) {
7125 setActiveItem : function(item)
7129 Roo.each(this.barItems, function(v){
7130 if (v.rid == item.rid) {
7140 item.setActive(true);
7142 this.fireEvent('changed', this, item, prev);
7145 getBarItem: function(rid)
7149 Roo.each(this.barItems, function(e) {
7161 indexOfItem : function(item)
7165 Roo.each(this.barItems, function(v, i){
7167 if (v.rid != item.rid) {
7178 setActiveNext : function()
7180 var i = this.indexOfItem(this.getActive());
7182 if (i > this.barItems.length) {
7186 this.setActiveItem(this.barItems[i+1]);
7189 setActivePrev : function()
7191 var i = this.indexOfItem(this.getActive());
7197 this.setActiveItem(this.barItems[i-1]);
7202 if(!this.barItems.length){
7206 var width = 100 / this.barItems.length;
7208 Roo.each(this.barItems, function(i){
7209 i.el.setStyle('width', width + '%');
7210 i.topEl.el.setStyle('width', width + '%');
7211 i.bottomEl.el.setStyle('width', width + '%');
7225 * @class Roo.bootstrap.nav.ProgressBarItem
7226 * @extends Roo.bootstrap.Component
7227 * Bootstrap NavProgressBarItem class
7228 * @cfg {String} rid the reference id
7229 * @cfg {Boolean} active (true|false) Is item active default false
7230 * @cfg {Boolean} disabled (true|false) Is item active default false
7231 * @cfg {String} html
7232 * @cfg {String} position (top|bottom) text position default bottom
7233 * @cfg {String} icon show icon instead of number
7236 * Create a new NavProgressBarItem
7237 * @param {Object} config The config object
7239 Roo.bootstrap.nav.ProgressBarItem = function(config){
7240 Roo.bootstrap.nav.ProgressBarItem.superclass.constructor.call(this, config);
7245 * The raw click event for the entire grid.
7246 * @param {Roo.bootstrap.nav.ProgressBarItem} this
7247 * @param {Roo.EventObject} e
7254 Roo.extend(Roo.bootstrap.nav.ProgressBarItem, Roo.bootstrap.Component, {
7260 position : 'bottom',
7263 getAutoCreate : function()
7265 var iconCls = 'roo-navigation-bar-item-icon';
7267 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
7271 cls: 'roo-navigation-bar-item',
7281 cfg.cls += ' active';
7284 cfg.cls += ' disabled';
7290 disable : function()
7292 this.setDisabled(true);
7297 this.setDisabled(false);
7300 initEvents: function()
7302 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
7304 this.iconEl.on('click', this.onClick, this);
7307 onClick : function(e)
7315 if(this.fireEvent('click', this, e) === false){
7319 this.parent().setActiveItem(this);
7322 isActive: function ()
7327 setActive : function(state)
7329 if(this.active == state){
7333 this.active = state;
7336 this.el.addClass('active');
7340 this.el.removeClass('active');
7345 setDisabled : function(state)
7347 if(this.disabled == state){
7351 this.disabled = state;
7354 this.el.addClass('disabled');
7358 this.el.removeClass('disabled');
7361 tooltipEl : function()
7363 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
7374 Roo.namespace('Roo.bootstrap.breadcrumb');
7378 * @class Roo.bootstrap.breadcrumb.Nav
7379 * @extends Roo.bootstrap.Component
7380 * Bootstrap Breadcrumb Nav Class
7382 * @children Roo.bootstrap.breadcrumb.Item
7385 * Create a new breadcrumb.Nav
7386 * @param {Object} config The config object
7390 Roo.bootstrap.breadcrumb.Nav = function(config){
7391 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
7396 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
7398 getAutoCreate : function()
7415 initEvents: function()
7417 this.olEl = this.el.select('ol',true).first();
7419 getChildContainer : function()
7435 * @class Roo.bootstrap.breadcrumb.Nav
7436 * @extends Roo.bootstrap.Component
7437 * @children Roo.bootstrap.Component
7438 * @parent Roo.bootstrap.breadcrumb.Nav
7439 * Bootstrap Breadcrumb Nav Class
7442 * @cfg {String} html the content of the link.
7443 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7444 * @cfg {Boolean} active is it active
7448 * Create a new breadcrumb.Nav
7449 * @param {Object} config The config object
7452 Roo.bootstrap.breadcrumb.Item = function(config){
7453 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7458 * The img click event for the img.
7459 * @param {Roo.EventObject} e
7466 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7471 getAutoCreate : function()
7476 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7478 if (this.href !== false) {
7485 cfg.html = this.html;
7491 initEvents: function()
7494 this.el.select('a', true).first().on('click',this.onClick, this)
7498 onClick : function(e)
7501 this.fireEvent('click',this, e);
7514 * @class Roo.bootstrap.Row
7515 * @extends Roo.bootstrap.Component
7516 * @children Roo.bootstrap.Component
7517 * Bootstrap Row class (contains columns...)
7521 * @param {Object} config The config object
7524 Roo.bootstrap.Row = function(config){
7525 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7528 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7530 getAutoCreate : function(){
7549 * @class Roo.bootstrap.Pagination
7550 * @extends Roo.bootstrap.Component
7551 * @children Roo.bootstrap.Pagination
7552 * Bootstrap Pagination class
7554 * @cfg {String} size (xs|sm|md|lg|xl)
7555 * @cfg {Boolean} inverse
7558 * Create a new Pagination
7559 * @param {Object} config The config object
7562 Roo.bootstrap.Pagination = function(config){
7563 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7566 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7572 getAutoCreate : function(){
7578 cfg.cls += ' inverse';
7584 cfg.cls += " " + this.cls;
7602 * @class Roo.bootstrap.PaginationItem
7603 * @extends Roo.bootstrap.Component
7604 * Bootstrap PaginationItem class
7605 * @cfg {String} html text
7606 * @cfg {String} href the link
7607 * @cfg {Boolean} preventDefault (true | false) default true
7608 * @cfg {Boolean} active (true | false) default false
7609 * @cfg {Boolean} disabled default false
7613 * Create a new PaginationItem
7614 * @param {Object} config The config object
7618 Roo.bootstrap.PaginationItem = function(config){
7619 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7624 * The raw click event for the entire grid.
7625 * @param {Roo.EventObject} e
7631 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7635 preventDefault: true,
7640 getAutoCreate : function(){
7646 href : this.href ? this.href : '#',
7647 html : this.html ? this.html : ''
7657 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7661 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7667 initEvents: function() {
7669 this.el.on('click', this.onClick, this);
7672 onClick : function(e)
7674 Roo.log('PaginationItem on click ');
7675 if(this.preventDefault){
7683 this.fireEvent('click', this, e);
7699 * @class Roo.bootstrap.Slider
7700 * @extends Roo.bootstrap.Component
7701 * Bootstrap Slider class
7704 * Create a new Slider
7705 * @param {Object} config The config object
7708 Roo.bootstrap.Slider = function(config){
7709 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7712 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7714 getAutoCreate : function(){
7718 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7722 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7734 * Ext JS Library 1.1.1
7735 * Copyright(c) 2006-2007, Ext JS, LLC.
7737 * Originally Released Under LGPL - original licence link has changed is not relivant.
7740 * <script type="text/javascript">
7743 * @extends Roo.dd.DDProxy
7744 * @class Roo.grid.SplitDragZone
7745 * Support for Column Header resizing
7747 * @param {Object} config
7750 // This is a support class used internally by the Grid components
7751 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7753 this.view = grid.getView();
7754 this.proxy = this.view.resizeProxy;
7755 Roo.grid.SplitDragZone.superclass.constructor.call(
7758 "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7760 dragElId : Roo.id(this.proxy.dom),
7765 this.setHandleElId(Roo.id(hd));
7766 if (hd2 !== false) {
7767 this.setOuterHandleElId(Roo.id(hd2));
7770 this.scroll = false;
7772 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7773 fly: Roo.Element.fly,
7775 b4StartDrag : function(x, y){
7776 this.view.headersDisabled = true;
7777 var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7778 this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7780 this.proxy.setHeight(h);
7782 // for old system colWidth really stored the actual width?
7783 // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7784 // which in reality did not work.. - it worked only for fixed sizes
7785 // for resizable we need to use actual sizes.
7786 var w = this.cm.getColumnWidth(this.cellIndex);
7787 if (!this.view.mainWrap) {
7789 w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7794 // this was w-this.grid.minColumnWidth;
7795 // doesnt really make sense? - w = thie curren width or the rendered one?
7796 var minw = Math.max(w-this.grid.minColumnWidth, 0);
7797 this.resetConstraints();
7798 this.setXConstraint(minw, 1000);
7799 this.setYConstraint(0, 0);
7800 this.minX = x - minw;
7801 this.maxX = x + 1000;
7803 if (!this.view.mainWrap) { // this is Bootstrap code..
7804 this.getDragEl().style.display='block';
7807 Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7811 handleMouseDown : function(e){
7812 ev = Roo.EventObject.setEvent(e);
7813 var t = this.fly(ev.getTarget());
7814 if(t.hasClass("x-grid-split")){
7815 this.cellIndex = this.view.getCellIndex(t.dom);
7817 this.cm = this.grid.colModel;
7818 if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7819 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7824 endDrag : function(e){
7825 this.view.headersDisabled = false;
7826 var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7827 var diff = endX - this.startPos;
7829 var w = this.cm.getColumnWidth(this.cellIndex);
7830 if (!this.view.mainWrap) {
7833 this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7836 autoOffset : function(){
7841 * Ext JS Library 1.1.1
7842 * Copyright(c) 2006-2007, Ext JS, LLC.
7844 * Originally Released Under LGPL - original licence link has changed is not relivant.
7847 * <script type="text/javascript">
7851 * @class Roo.grid.AbstractSelectionModel
7852 * @extends Roo.util.Observable
7854 * Abstract base class for grid SelectionModels. It provides the interface that should be
7855 * implemented by descendant classes. This class should not be directly instantiated.
7858 Roo.grid.AbstractSelectionModel = function(){
7859 this.locked = false;
7860 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7863 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
7864 /** @ignore Called by the grid automatically. Do not call directly. */
7865 init : function(grid){
7871 * Locks the selections.
7878 * Unlocks the selections.
7880 unlock : function(){
7881 this.locked = false;
7885 * Returns true if the selections are locked.
7888 isLocked : function(){
7893 * Ext JS Library 1.1.1
7894 * Copyright(c) 2006-2007, Ext JS, LLC.
7896 * Originally Released Under LGPL - original licence link has changed is not relivant.
7899 * <script type="text/javascript">
7902 * @extends Roo.grid.AbstractSelectionModel
7903 * @class Roo.grid.RowSelectionModel
7904 * The default SelectionModel used by {@link Roo.grid.Grid}.
7905 * It supports multiple selections and keyboard selection/navigation.
7907 * @param {Object} config
7909 Roo.grid.RowSelectionModel = function(config){
7910 Roo.apply(this, config);
7911 this.selections = new Roo.util.MixedCollection(false, function(o){
7916 this.lastActive = false;
7920 * @event selectionchange
7921 * Fires when the selection changes
7922 * @param {SelectionModel} this
7924 "selectionchange" : true,
7926 * @event afterselectionchange
7927 * Fires after the selection changes (eg. by key press or clicking)
7928 * @param {SelectionModel} this
7930 "afterselectionchange" : true,
7932 * @event beforerowselect
7933 * Fires when a row is selected being selected, return false to cancel.
7934 * @param {SelectionModel} this
7935 * @param {Number} rowIndex The selected index
7936 * @param {Boolean} keepExisting False if other selections will be cleared
7938 "beforerowselect" : true,
7941 * Fires when a row is selected.
7942 * @param {SelectionModel} this
7943 * @param {Number} rowIndex The selected index
7944 * @param {Roo.data.Record} r The record
7948 * @event rowdeselect
7949 * Fires when a row is deselected.
7950 * @param {SelectionModel} this
7951 * @param {Number} rowIndex The selected index
7953 "rowdeselect" : true
7955 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7956 this.locked = false;
7959 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
7961 * @cfg {Boolean} singleSelect
7962 * True to allow selection of only one row at a time (defaults to false)
7964 singleSelect : false,
7967 initEvents : function(){
7969 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7970 this.grid.on("mousedown", this.handleMouseDown, this);
7971 }else{ // allow click to work like normal
7972 this.grid.on("rowclick", this.handleDragableRowClick, this);
7974 // bootstrap does not have a view..
7975 var view = this.grid.view ? this.grid.view : this.grid;
7976 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7979 this.selectPrevious(e.shiftKey);
7980 }else if(this.last !== false && this.lastActive !== false){
7981 var last = this.last;
7982 this.selectRange(this.last, this.lastActive-1);
7983 view.focusRow(this.lastActive);
7988 this.selectFirstRow();
7990 this.fireEvent("afterselectionchange", this);
7992 "down" : function(e){
7994 this.selectNext(e.shiftKey);
7995 }else if(this.last !== false && this.lastActive !== false){
7996 var last = this.last;
7997 this.selectRange(this.last, this.lastActive+1);
7998 view.focusRow(this.lastActive);
8003 this.selectFirstRow();
8005 this.fireEvent("afterselectionchange", this);
8011 view.on("refresh", this.onRefresh, this);
8012 view.on("rowupdated", this.onRowUpdated, this);
8013 view.on("rowremoved", this.onRemove, this);
8017 onRefresh : function(){
8018 var ds = this.grid.ds, i, v = this.grid.view;
8019 var s = this.selections;
8021 if((i = ds.indexOfId(r.id)) != -1){
8023 s.add(ds.getAt(i)); // updating the selection relate data
8031 onRemove : function(v, index, r){
8032 this.selections.remove(r);
8036 onRowUpdated : function(v, index, r){
8037 if(this.isSelected(r)){
8038 v.onRowSelect(index);
8044 * @param {Array} records The records to select
8045 * @param {Boolean} keepExisting (optional) True to keep existing selections
8047 selectRecords : function(records, keepExisting){
8049 this.clearSelections();
8051 var ds = this.grid.ds;
8052 for(var i = 0, len = records.length; i < len; i++){
8053 this.selectRow(ds.indexOf(records[i]), true);
8058 * Gets the number of selected rows.
8061 getCount : function(){
8062 return this.selections.length;
8066 * Selects the first row in the grid.
8068 selectFirstRow : function(){
8073 * Select the last row.
8074 * @param {Boolean} keepExisting (optional) True to keep existing selections
8076 selectLastRow : function(keepExisting){
8077 this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
8081 * Selects the row immediately following the last selected row.
8082 * @param {Boolean} keepExisting (optional) True to keep existing selections
8084 selectNext : function(keepExisting){
8085 if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
8086 this.selectRow(this.last+1, keepExisting);
8087 var view = this.grid.view ? this.grid.view : this.grid;
8088 view.focusRow(this.last);
8093 * Selects the row that precedes the last selected row.
8094 * @param {Boolean} keepExisting (optional) True to keep existing selections
8096 selectPrevious : function(keepExisting){
8098 this.selectRow(this.last-1, keepExisting);
8099 var view = this.grid.view ? this.grid.view : this.grid;
8100 view.focusRow(this.last);
8105 * Returns the selected records
8106 * @return {Array} Array of selected records
8108 getSelections : function(){
8109 return [].concat(this.selections.items);
8113 * Returns the first selected record.
8116 getSelected : function(){
8117 return this.selections.itemAt(0);
8122 * Clears all selections.
8124 clearSelections : function(fast){
8129 var ds = this.grid.ds;
8130 var s = this.selections;
8132 this.deselectRow(ds.indexOfId(r.id));
8136 this.selections.clear();
8145 selectAll : function(){
8149 this.selections.clear();
8150 for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
8151 this.selectRow(i, true);
8156 * Returns True if there is a selection.
8159 hasSelection : function(){
8160 return this.selections.length > 0;
8164 * Returns True if the specified row is selected.
8165 * @param {Number/Record} record The record or index of the record to check
8168 isSelected : function(index){
8169 var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
8170 return (r && this.selections.key(r.id) ? true : false);
8174 * Returns True if the specified record id is selected.
8175 * @param {String} id The id of record to check
8178 isIdSelected : function(id){
8179 return (this.selections.key(id) ? true : false);
8183 handleMouseDown : function(e, t)
8185 var view = this.grid.view ? this.grid.view : this.grid;
8187 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
8190 if(e.shiftKey && this.last !== false){
8191 var last = this.last;
8192 this.selectRange(last, rowIndex, e.ctrlKey);
8193 this.last = last; // reset the last
8194 view.focusRow(rowIndex);
8196 var isSelected = this.isSelected(rowIndex);
8197 if(e.button !== 0 && isSelected){
8198 view.focusRow(rowIndex);
8199 }else if(e.ctrlKey && isSelected){
8200 this.deselectRow(rowIndex);
8201 }else if(!isSelected){
8202 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
8203 view.focusRow(rowIndex);
8206 this.fireEvent("afterselectionchange", this);
8209 handleDragableRowClick : function(grid, rowIndex, e)
8211 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
8212 this.selectRow(rowIndex, false);
8213 var view = this.grid.view ? this.grid.view : this.grid;
8214 view.focusRow(rowIndex);
8215 this.fireEvent("afterselectionchange", this);
8220 * Selects multiple rows.
8221 * @param {Array} rows Array of the indexes of the row to select
8222 * @param {Boolean} keepExisting (optional) True to keep existing selections
8224 selectRows : function(rows, keepExisting){
8226 this.clearSelections();
8228 for(var i = 0, len = rows.length; i < len; i++){
8229 this.selectRow(rows[i], true);
8234 * Selects a range of rows. All rows in between startRow and endRow are also selected.
8235 * @param {Number} startRow The index of the first row in the range
8236 * @param {Number} endRow The index of the last row in the range
8237 * @param {Boolean} keepExisting (optional) True to retain existing selections
8239 selectRange : function(startRow, endRow, keepExisting){
8244 this.clearSelections();
8246 if(startRow <= endRow){
8247 for(var i = startRow; i <= endRow; i++){
8248 this.selectRow(i, true);
8251 for(var i = startRow; i >= endRow; i--){
8252 this.selectRow(i, true);
8258 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
8259 * @param {Number} startRow The index of the first row in the range
8260 * @param {Number} endRow The index of the last row in the range
8262 deselectRange : function(startRow, endRow, preventViewNotify){
8266 for(var i = startRow; i <= endRow; i++){
8267 this.deselectRow(i, preventViewNotify);
8273 * @param {Number} row The index of the row to select
8274 * @param {Boolean} keepExisting (optional) True to keep existing selections
8276 selectRow : function(index, keepExisting, preventViewNotify){
8277 if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
8280 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
8281 if(!keepExisting || this.singleSelect){
8282 this.clearSelections();
8284 var r = this.grid.ds.getAt(index);
8285 this.selections.add(r);
8286 this.last = this.lastActive = index;
8287 if(!preventViewNotify){
8288 var view = this.grid.view ? this.grid.view : this.grid;
8289 view.onRowSelect(index);
8291 this.fireEvent("rowselect", this, index, r);
8292 this.fireEvent("selectionchange", this);
8298 * @param {Number} row The index of the row to deselect
8300 deselectRow : function(index, preventViewNotify){
8304 if(this.last == index){
8307 if(this.lastActive == index){
8308 this.lastActive = false;
8310 var r = this.grid.ds.getAt(index);
8311 this.selections.remove(r);
8312 if(!preventViewNotify){
8313 var view = this.grid.view ? this.grid.view : this.grid;
8314 view.onRowDeselect(index);
8316 this.fireEvent("rowdeselect", this, index);
8317 this.fireEvent("selectionchange", this);
8321 restoreLast : function(){
8323 this.last = this._last;
8328 acceptsNav : function(row, col, cm){
8329 return !cm.isHidden(col) && cm.isCellEditable(col, row);
8333 onEditorKey : function(field, e){
8334 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
8339 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
8341 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
8343 }else if(k == e.ENTER && !e.ctrlKey){
8347 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
8349 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
8351 }else if(k == e.ESC){
8355 g.startEditing(newCell[0], newCell[1]);
8360 * Ext JS Library 1.1.1
8361 * Copyright(c) 2006-2007, Ext JS, LLC.
8363 * Originally Released Under LGPL - original licence link has changed is not relivant.
8366 * <script type="text/javascript">
8371 * @class Roo.grid.ColumnModel
8372 * @extends Roo.util.Observable
8373 * This is the default implementation of a ColumnModel used by the Grid. It defines
8374 * the columns in the grid.
8377 var colModel = new Roo.grid.ColumnModel([
8378 {header: "Ticker", width: 60, sortable: true, locked: true},
8379 {header: "Company Name", width: 150, sortable: true},
8380 {header: "Market Cap.", width: 100, sortable: true},
8381 {header: "$ Sales", width: 100, sortable: true, renderer: money},
8382 {header: "Employees", width: 100, sortable: true, resizable: false}
8387 * The config options listed for this class are options which may appear in each
8388 * individual column definition.
8389 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
8391 * @param {Object} config An Array of column config objects. See this class's
8392 * config objects for details.
8394 Roo.grid.ColumnModel = function(config){
8396 * The config passed into the constructor
8398 this.config = []; //config;
8401 // if no id, create one
8402 // if the column does not have a dataIndex mapping,
8403 // map it to the order it is in the config
8404 for(var i = 0, len = config.length; i < len; i++){
8405 this.addColumn(config[i]);
8410 * The width of columns which have no width specified (defaults to 100)
8413 this.defaultWidth = 100;
8416 * Default sortable of columns which have no sortable specified (defaults to false)
8419 this.defaultSortable = false;
8423 * @event widthchange
8424 * Fires when the width of a column changes.
8425 * @param {ColumnModel} this
8426 * @param {Number} columnIndex The column index
8427 * @param {Number} newWidth The new width
8429 "widthchange": true,
8431 * @event headerchange
8432 * Fires when the text of a header changes.
8433 * @param {ColumnModel} this
8434 * @param {Number} columnIndex The column index
8435 * @param {Number} newText The new header text
8437 "headerchange": true,
8439 * @event hiddenchange
8440 * Fires when a column is hidden or "unhidden".
8441 * @param {ColumnModel} this
8442 * @param {Number} columnIndex The column index
8443 * @param {Boolean} hidden true if hidden, false otherwise
8445 "hiddenchange": true,
8447 * @event columnmoved
8448 * Fires when a column is moved.
8449 * @param {ColumnModel} this
8450 * @param {Number} oldIndex
8451 * @param {Number} newIndex
8453 "columnmoved" : true,
8455 * @event columlockchange
8456 * Fires when a column's locked state is changed
8457 * @param {ColumnModel} this
8458 * @param {Number} colIndex
8459 * @param {Boolean} locked true if locked
8461 "columnlockchange" : true
8463 Roo.grid.ColumnModel.superclass.constructor.call(this);
8465 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8467 * @cfg {String} header [required] The header text to display in the Grid view.
8470 * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8473 * @cfg {String} smHeader Header at Bootsrap Small width
8476 * @cfg {String} mdHeader Header at Bootsrap Medium width
8479 * @cfg {String} lgHeader Header at Bootsrap Large width
8482 * @cfg {String} xlHeader Header at Bootsrap extra Large width
8485 * @cfg {String} dataIndex The name of the field in the grid's {@link Roo.data.Store}'s
8486 * {@link Roo.data.Record} definition from which to draw the column's value. If not
8487 * specified, the column's index is used as an index into the Record's data Array.
8490 * @cfg {Number} width The initial width in pixels of the column. Using this
8491 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8494 * @cfg {Boolean} sortable True if sorting is to be allowed on this column.
8495 * Defaults to the value of the {@link #defaultSortable} property.
8496 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8499 * @cfg {Boolean} locked True to lock the column in place while scrolling the Grid. Defaults to false.
8502 * @cfg {Boolean} fixed True if the column width cannot be changed. Defaults to false.
8505 * @cfg {Boolean} resizable False to disable column resizing. Defaults to true.
8508 * @cfg {Boolean} hidden True to hide the column. Defaults to false.
8511 * @cfg {Function} renderer A function used to generate HTML markup for a cell
8512 * given the cell's data value. See {@link #setRenderer}. If not specified, the
8513 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8514 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8517 * @cfg {Roo.grid.GridEditor} editor For grid editors - returns the grid editor
8520 * @cfg {String} align (left|right) Set the CSS text-align property of the column. Defaults to undefined (left).
8523 * @cfg {String} valign (top|bottom|middle) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined (middle)
8526 * @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)
8529 * @cfg {String} tooltip mouse over tooltip text
8532 * @cfg {Number} xs can be '0' for hidden at this size (number less than 12)
8535 * @cfg {Number} sm can be '0' for hidden at this size (number less than 12)
8538 * @cfg {Number} md can be '0' for hidden at this size (number less than 12)
8541 * @cfg {Number} lg can be '0' for hidden at this size (number less than 12)
8544 * @cfg {Number} xl can be '0' for hidden at this size (number less than 12)
8547 * Returns the id of the column at the specified index.
8548 * @param {Number} index The column index
8549 * @return {String} the id
8551 getColumnId : function(index){
8552 return this.config[index].id;
8556 * Returns the column for a specified id.
8557 * @param {String} id The column id
8558 * @return {Object} the column
8560 getColumnById : function(id){
8561 return this.lookup[id];
8566 * Returns the column Object for a specified dataIndex.
8567 * @param {String} dataIndex The column dataIndex
8568 * @return {Object|Boolean} the column or false if not found
8570 getColumnByDataIndex: function(dataIndex){
8571 var index = this.findColumnIndex(dataIndex);
8572 return index > -1 ? this.config[index] : false;
8576 * Returns the index for a specified column id.
8577 * @param {String} id The column id
8578 * @return {Number} the index, or -1 if not found
8580 getIndexById : function(id){
8581 for(var i = 0, len = this.config.length; i < len; i++){
8582 if(this.config[i].id == id){
8590 * Returns the index for a specified column dataIndex.
8591 * @param {String} dataIndex The column dataIndex
8592 * @return {Number} the index, or -1 if not found
8595 findColumnIndex : function(dataIndex){
8596 for(var i = 0, len = this.config.length; i < len; i++){
8597 if(this.config[i].dataIndex == dataIndex){
8605 moveColumn : function(oldIndex, newIndex){
8606 var c = this.config[oldIndex];
8607 this.config.splice(oldIndex, 1);
8608 this.config.splice(newIndex, 0, c);
8609 this.dataMap = null;
8610 this.fireEvent("columnmoved", this, oldIndex, newIndex);
8613 isLocked : function(colIndex){
8614 return this.config[colIndex].locked === true;
8617 setLocked : function(colIndex, value, suppressEvent){
8618 if(this.isLocked(colIndex) == value){
8621 this.config[colIndex].locked = value;
8623 this.fireEvent("columnlockchange", this, colIndex, value);
8627 getTotalLockedWidth : function(){
8629 for(var i = 0; i < this.config.length; i++){
8630 if(this.isLocked(i) && !this.isHidden(i)){
8631 this.totalWidth += this.getColumnWidth(i);
8637 getLockedCount : function(){
8638 for(var i = 0, len = this.config.length; i < len; i++){
8639 if(!this.isLocked(i)){
8644 return this.config.length;
8648 * Returns the number of columns.
8651 getColumnCount : function(visibleOnly){
8652 if(visibleOnly === true){
8654 for(var i = 0, len = this.config.length; i < len; i++){
8655 if(!this.isHidden(i)){
8661 return this.config.length;
8665 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8666 * @param {Function} fn
8667 * @param {Object} scope (optional)
8668 * @return {Array} result
8670 getColumnsBy : function(fn, scope){
8672 for(var i = 0, len = this.config.length; i < len; i++){
8673 var c = this.config[i];
8674 if(fn.call(scope||this, c, i) === true){
8682 * Returns true if the specified column is sortable.
8683 * @param {Number} col The column index
8686 isSortable : function(col){
8687 if(typeof this.config[col].sortable == "undefined"){
8688 return this.defaultSortable;
8690 return this.config[col].sortable;
8694 * Returns the rendering (formatting) function defined for the column.
8695 * @param {Number} col The column index.
8696 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8698 getRenderer : function(col){
8699 if(!this.config[col].renderer){
8700 return Roo.grid.ColumnModel.defaultRenderer;
8702 return this.config[col].renderer;
8706 * Sets the rendering (formatting) function for a column.
8707 * @param {Number} col The column index
8708 * @param {Function} fn The function to use to process the cell's raw data
8709 * to return HTML markup for the grid view. The render function is called with
8710 * the following parameters:<ul>
8711 * <li>Data value.</li>
8712 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8713 * <li>css A CSS style string to apply to the table cell.</li>
8714 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8715 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8716 * <li>Row index</li>
8717 * <li>Column index</li>
8718 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8720 setRenderer : function(col, fn){
8721 this.config[col].renderer = fn;
8725 * Returns the width for the specified column.
8726 * @param {Number} col The column index
8727 * @param (optional) {String} gridSize bootstrap width size.
8730 getColumnWidth : function(col, gridSize)
8732 var cfg = this.config[col];
8734 if (typeof(gridSize) == 'undefined') {
8735 return cfg.width * 1 || this.defaultWidth;
8737 if (gridSize === false) { // if we set it..
8738 return cfg.width || false;
8740 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8742 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8743 if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8746 return cfg[ sizes[i] ];
8753 * Sets the width for a column.
8754 * @param {Number} col The column index
8755 * @param {Number} width The new width
8757 setColumnWidth : function(col, width, suppressEvent){
8758 this.config[col].width = width;
8759 this.totalWidth = null;
8761 this.fireEvent("widthchange", this, col, width);
8766 * Returns the total width of all columns.
8767 * @param {Boolean} includeHidden True to include hidden column widths
8770 getTotalWidth : function(includeHidden){
8771 if(!this.totalWidth){
8772 this.totalWidth = 0;
8773 for(var i = 0, len = this.config.length; i < len; i++){
8774 if(includeHidden || !this.isHidden(i)){
8775 this.totalWidth += this.getColumnWidth(i);
8779 return this.totalWidth;
8783 * Returns the header for the specified column.
8784 * @param {Number} col The column index
8787 getColumnHeader : function(col){
8788 return this.config[col].header;
8792 * Sets the header for a column.
8793 * @param {Number} col The column index
8794 * @param {String} header The new header
8796 setColumnHeader : function(col, header){
8797 this.config[col].header = header;
8798 this.fireEvent("headerchange", this, col, header);
8802 * Returns the tooltip for the specified column.
8803 * @param {Number} col The column index
8806 getColumnTooltip : function(col){
8807 return this.config[col].tooltip;
8810 * Sets the tooltip for a column.
8811 * @param {Number} col The column index
8812 * @param {String} tooltip The new tooltip
8814 setColumnTooltip : function(col, tooltip){
8815 this.config[col].tooltip = tooltip;
8819 * Returns the dataIndex for the specified column.
8820 * @param {Number} col The column index
8823 getDataIndex : function(col){
8824 return this.config[col].dataIndex;
8828 * Sets the dataIndex for a column.
8829 * @param {Number} col The column index
8830 * @param {Number} dataIndex The new dataIndex
8832 setDataIndex : function(col, dataIndex){
8833 this.config[col].dataIndex = dataIndex;
8839 * Returns true if the cell is editable.
8840 * @param {Number} colIndex The column index
8841 * @param {Number} rowIndex The row index - this is nto actually used..?
8844 isCellEditable : function(colIndex, rowIndex){
8845 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8849 * Returns the editor defined for the cell/column.
8850 * return false or null to disable editing.
8851 * @param {Number} colIndex The column index
8852 * @param {Number} rowIndex The row index
8855 getCellEditor : function(colIndex, rowIndex){
8856 return this.config[colIndex].editor;
8860 * Sets if a column is editable.
8861 * @param {Number} col The column index
8862 * @param {Boolean} editable True if the column is editable
8864 setEditable : function(col, editable){
8865 this.config[col].editable = editable;
8870 * Returns true if the column is hidden.
8871 * @param {Number} colIndex The column index
8874 isHidden : function(colIndex){
8875 return this.config[colIndex].hidden;
8880 * Returns true if the column width cannot be changed
8882 isFixed : function(colIndex){
8883 return this.config[colIndex].fixed;
8887 * Returns true if the column can be resized
8890 isResizable : function(colIndex){
8891 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8894 * Sets if a column is hidden.
8895 * @param {Number} colIndex The column index
8896 * @param {Boolean} hidden True if the column is hidden
8898 setHidden : function(colIndex, hidden){
8899 this.config[colIndex].hidden = hidden;
8900 this.totalWidth = null;
8901 this.fireEvent("hiddenchange", this, colIndex, hidden);
8905 * Sets the editor for a column.
8906 * @param {Number} col The column index
8907 * @param {Object} editor The editor object
8909 setEditor : function(col, editor){
8910 this.config[col].editor = editor;
8913 * Add a column (experimental...) - defaults to adding to the end..
8914 * @param {Object} config
8916 addColumn : function(c)
8919 var i = this.config.length;
8922 if(typeof c.dataIndex == "undefined"){
8925 if(typeof c.renderer == "string"){
8926 c.renderer = Roo.util.Format[c.renderer];
8928 if(typeof c.id == "undefined"){
8931 if(c.editor && c.editor.xtype){
8932 c.editor = Roo.factory(c.editor, Roo.grid);
8934 if(c.editor && c.editor.isFormField){
8935 c.editor = new Roo.grid.GridEditor(c.editor);
8937 this.lookup[c.id] = c;
8942 Roo.grid.ColumnModel.defaultRenderer = function(value)
8944 if(typeof value == "object") {
8947 if(typeof value == "string" && value.length < 1){
8951 return String.format("{0}", value);
8954 // Alias for backwards compatibility
8955 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8958 * Ext JS Library 1.1.1
8959 * Copyright(c) 2006-2007, Ext JS, LLC.
8961 * Originally Released Under LGPL - original licence link has changed is not relivant.
8964 * <script type="text/javascript">
8968 * @class Roo.LoadMask
8969 * A simple utility class for generically masking elements while loading data. If the element being masked has
8970 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8971 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
8972 * element's UpdateManager load indicator and will be destroyed after the initial load.
8974 * Create a new LoadMask
8975 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8976 * @param {Object} config The config object
8978 Roo.LoadMask = function(el, config){
8979 this.el = Roo.get(el);
8980 Roo.apply(this, config);
8982 this.store.on('beforeload', this.onBeforeLoad, this);
8983 this.store.on('load', this.onLoad, this);
8984 this.store.on('loadexception', this.onLoadException, this);
8985 this.removeMask = false;
8987 var um = this.el.getUpdateManager();
8988 um.showLoadIndicator = false; // disable the default indicator
8989 um.on('beforeupdate', this.onBeforeLoad, this);
8990 um.on('update', this.onLoad, this);
8991 um.on('failure', this.onLoad, this);
8992 this.removeMask = true;
8996 Roo.LoadMask.prototype = {
8998 * @cfg {Boolean} removeMask
8999 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
9000 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
9005 * The text to display in a centered loading message box (defaults to 'Loading...')
9009 * @cfg {String} msgCls
9010 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
9012 msgCls : 'x-mask-loading',
9015 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
9021 * Disables the mask to prevent it from being displayed
9023 disable : function(){
9024 this.disabled = true;
9028 * Enables the mask so that it can be displayed
9030 enable : function(){
9031 this.disabled = false;
9034 onLoadException : function()
9038 if (typeof(arguments[3]) != 'undefined') {
9039 Roo.MessageBox.alert("Error loading",arguments[3]);
9043 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9044 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9051 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9056 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
9060 onBeforeLoad : function(){
9062 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
9067 destroy : function(){
9069 this.store.un('beforeload', this.onBeforeLoad, this);
9070 this.store.un('load', this.onLoad, this);
9071 this.store.un('loadexception', this.onLoadException, this);
9073 var um = this.el.getUpdateManager();
9074 um.un('beforeupdate', this.onBeforeLoad, this);
9075 um.un('update', this.onLoad, this);
9076 um.un('failure', this.onLoad, this);
9080 * @class Roo.bootstrap.Table
9082 * @extends Roo.bootstrap.Component
9083 * @children Roo.bootstrap.TableBody
9084 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
9085 * Similar to Roo.grid.Grid
9087 var table = Roo.factory({
9089 xns : Roo.bootstrap,
9090 autoSizeColumns: true,
9097 sortInfo : { direction : 'ASC', field: 'name' },
9099 xtype : 'HttpProxy',
9102 url : 'https://example.com/some.data.url.json'
9105 xtype : 'JsonReader',
9107 fields : [ 'id', 'name', whatever' ],
9114 xtype : 'ColumnModel',
9118 dataIndex : 'is_in_group',
9121 renderer : function(v, x , r) {
9123 return String.format("{0}", v)
9129 xtype : 'RowSelectionModel',
9130 xns : Roo.bootstrap.Table
9131 // you can add listeners to catch selection change here....
9137 grid.render(Roo.get("some-div"));
9140 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
9145 * @cfg {Roo.grid.AbstractSelectionModel} sm The selection model to use (cell selection is not supported yet)
9146 * @cfg {Roo.data.Store} store The data store to use
9147 * @cfg {Roo.grid.ColumnModel} cm[] A column for the grid.
9149 * @cfg {String} cls table class
9152 * @cfg {string} empty_results Text to display for no results
9153 * @cfg {boolean} striped Should the rows be alternative striped
9154 * @cfg {boolean} bordered Add borders to the table
9155 * @cfg {boolean} hover Add hover highlighting
9156 * @cfg {boolean} condensed Format condensed
9157 * @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,
9158 * also adds table-responsive (see bootstrap docs for details)
9159 * @cfg {Boolean} loadMask (true|false) default false
9160 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
9161 * @cfg {Boolean} headerShow (true|false) generate thead, default true
9162 * @cfg {Boolean} rowSelection (true|false) default false
9163 * @cfg {Boolean} cellSelection (true|false) default false
9164 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header (with resizable columns)
9165 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
9166 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
9167 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
9168 * @cfg {Boolean} enableColumnResize default true if columns can be resized = needs scrollBody to be set to work (drag/drop)
9171 * @cfg {Number} minColumnWidth default 50 pixels minimum column width
9174 * Create a new Table
9175 * @param {Object} config The config object
9178 Roo.bootstrap.Table = function(config)
9180 Roo.bootstrap.Table.superclass.constructor.call(this, config);
9183 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
9184 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
9185 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
9186 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
9188 this.view = this; // compat with grid.
9190 this.sm = this.sm || {xtype: 'RowSelectionModel'};
9192 this.sm.grid = this;
9193 this.selModel = Roo.factory(this.sm, Roo.grid);
9194 this.sm = this.selModel;
9195 this.sm.xmodule = this.xmodule || false;
9198 if (this.cm && typeof(this.cm.config) == 'undefined') {
9199 this.colModel = new Roo.grid.ColumnModel(this.cm);
9200 this.cm = this.colModel;
9201 this.cm.xmodule = this.xmodule || false;
9204 this.store= Roo.factory(this.store, Roo.data);
9205 this.ds = this.store;
9206 this.ds.xmodule = this.xmodule || false;
9209 if (this.footer && this.store) {
9210 this.footer.dataSource = this.ds;
9211 this.footer = Roo.factory(this.footer);
9218 * Fires when a cell is clicked
9219 * @param {Roo.bootstrap.Table} this
9220 * @param {Roo.Element} el
9221 * @param {Number} rowIndex
9222 * @param {Number} columnIndex
9223 * @param {Roo.EventObject} e
9227 * @event celldblclick
9228 * Fires when a cell is double clicked
9229 * @param {Roo.bootstrap.Table} this
9230 * @param {Roo.Element} el
9231 * @param {Number} rowIndex
9232 * @param {Number} columnIndex
9233 * @param {Roo.EventObject} e
9235 "celldblclick" : true,
9238 * Fires when a row is clicked
9239 * @param {Roo.bootstrap.Table} this
9240 * @param {Roo.Element} el
9241 * @param {Number} rowIndex
9242 * @param {Roo.EventObject} e
9246 * @event rowdblclick
9247 * Fires when a row is double clicked
9248 * @param {Roo.bootstrap.Table} this
9249 * @param {Roo.Element} el
9250 * @param {Number} rowIndex
9251 * @param {Roo.EventObject} e
9253 "rowdblclick" : true,
9256 * Fires when a mouseover occur
9257 * @param {Roo.bootstrap.Table} this
9258 * @param {Roo.Element} el
9259 * @param {Number} rowIndex
9260 * @param {Number} columnIndex
9261 * @param {Roo.EventObject} e
9266 * Fires when a mouseout occur
9267 * @param {Roo.bootstrap.Table} this
9268 * @param {Roo.Element} el
9269 * @param {Number} rowIndex
9270 * @param {Number} columnIndex
9271 * @param {Roo.EventObject} e
9276 * Fires when a row is rendered, so you can change add a style to it.
9277 * @param {Roo.bootstrap.Table} this
9278 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
9282 * @event rowsrendered
9283 * Fires when all the rows have been rendered
9284 * @param {Roo.bootstrap.Table} this
9286 'rowsrendered' : true,
9288 * @event contextmenu
9289 * The raw contextmenu event for the entire grid.
9290 * @param {Roo.EventObject} e
9292 "contextmenu" : true,
9294 * @event rowcontextmenu
9295 * Fires when a row is right clicked
9296 * @param {Roo.bootstrap.Table} this
9297 * @param {Number} rowIndex
9298 * @param {Roo.EventObject} e
9300 "rowcontextmenu" : true,
9302 * @event cellcontextmenu
9303 * Fires when a cell is right clicked
9304 * @param {Roo.bootstrap.Table} this
9305 * @param {Number} rowIndex
9306 * @param {Number} cellIndex
9307 * @param {Roo.EventObject} e
9309 "cellcontextmenu" : true,
9311 * @event headercontextmenu
9312 * Fires when a header is right clicked
9313 * @param {Roo.bootstrap.Table} this
9314 * @param {Number} columnIndex
9315 * @param {Roo.EventObject} e
9317 "headercontextmenu" : true,
9320 * The raw mousedown event for the entire grid.
9321 * @param {Roo.EventObject} e
9328 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
9345 enableColumnResize: true,
9347 rowSelection : false,
9348 cellSelection : false,
9351 minColumnWidth : 50,
9353 // Roo.Element - the tbody
9354 bodyEl: false, // <tbody> Roo.Element - thead element
9355 headEl: false, // <thead> Roo.Element - thead element
9356 resizeProxy : false, // proxy element for dragging?
9360 container: false, // used by gridpanel...
9366 auto_hide_footer : false,
9368 view: false, // actually points to this..
9370 getAutoCreate : function()
9372 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
9379 // this get's auto added by panel.Grid
9380 if (this.scrollBody) {
9381 cfg.cls += ' table-body-fixed';
9384 cfg.cls += ' table-striped';
9388 cfg.cls += ' table-hover';
9390 if (this.bordered) {
9391 cfg.cls += ' table-bordered';
9393 if (this.condensed) {
9394 cfg.cls += ' table-condensed';
9397 if (this.responsive) {
9398 cfg.cls += ' table-responsive';
9402 cfg.cls+= ' ' +this.cls;
9408 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
9411 if(this.store || this.cm){
9412 if(this.headerShow){
9413 cfg.cn.push(this.renderHeader());
9416 cfg.cn.push(this.renderBody());
9418 if(this.footerShow){
9419 cfg.cn.push(this.renderFooter());
9421 // where does this come from?
9422 //cfg.cls+= ' TableGrid';
9425 return { cn : [ cfg ] };
9428 initEvents : function()
9430 if(!this.store || !this.cm){
9433 if (this.selModel) {
9434 this.selModel.initEvents();
9438 //Roo.log('initEvents with ds!!!!');
9440 this.bodyEl = this.el.select('tbody', true).first();
9441 this.headEl = this.el.select('thead', true).first();
9442 this.mainFoot = this.el.select('tfoot', true).first();
9447 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9448 e.on('click', this.sort, this);
9452 // why is this done????? = it breaks dialogs??
9453 //this.parent().el.setStyle('position', 'relative');
9457 this.footer.parentId = this.id;
9458 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9461 this.el.select('tfoot tr td').first().addClass('hide');
9466 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9469 this.store.on('load', this.onLoad, this);
9470 this.store.on('beforeload', this.onBeforeLoad, this);
9471 this.store.on('update', this.onUpdate, this);
9472 this.store.on('add', this.onAdd, this);
9473 this.store.on("clear", this.clear, this);
9475 this.el.on("contextmenu", this.onContextMenu, this);
9478 this.cm.on("headerchange", this.onHeaderChange, this);
9479 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9481 //?? does bodyEl get replaced on render?
9482 this.bodyEl.on("click", this.onClick, this);
9483 this.bodyEl.on("dblclick", this.onDblClick, this);
9484 this.bodyEl.on('scroll', this.onBodyScroll, this);
9486 // guessing mainbody will work - this relays usually caught by selmodel at present.
9487 this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9490 this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: ' ' });
9493 if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9494 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9499 // Compatibility with grid - we implement all the view features at present.
9500 getView : function()
9505 initCSS : function()
9509 var cm = this.cm, styles = [];
9510 this.CSS.removeStyleSheet(this.id + '-cssrules');
9511 var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9512 // we can honour xs/sm/md/xl as widths...
9513 // we first have to decide what widht we are currently at...
9514 var sz = Roo.getGridSize();
9518 var cols = []; // visable cols.
9520 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9521 var w = cm.getColumnWidth(i, false);
9523 cols.push( { rel : false, abs : 0 });
9527 cols.push( { rel : false, abs : w });
9529 last = i; // not really..
9532 var w = cm.getColumnWidth(i, sz);
9537 cols.push( { rel : w, abs : false });
9540 var avail = this.bodyEl.dom.clientWidth - total_abs;
9542 var unitWidth = Math.floor(avail / total);
9543 var rem = avail - (unitWidth * total);
9545 var hidden, width, pos = 0 , splithide , left;
9546 for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9548 hidden = 'display:none;';
9550 width = 'width:0px;';
9552 if(!cm.isHidden(i)){
9556 // we can honour xs/sm/md/xl ?
9557 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9559 hidden = 'display:none;';
9561 // width should return a small number...
9563 w+=rem; // add the remaining with..
9566 left = "left:" + (pos -4) + "px;";
9567 width = "width:" + w+ "px;";
9570 if (this.responsive) {
9573 hidden = cm.isHidden(i) ? 'display:none;' : '';
9574 splithide = 'display: none;';
9577 styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9580 splithide = 'display:none;';
9583 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9584 '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', (headHeight - 4), "px;}\n",
9585 // this is the popover version..
9586 '.popover-inner #' , this.id , ' .x-grid-split-' , i, " { ", left, splithide, 'height:', 100, "%;}\n"
9591 //Roo.log(styles.join(''));
9592 this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9598 onContextMenu : function(e, t)
9600 this.processEvent("contextmenu", e);
9603 processEvent : function(name, e)
9605 if (name != 'touchstart' ) {
9606 this.fireEvent(name, e);
9609 var t = e.getTarget();
9611 var cell = Roo.get(t);
9617 if(cell.findParent('tfoot', false, true)){
9621 if(cell.findParent('thead', false, true)){
9623 if(e.getTarget().nodeName.toLowerCase() != 'th'){
9624 cell = Roo.get(t).findParent('th', false, true);
9626 Roo.log("failed to find th in thead?");
9627 Roo.log(e.getTarget());
9632 var cellIndex = cell.dom.cellIndex;
9634 var ename = name == 'touchstart' ? 'click' : name;
9635 this.fireEvent("header" + ename, this, cellIndex, e);
9640 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9641 cell = Roo.get(t).findParent('td', false, true);
9643 Roo.log("failed to find th in tbody?");
9644 Roo.log(e.getTarget());
9649 var row = cell.findParent('tr', false, true);
9650 var cellIndex = cell.dom.cellIndex;
9651 var rowIndex = row.dom.rowIndex - 1;
9655 this.fireEvent("row" + name, this, rowIndex, e);
9659 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9665 onMouseover : function(e, el)
9667 var cell = Roo.get(el);
9673 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9674 cell = cell.findParent('td', false, true);
9677 var row = cell.findParent('tr', false, true);
9678 var cellIndex = cell.dom.cellIndex;
9679 var rowIndex = row.dom.rowIndex - 1; // start from 0
9681 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9685 onMouseout : function(e, el)
9687 var cell = Roo.get(el);
9693 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9694 cell = cell.findParent('td', false, true);
9697 var row = cell.findParent('tr', false, true);
9698 var cellIndex = cell.dom.cellIndex;
9699 var rowIndex = row.dom.rowIndex - 1; // start from 0
9701 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9705 onClick : function(e, el)
9707 var cell = Roo.get(el);
9709 if(!cell || (!this.cellSelection && !this.rowSelection)){
9713 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9714 cell = cell.findParent('td', false, true);
9717 if(!cell || typeof(cell) == 'undefined'){
9721 var row = cell.findParent('tr', false, true);
9723 if(!row || typeof(row) == 'undefined'){
9727 var cellIndex = cell.dom.cellIndex;
9728 var rowIndex = this.getRowIndex(row);
9730 // why??? - should these not be based on SelectionModel?
9731 //if(this.cellSelection){
9732 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9735 //if(this.rowSelection){
9736 this.fireEvent('rowclick', this, row, rowIndex, e);
9741 onDblClick : function(e,el)
9743 var cell = Roo.get(el);
9745 if(!cell || (!this.cellSelection && !this.rowSelection)){
9749 if(e.getTarget().nodeName.toLowerCase() != 'td'){
9750 cell = cell.findParent('td', false, true);
9753 if(!cell || typeof(cell) == 'undefined'){
9757 var row = cell.findParent('tr', false, true);
9759 if(!row || typeof(row) == 'undefined'){
9763 var cellIndex = cell.dom.cellIndex;
9764 var rowIndex = this.getRowIndex(row);
9766 if(this.cellSelection){
9767 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9770 if(this.rowSelection){
9771 this.fireEvent('rowdblclick', this, row, rowIndex, e);
9774 findRowIndex : function(el)
9776 var cell = Roo.get(el);
9780 var row = cell.findParent('tr', false, true);
9782 if(!row || typeof(row) == 'undefined'){
9785 return this.getRowIndex(row);
9787 sort : function(e,el)
9789 var col = Roo.get(el);
9791 if(!col.hasClass('sortable')){
9795 var sort = col.attr('sort');
9798 if(col.select('i', true).first().hasClass('fa-arrow-up')){
9802 this.store.sortInfo = {field : sort, direction : dir};
9805 Roo.log("calling footer first");
9806 this.footer.onClick('first');
9809 this.store.load({ params : { start : 0 } });
9813 renderHeader : function()
9821 this.totalWidth = 0;
9823 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9825 var config = cm.config[i];
9829 cls : 'x-hcol-' + i,
9832 html: cm.getColumnHeader(i)
9835 var tooltip = cm.getColumnTooltip(i);
9837 c.tooltip = tooltip;
9843 if(typeof(config.sortable) != 'undefined' && config.sortable){
9844 c.cls += ' sortable';
9845 c.html = '<i class="fa"></i>' + c.html;
9848 // could use BS4 hidden-..-down
9850 if(typeof(config.lgHeader) != 'undefined'){
9851 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9854 if(typeof(config.mdHeader) != 'undefined'){
9855 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9858 if(typeof(config.smHeader) != 'undefined'){
9859 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9862 if(typeof(config.xsHeader) != 'undefined'){
9863 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9870 if(typeof(config.tooltip) != 'undefined'){
9871 c.tooltip = config.tooltip;
9874 if(typeof(config.colspan) != 'undefined'){
9875 c.colspan = config.colspan;
9878 // hidden is handled by CSS now
9880 if(typeof(config.dataIndex) != 'undefined'){
9881 c.sort = config.dataIndex;
9886 if(typeof(config.align) != 'undefined' && config.align.length){
9887 c.style += ' text-align:' + config.align + ';';
9890 /* width is done in CSS
9891 *if(typeof(config.width) != 'undefined'){
9892 c.style += ' width:' + config.width + 'px;';
9893 this.totalWidth += config.width;
9895 this.totalWidth += 100; // assume minimum of 100 per column?
9899 if(typeof(config.cls) != 'undefined'){
9900 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9902 // this is the bit that doesnt reall work at all...
9904 if (this.responsive) {
9907 ['xs','sm','md','lg'].map(function(size){
9909 if(typeof(config[size]) == 'undefined'){
9913 if (!config[size]) { // 0 = hidden
9914 // BS 4 '0' is treated as hide that column and below.
9915 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9919 c.cls += ' col-' + size + '-' + config[size] + (
9920 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9928 c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9939 renderBody : function()
9949 colspan : this.cm.getColumnCount()
9959 renderFooter : function()
9969 colspan : this.cm.getColumnCount()
9983 // Roo.log('ds onload');
9988 var ds = this.store;
9990 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9991 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9992 if (_this.store.sortInfo) {
9994 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9995 e.select('i', true).addClass(['fa-arrow-up']);
9998 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9999 e.select('i', true).addClass(['fa-arrow-down']);
10004 var tbody = this.bodyEl;
10006 if(ds.getCount() > 0){
10007 ds.data.each(function(d,rowIndex){
10008 var row = this.renderRow(cm, ds, rowIndex);
10010 tbody.createChild(row);
10014 if(row.cellObjects.length){
10015 Roo.each(row.cellObjects, function(r){
10016 _this.renderCellObject(r);
10021 } else if (this.empty_results.length) {
10022 this.el.mask(this.empty_results, 'no-spinner');
10025 var tfoot = this.el.select('tfoot', true).first();
10027 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
10029 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
10031 var total = this.ds.getTotalCount();
10033 if(this.footer.pageSize < total){
10034 this.mainFoot.show();
10038 Roo.each(this.el.select('tbody td', true).elements, function(e){
10039 e.on('mouseover', _this.onMouseover, _this);
10042 Roo.each(this.el.select('tbody td', true).elements, function(e){
10043 e.on('mouseout', _this.onMouseout, _this);
10045 this.fireEvent('rowsrendered', this);
10049 this.initCSS(); /// resize cols
10055 onUpdate : function(ds,record)
10057 this.refreshRow(record);
10061 onRemove : function(ds, record, index, isUpdate){
10062 if(isUpdate !== true){
10063 this.fireEvent("beforerowremoved", this, index, record);
10065 var bt = this.bodyEl.dom;
10067 var rows = this.el.select('tbody > tr', true).elements;
10069 if(typeof(rows[index]) != 'undefined'){
10070 bt.removeChild(rows[index].dom);
10073 // if(bt.rows[index]){
10074 // bt.removeChild(bt.rows[index]);
10077 if(isUpdate !== true){
10078 //this.stripeRows(index);
10079 //this.syncRowHeights(index, index);
10081 this.fireEvent("rowremoved", this, index, record);
10085 onAdd : function(ds, records, rowIndex)
10087 //Roo.log('on Add called');
10088 // - note this does not handle multiple adding very well..
10089 var bt = this.bodyEl.dom;
10090 for (var i =0 ; i < records.length;i++) {
10091 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
10092 //Roo.log(records[i]);
10093 //Roo.log(this.store.getAt(rowIndex+i));
10094 this.insertRow(this.store, rowIndex + i, false);
10101 refreshRow : function(record){
10102 var ds = this.store, index;
10103 if(typeof record == 'number'){
10105 record = ds.getAt(index);
10107 index = ds.indexOf(record);
10109 return; // should not happen - but seems to
10112 this.insertRow(ds, index, true);
10114 this.onRemove(ds, record, index+1, true);
10116 //this.syncRowHeights(index, index);
10118 this.fireEvent("rowupdated", this, index, record);
10120 // private - called by RowSelection
10121 onRowSelect : function(rowIndex){
10122 var row = this.getRowDom(rowIndex);
10123 row.addClass(['bg-info','info']);
10125 // private - called by RowSelection
10126 onRowDeselect : function(rowIndex)
10128 if (rowIndex < 0) {
10131 var row = this.getRowDom(rowIndex);
10132 row.removeClass(['bg-info','info']);
10135 * Focuses the specified row.
10136 * @param {Number} row The row index
10138 focusRow : function(row)
10140 //Roo.log('GridView.focusRow');
10141 var x = this.bodyEl.dom.scrollLeft;
10142 this.focusCell(row, 0, false);
10143 this.bodyEl.dom.scrollLeft = x;
10147 * Focuses the specified cell.
10148 * @param {Number} row The row index
10149 * @param {Number} col The column index
10150 * @param {Boolean} hscroll false to disable horizontal scrolling
10152 focusCell : function(row, col, hscroll)
10154 //Roo.log('GridView.focusCell');
10155 var el = this.ensureVisible(row, col, hscroll);
10156 // not sure what focusEL achives = it's a <a> pos relative
10157 //this.focusEl.alignTo(el, "tl-tl");
10159 // this.focusEl.focus();
10161 // this.focusEl.focus.defer(1, this.focusEl);
10166 * Scrolls the specified cell into view
10167 * @param {Number} row The row index
10168 * @param {Number} col The column index
10169 * @param {Boolean} hscroll false to disable horizontal scrolling
10171 ensureVisible : function(row, col, hscroll)
10173 //Roo.log('GridView.ensureVisible,' + row + ',' + col);
10174 //return null; //disable for testing.
10175 if(typeof row != "number"){
10176 row = row.rowIndex;
10178 if(row < 0 && row >= this.ds.getCount()){
10181 col = (col !== undefined ? col : 0);
10183 while(cm.isHidden(col)){
10187 var el = this.getCellDom(row, col);
10191 var c = this.bodyEl.dom;
10193 var ctop = parseInt(el.offsetTop, 10);
10194 var cleft = parseInt(el.offsetLeft, 10);
10195 var cbot = ctop + el.offsetHeight;
10196 var cright = cleft + el.offsetWidth;
10198 //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
10199 var ch = 0; //?? header is not withing the area?
10200 var stop = parseInt(c.scrollTop, 10);
10201 var sleft = parseInt(c.scrollLeft, 10);
10202 var sbot = stop + ch;
10203 var sright = sleft + c.clientWidth;
10205 Roo.log('GridView.ensureVisible:' +
10207 ' c.clientHeight:' + c.clientHeight +
10208 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
10216 c.scrollTop = ctop;
10217 //Roo.log("set scrolltop to ctop DISABLE?");
10218 }else if(cbot > sbot){
10219 //Roo.log("set scrolltop to cbot-ch");
10220 c.scrollTop = cbot-ch;
10223 if(hscroll !== false){
10225 c.scrollLeft = cleft;
10226 }else if(cright > sright){
10227 c.scrollLeft = cright-c.clientWidth;
10235 insertRow : function(dm, rowIndex, isUpdate){
10238 this.fireEvent("beforerowsinserted", this, rowIndex);
10240 //var s = this.getScrollState();
10241 var row = this.renderRow(this.cm, this.store, rowIndex);
10242 // insert before rowIndex..
10243 var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
10247 if(row.cellObjects.length){
10248 Roo.each(row.cellObjects, function(r){
10249 _this.renderCellObject(r);
10254 this.fireEvent("rowsinserted", this, rowIndex);
10255 //this.syncRowHeights(firstRow, lastRow);
10256 //this.stripeRows(firstRow);
10263 getRowDom : function(rowIndex)
10265 var rows = this.el.select('tbody > tr', true).elements;
10267 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
10270 getCellDom : function(rowIndex, colIndex)
10272 var row = this.getRowDom(rowIndex);
10273 if (row === false) {
10276 var cols = row.select('td', true).elements;
10277 return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
10281 // returns the object tree for a tr..
10284 renderRow : function(cm, ds, rowIndex)
10286 var d = ds.getAt(rowIndex);
10290 cls : 'x-row-' + rowIndex,
10294 var cellObjects = [];
10296 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
10297 var config = cm.config[i];
10299 var renderer = cm.getRenderer(i);
10303 if(typeof(renderer) !== 'undefined'){
10304 value = renderer(d.data[cm.getDataIndex(i)], false, d);
10306 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
10307 // and are rendered into the cells after the row is rendered - using the id for the element.
10309 if(typeof(value) === 'object'){
10319 rowIndex : rowIndex,
10324 this.fireEvent('rowclass', this, rowcfg);
10328 // this might end up displaying HTML?
10329 // this is too messy... - better to only do it on columsn you know are going to be too long
10330 //tooltip : (typeof(value) === 'object') ? '' : value,
10331 cls : rowcfg.rowClass + ' x-col-' + i,
10333 html: (typeof(value) === 'object') ? '' : value
10340 if(typeof(config.colspan) != 'undefined'){
10341 td.colspan = config.colspan;
10346 if(typeof(config.align) != 'undefined' && config.align.length){
10347 td.style += ' text-align:' + config.align + ';';
10349 if(typeof(config.valign) != 'undefined' && config.valign.length){
10350 td.style += ' vertical-align:' + config.valign + ';';
10353 if(typeof(config.width) != 'undefined'){
10354 td.style += ' width:' + config.width + 'px;';
10358 if(typeof(config.cursor) != 'undefined'){
10359 td.style += ' cursor:' + config.cursor + ';';
10362 if(typeof(config.cls) != 'undefined'){
10363 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
10365 if (this.responsive) {
10366 ['xs','sm','md','lg'].map(function(size){
10368 if(typeof(config[size]) == 'undefined'){
10374 if (!config[size]) { // 0 = hidden
10375 // BS 4 '0' is treated as hide that column and below.
10376 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
10380 td.cls += ' col-' + size + '-' + config[size] + (
10381 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
10391 row.cellObjects = cellObjects;
10399 onBeforeLoad : function()
10401 this.el.unmask(); // if needed.
10408 this.el.select('tbody', true).first().dom.innerHTML = '';
10411 * Show or hide a row.
10412 * @param {Number} rowIndex to show or hide
10413 * @param {Boolean} state hide
10415 setRowVisibility : function(rowIndex, state)
10417 var bt = this.bodyEl.dom;
10419 var rows = this.el.select('tbody > tr', true).elements;
10421 if(typeof(rows[rowIndex]) == 'undefined'){
10424 rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
10429 getSelectionModel : function(){
10430 if(!this.selModel){
10431 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
10433 return this.selModel;
10436 * Render the Roo.bootstrap object from renderder
10438 renderCellObject : function(r)
10442 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10444 var t = r.cfg.render(r.container);
10447 Roo.each(r.cfg.cn, function(c){
10449 container: t.getChildContainer(),
10452 _this.renderCellObject(child);
10457 * get the Row Index from a dom element.
10458 * @param {Roo.Element} row The row to look for
10459 * @returns {Number} the row
10461 getRowIndex : function(row)
10465 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10476 * get the header TH element for columnIndex
10477 * @param {Number} columnIndex
10478 * @returns {Roo.Element}
10480 getHeaderIndex: function(colIndex)
10482 var cols = this.headEl.select('th', true).elements;
10483 return cols[colIndex];
10486 * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10487 * @param {domElement} cell to look for
10488 * @returns {Number} the column
10490 getCellIndex : function(cell)
10492 var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10494 return parseInt(id[1], 10);
10499 * Returns the grid's underlying element = used by panel.Grid
10500 * @return {Element} The element
10502 getGridEl : function(){
10506 * Forces a resize - used by panel.Grid
10507 * @return {Element} The element
10509 autoSize : function()
10511 //var ctr = Roo.get(this.container.dom.parentElement);
10512 var ctr = Roo.get(this.el.dom);
10514 var thd = this.getGridEl().select('thead',true).first();
10515 var tbd = this.getGridEl().select('tbody', true).first();
10516 var tfd = this.getGridEl().select('tfoot', true).first();
10518 var cw = ctr.getWidth();
10519 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
10523 tbd.setWidth(ctr.getWidth());
10524 // if the body has a max height - and then scrolls - we should perhaps set up the height here
10525 // this needs fixing for various usage - currently only hydra job advers I think..
10527 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10529 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10532 cw = Math.max(cw, this.totalWidth);
10533 this.getGridEl().select('tbody tr',true).setWidth(cw);
10536 // resize 'expandable coloumn?
10538 return; // we doe not have a view in this design..
10541 onBodyScroll: function()
10543 //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10545 this.headEl.setStyle({
10546 'position' : 'relative',
10547 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10553 var scrollHeight = this.bodyEl.dom.scrollHeight;
10555 var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10557 var height = this.bodyEl.getHeight();
10559 if(scrollHeight - height == scrollTop) {
10561 var total = this.ds.getTotalCount();
10563 if(this.footer.cursor + this.footer.pageSize < total){
10565 this.footer.ds.load({
10567 start : this.footer.cursor + this.footer.pageSize,
10568 limit : this.footer.pageSize
10577 onColumnSplitterMoved : function(i, diff)
10579 this.userResized = true;
10581 var cm = this.colModel;
10583 var w = this.getHeaderIndex(i).getWidth() + diff;
10586 cm.setColumnWidth(i, w, true);
10588 //var cid = cm.getColumnId(i); << not used in this version?
10589 /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10591 this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10592 this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10593 this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10595 //this.updateSplitters();
10596 //this.layout(); << ??
10597 this.fireEvent("columnresize", i, w);
10599 onHeaderChange : function()
10601 var header = this.renderHeader();
10602 var table = this.el.select('table', true).first();
10604 this.headEl.remove();
10605 this.headEl = table.createChild(header, this.bodyEl, false);
10607 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10608 e.on('click', this.sort, this);
10611 if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10612 new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10617 onHiddenChange : function(colModel, colIndex, hidden)
10620 this.cm.setHidden()
10621 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10622 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10624 this.CSS.updateRule(thSelector, "display", "");
10625 this.CSS.updateRule(tdSelector, "display", "");
10628 this.CSS.updateRule(thSelector, "display", "none");
10629 this.CSS.updateRule(tdSelector, "display", "none");
10632 // onload calls initCSS()
10633 this.onHeaderChange();
10637 setColumnWidth: function(col_index, width)
10639 // width = "md-2 xs-2..."
10640 if(!this.colModel.config[col_index]) {
10644 var w = width.split(" ");
10646 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10648 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10651 for(var j = 0; j < w.length; j++) {
10657 var size_cls = w[j].split("-");
10659 if(!Number.isInteger(size_cls[1] * 1)) {
10663 if(!this.colModel.config[col_index][size_cls[0]]) {
10667 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10671 h_row[0].classList.replace(
10672 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10673 "col-"+size_cls[0]+"-"+size_cls[1]
10676 for(var i = 0; i < rows.length; i++) {
10678 var size_cls = w[j].split("-");
10680 if(!Number.isInteger(size_cls[1] * 1)) {
10684 if(!this.colModel.config[col_index][size_cls[0]]) {
10688 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10692 rows[i].classList.replace(
10693 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10694 "col-"+size_cls[0]+"-"+size_cls[1]
10698 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10703 // currently only used to find the split on drag..
10704 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10709 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10710 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10719 * @class Roo.bootstrap.TableCell
10720 * @extends Roo.bootstrap.Component
10721 * @children Roo.bootstrap.Component
10722 * @parent Roo.bootstrap.TableRow
10723 * Bootstrap TableCell class
10725 * @cfg {String} html cell contain text
10726 * @cfg {String} cls cell class
10727 * @cfg {String} tag cell tag (td|th) default td
10728 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10729 * @cfg {String} align Aligns the content in a cell
10730 * @cfg {String} axis Categorizes cells
10731 * @cfg {String} bgcolor Specifies the background color of a cell
10732 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10733 * @cfg {Number} colspan Specifies the number of columns a cell should span
10734 * @cfg {String} headers Specifies one or more header cells a cell is related to
10735 * @cfg {Number} height Sets the height of a cell
10736 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10737 * @cfg {Number} rowspan Sets the number of rows a cell should span
10738 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10739 * @cfg {String} valign Vertical aligns the content in a cell
10740 * @cfg {Number} width Specifies the width of a cell
10743 * Create a new TableCell
10744 * @param {Object} config The config object
10747 Roo.bootstrap.TableCell = function(config){
10748 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10751 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
10771 getAutoCreate : function(){
10772 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10779 cfg.tag = this.tag;
10792 cfg.align=this.align
10797 if (this.bgcolor) {
10798 cfg.bgcolor=this.bgcolor
10800 if (this.charoff) {
10801 cfg.charoff=this.charoff
10803 if (this.colspan) {
10804 cfg.colspan=this.colspan
10806 if (this.headers) {
10807 cfg.headers=this.headers
10810 cfg.height=this.height
10813 cfg.nowrap=this.nowrap
10815 if (this.rowspan) {
10816 cfg.rowspan=this.rowspan
10819 cfg.scope=this.scope
10822 cfg.valign=this.valign
10825 cfg.width=this.width
10844 * @class Roo.bootstrap.TableRow
10845 * @extends Roo.bootstrap.Component
10846 * @children Roo.bootstrap.TableCell
10847 * @parent Roo.bootstrap.TableBody
10848 * Bootstrap TableRow class
10849 * @cfg {String} cls row class
10850 * @cfg {String} align Aligns the content in a table row
10851 * @cfg {String} bgcolor Specifies a background color for a table row
10852 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10853 * @cfg {String} valign Vertical aligns the content in a table row
10856 * Create a new TableRow
10857 * @param {Object} config The config object
10860 Roo.bootstrap.TableRow = function(config){
10861 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10864 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
10872 getAutoCreate : function(){
10873 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10880 cfg.cls = this.cls;
10883 cfg.align = this.align;
10886 cfg.bgcolor = this.bgcolor;
10889 cfg.charoff = this.charoff;
10892 cfg.valign = this.valign;
10910 * @class Roo.bootstrap.TableBody
10911 * @extends Roo.bootstrap.Component
10912 * @children Roo.bootstrap.TableRow
10913 * @parent Roo.bootstrap.Table
10914 * Bootstrap TableBody class
10915 * @cfg {String} cls element class
10916 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10917 * @cfg {String} align Aligns the content inside the element
10918 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10919 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10922 * Create a new TableBody
10923 * @param {Object} config The config object
10926 Roo.bootstrap.TableBody = function(config){
10927 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10930 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
10938 getAutoCreate : function(){
10939 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10949 cfg.tag = this.tag;
10953 cfg.align = this.align;
10956 cfg.charoff = this.charoff;
10959 cfg.valign = this.valign;
10966 // initEvents : function()
10969 // if(!this.store){
10973 // this.store = Roo.factory(this.store, Roo.data);
10974 // this.store.on('load', this.onLoad, this);
10976 // this.store.load();
10980 // onLoad: function ()
10982 // this.fireEvent('load', this);
10992 * Ext JS Library 1.1.1
10993 * Copyright(c) 2006-2007, Ext JS, LLC.
10995 * Originally Released Under LGPL - original licence link has changed is not relivant.
10998 * <script type="text/javascript">
11001 // as we use this in bootstrap.
11002 Roo.namespace('Roo.form');
11004 * @class Roo.form.Action
11005 * Internal Class used to handle form actions
11007 * @param {Roo.form.BasicForm} el The form element or its id
11008 * @param {Object} config Configuration options
11013 // define the action interface
11014 Roo.form.Action = function(form, options){
11016 this.options = options || {};
11019 * Client Validation Failed
11022 Roo.form.Action.CLIENT_INVALID = 'client';
11024 * Server Validation Failed
11027 Roo.form.Action.SERVER_INVALID = 'server';
11029 * Connect to Server Failed
11032 Roo.form.Action.CONNECT_FAILURE = 'connect';
11034 * Reading Data from Server Failed
11037 Roo.form.Action.LOAD_FAILURE = 'load';
11039 Roo.form.Action.prototype = {
11041 failureType : undefined,
11042 response : undefined,
11043 result : undefined,
11045 // interface method
11046 run : function(options){
11050 // interface method
11051 success : function(response){
11055 // interface method
11056 handleResponse : function(response){
11060 // default connection failure
11061 failure : function(response){
11063 this.response = response;
11064 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11065 this.form.afterAction(this, false);
11068 processResponse : function(response){
11069 this.response = response;
11070 if(!response.responseText){
11073 this.result = this.handleResponse(response);
11074 return this.result;
11077 // utility functions used internally
11078 getUrl : function(appendParams){
11079 var url = this.options.url || this.form.url || this.form.el.dom.action;
11081 var p = this.getParams();
11083 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11089 getMethod : function(){
11090 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
11093 getParams : function(){
11094 var bp = this.form.baseParams;
11095 var p = this.options.params;
11097 if(typeof p == "object"){
11098 p = Roo.urlEncode(Roo.applyIf(p, bp));
11099 }else if(typeof p == 'string' && bp){
11100 p += '&' + Roo.urlEncode(bp);
11103 p = Roo.urlEncode(bp);
11108 createCallback : function(){
11110 success: this.success,
11111 failure: this.failure,
11113 timeout: (this.form.timeout*1000),
11114 upload: this.form.fileUpload ? this.success : undefined
11119 Roo.form.Action.Submit = function(form, options){
11120 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
11123 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
11126 haveProgress : false,
11127 uploadComplete : false,
11129 // uploadProgress indicator.
11130 uploadProgress : function()
11132 if (!this.form.progressUrl) {
11136 if (!this.haveProgress) {
11137 Roo.MessageBox.progress("Uploading", "Uploading");
11139 if (this.uploadComplete) {
11140 Roo.MessageBox.hide();
11144 this.haveProgress = true;
11146 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
11148 var c = new Roo.data.Connection();
11150 url : this.form.progressUrl,
11155 success : function(req){
11156 //console.log(data);
11160 rdata = Roo.decode(req.responseText)
11162 Roo.log("Invalid data from server..");
11166 if (!rdata || !rdata.success) {
11168 Roo.MessageBox.alert(Roo.encode(rdata));
11171 var data = rdata.data;
11173 if (this.uploadComplete) {
11174 Roo.MessageBox.hide();
11179 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
11180 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
11183 this.uploadProgress.defer(2000,this);
11186 failure: function(data) {
11187 Roo.log('progress url failed ');
11198 // run get Values on the form, so it syncs any secondary forms.
11199 this.form.getValues();
11201 var o = this.options;
11202 var method = this.getMethod();
11203 var isPost = method == 'POST';
11204 if(o.clientValidation === false || this.form.isValid()){
11206 if (this.form.progressUrl) {
11207 this.form.findField('UPLOAD_IDENTIFIER').setValue(
11208 (new Date() * 1) + '' + Math.random());
11213 Roo.Ajax.request(Roo.apply(this.createCallback(), {
11214 form:this.form.el.dom,
11215 url:this.getUrl(!isPost),
11217 params:isPost ? this.getParams() : null,
11218 isUpload: this.form.fileUpload,
11219 formData : this.form.formData
11222 this.uploadProgress();
11224 }else if (o.clientValidation !== false){ // client validation failed
11225 this.failureType = Roo.form.Action.CLIENT_INVALID;
11226 this.form.afterAction(this, false);
11230 success : function(response)
11232 this.uploadComplete= true;
11233 if (this.haveProgress) {
11234 Roo.MessageBox.hide();
11238 var result = this.processResponse(response);
11239 if(result === true || result.success){
11240 this.form.afterAction(this, true);
11244 this.form.markInvalid(result.errors);
11245 this.failureType = Roo.form.Action.SERVER_INVALID;
11247 this.form.afterAction(this, false);
11249 failure : function(response)
11251 this.uploadComplete= true;
11252 if (this.haveProgress) {
11253 Roo.MessageBox.hide();
11256 this.response = response;
11257 this.failureType = Roo.form.Action.CONNECT_FAILURE;
11258 this.form.afterAction(this, false);
11261 handleResponse : function(response){
11262 if(this.form.errorReader){
11263 var rs = this.form.errorReader.read(response);
11266 for(var i = 0, len = rs.records.length; i < len; i++) {
11267 var r = rs.records[i];
11268 errors[i] = r.data;
11271 if(errors.length < 1){
11275 success : rs.success,
11281 ret = Roo.decode(response.responseText);
11285 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
11295 Roo.form.Action.Load = function(form, options){
11296 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
11297 this.reader = this.form.reader;
11300 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
11305 Roo.Ajax.request(Roo.apply(
11306 this.createCallback(), {
11307 method:this.getMethod(),
11308 url:this.getUrl(false),
11309 params:this.getParams()
11313 success : function(response){
11315 var result = this.processResponse(response);
11316 if(result === true || !result.success || !result.data){
11317 this.failureType = Roo.form.Action.LOAD_FAILURE;
11318 this.form.afterAction(this, false);
11321 this.form.clearInvalid();
11322 this.form.setValues(result.data);
11323 this.form.afterAction(this, true);
11326 handleResponse : function(response){
11327 if(this.form.reader){
11328 var rs = this.form.reader.read(response);
11329 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
11331 success : rs.success,
11335 return Roo.decode(response.responseText);
11339 Roo.form.Action.ACTION_TYPES = {
11340 'load' : Roo.form.Action.Load,
11341 'submit' : Roo.form.Action.Submit
11350 * @class Roo.bootstrap.form.Form
11351 * @extends Roo.bootstrap.Component
11352 * @children Roo.bootstrap.Component
11353 * Bootstrap Form class
11354 * @cfg {String} method GET | POST (default POST)
11355 * @cfg {String} labelAlign top | left (default top)
11356 * @cfg {String} align left | right - for navbars
11357 * @cfg {Boolean} loadMask load mask when submit (default true)
11361 * Create a new Form
11362 * @param {Object} config The config object
11366 Roo.bootstrap.form.Form = function(config){
11368 Roo.bootstrap.form.Form.superclass.constructor.call(this, config);
11370 Roo.bootstrap.form.Form.popover.apply();
11374 * @event clientvalidation
11375 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
11376 * @param {Form} this
11377 * @param {Boolean} valid true if the form has passed client-side validation
11379 clientvalidation: true,
11381 * @event beforeaction
11382 * Fires before any action is performed. Return false to cancel the action.
11383 * @param {Form} this
11384 * @param {Action} action The action to be performed
11386 beforeaction: true,
11388 * @event actionfailed
11389 * Fires when an action fails.
11390 * @param {Form} this
11391 * @param {Action} action The action that failed
11393 actionfailed : true,
11395 * @event actioncomplete
11396 * Fires when an action is completed.
11397 * @param {Form} this
11398 * @param {Action} action The action that completed
11400 actioncomplete : true
11404 Roo.extend(Roo.bootstrap.form.Form, Roo.bootstrap.Component, {
11407 * @cfg {String} method
11408 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
11412 * @cfg {String} url
11413 * The URL to use for form actions if one isn't supplied in the action options.
11416 * @cfg {Boolean} fileUpload
11417 * Set to true if this form is a file upload.
11421 * @cfg {Object} baseParams
11422 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
11426 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
11430 * @cfg {Sting} align (left|right) for navbar forms
11435 activeAction : null,
11438 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
11439 * element by passing it or its id or mask the form itself by passing in true.
11442 waitMsgTarget : false,
11447 * @cfg {Boolean} errorMask (true|false) default false
11452 * @cfg {Number} maskOffset Default 100
11457 * @cfg {Boolean} maskBody
11461 getAutoCreate : function(){
11465 method : this.method || 'POST',
11466 id : this.id || Roo.id(),
11469 if (this.parent().xtype.match(/^Nav/)) {
11470 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11474 if (this.labelAlign == 'left' ) {
11475 cfg.cls += ' form-horizontal';
11481 initEvents : function()
11483 this.el.on('submit', this.onSubmit, this);
11484 // this was added as random key presses on the form where triggering form submit.
11485 this.el.on('keypress', function(e) {
11486 if (e.getCharCode() != 13) {
11489 // we might need to allow it for textareas.. and some other items.
11490 // check e.getTarget().
11492 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11496 Roo.log("keypress blocked");
11498 e.preventDefault();
11504 onSubmit : function(e){
11509 * Returns true if client-side validation on the form is successful.
11512 isValid : function(){
11513 var items = this.getItems();
11515 var target = false;
11517 items.each(function(f){
11523 Roo.log('invalid field: ' + f.name);
11527 if(!target && f.el.isVisible(true)){
11533 if(this.errorMask && !valid){
11534 Roo.bootstrap.form.Form.popover.mask(this, target);
11541 * Returns true if any fields in this form have changed since their original load.
11544 isDirty : function(){
11546 var items = this.getItems();
11547 items.each(function(f){
11557 * Performs a predefined action (submit or load) or custom actions you define on this form.
11558 * @param {String} actionName The name of the action type
11559 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
11560 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11561 * accept other config options):
11563 Property Type Description
11564 ---------------- --------------- ----------------------------------------------------------------------------------
11565 url String The url for the action (defaults to the form's url)
11566 method String The form method to use (defaults to the form's method, or POST if not defined)
11567 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
11568 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
11569 validate the form on the client (defaults to false)
11571 * @return {BasicForm} this
11573 doAction : function(action, options){
11574 if(typeof action == 'string'){
11575 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11577 if(this.fireEvent('beforeaction', this, action) !== false){
11578 this.beforeAction(action);
11579 action.run.defer(100, action);
11585 beforeAction : function(action){
11586 var o = action.options;
11591 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11593 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11596 // not really supported yet.. ??
11598 //if(this.waitMsgTarget === true){
11599 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11600 //}else if(this.waitMsgTarget){
11601 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11602 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11604 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11610 afterAction : function(action, success){
11611 this.activeAction = null;
11612 var o = action.options;
11617 Roo.get(document.body).unmask();
11623 //if(this.waitMsgTarget === true){
11624 // this.el.unmask();
11625 //}else if(this.waitMsgTarget){
11626 // this.waitMsgTarget.unmask();
11628 // Roo.MessageBox.updateProgress(1);
11629 // Roo.MessageBox.hide();
11636 Roo.callback(o.success, o.scope, [this, action]);
11637 this.fireEvent('actioncomplete', this, action);
11641 // failure condition..
11642 // we have a scenario where updates need confirming.
11643 // eg. if a locking scenario exists..
11644 // we look for { errors : { needs_confirm : true }} in the response.
11646 (typeof(action.result) != 'undefined') &&
11647 (typeof(action.result.errors) != 'undefined') &&
11648 (typeof(action.result.errors.needs_confirm) != 'undefined')
11651 Roo.log("not supported yet");
11654 Roo.MessageBox.confirm(
11655 "Change requires confirmation",
11656 action.result.errorMsg,
11661 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
11671 Roo.callback(o.failure, o.scope, [this, action]);
11672 // show an error message if no failed handler is set..
11673 if (!this.hasListener('actionfailed')) {
11674 Roo.log("need to add dialog support");
11676 Roo.MessageBox.alert("Error",
11677 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11678 action.result.errorMsg :
11679 "Saving Failed, please check your entries or try again"
11684 this.fireEvent('actionfailed', this, action);
11689 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11690 * @param {String} id The value to search for
11693 findField : function(id){
11694 var items = this.getItems();
11695 var field = items.get(id);
11697 items.each(function(f){
11698 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11705 return field || null;
11708 * Mark fields in this form invalid in bulk.
11709 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11710 * @return {BasicForm} this
11712 markInvalid : function(errors){
11713 if(errors instanceof Array){
11714 for(var i = 0, len = errors.length; i < len; i++){
11715 var fieldError = errors[i];
11716 var f = this.findField(fieldError.id);
11718 f.markInvalid(fieldError.msg);
11724 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11725 field.markInvalid(errors[id]);
11729 //Roo.each(this.childForms || [], function (f) {
11730 // f.markInvalid(errors);
11737 * Set values for fields in this form in bulk.
11738 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11739 * @return {BasicForm} this
11741 setValues : function(values){
11742 if(values instanceof Array){ // array of objects
11743 for(var i = 0, len = values.length; i < len; i++){
11745 var f = this.findField(v.id);
11747 f.setValue(v.value);
11748 if(this.trackResetOnLoad){
11749 f.originalValue = f.getValue();
11753 }else{ // object hash
11756 if(typeof values[id] != 'function' && (field = this.findField(id))){
11758 if (field.setFromData &&
11759 field.valueField &&
11760 field.displayField &&
11761 // combos' with local stores can
11762 // be queried via setValue()
11763 // to set their value..
11764 (field.store && !field.store.isLocal)
11768 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11769 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11770 field.setFromData(sd);
11772 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11774 field.setFromData(values);
11777 field.setValue(values[id]);
11781 if(this.trackResetOnLoad){
11782 field.originalValue = field.getValue();
11788 //Roo.each(this.childForms || [], function (f) {
11789 // f.setValues(values);
11796 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11797 * they are returned as an array.
11798 * @param {Boolean} asString
11801 getValues : function(asString){
11802 //if (this.childForms) {
11803 // copy values from the child forms
11804 // Roo.each(this.childForms, function (f) {
11805 // this.setValues(f.getValues());
11811 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11812 if(asString === true){
11815 return Roo.urlDecode(fs);
11819 * Returns the fields in this form as an object with key/value pairs.
11820 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11823 getFieldValues : function(with_hidden)
11825 var items = this.getItems();
11827 items.each(function(f){
11829 if (!f.getName()) {
11833 var v = f.getValue();
11835 if (f.inputType =='radio') {
11836 if (typeof(ret[f.getName()]) == 'undefined') {
11837 ret[f.getName()] = ''; // empty..
11840 if (!f.el.dom.checked) {
11844 v = f.el.dom.value;
11848 if(f.xtype == 'MoneyField'){
11849 ret[f.currencyName] = f.getCurrency();
11852 // not sure if this supported any more..
11853 if ((typeof(v) == 'object') && f.getRawValue) {
11854 v = f.getRawValue() ; // dates..
11856 // combo boxes where name != hiddenName...
11857 if (f.name !== false && f.name != '' && f.name != f.getName()) {
11858 ret[f.name] = f.getRawValue();
11860 ret[f.getName()] = v;
11867 * Clears all invalid messages in this form.
11868 * @return {BasicForm} this
11870 clearInvalid : function(){
11871 var items = this.getItems();
11873 items.each(function(f){
11881 * Resets this form.
11882 * @return {BasicForm} this
11884 reset : function(){
11885 var items = this.getItems();
11886 items.each(function(f){
11890 Roo.each(this.childForms || [], function (f) {
11898 getItems : function()
11900 var r=new Roo.util.MixedCollection(false, function(o){
11901 return o.id || (o.id = Roo.id());
11903 var iter = function(el) {
11910 Roo.each(el.items,function(e) {
11919 hideFields : function(items)
11921 Roo.each(items, function(i){
11923 var f = this.findField(i);
11934 showFields : function(items)
11936 Roo.each(items, function(i){
11938 var f = this.findField(i);
11951 Roo.apply(Roo.bootstrap.form.Form, {
11967 intervalID : false,
11973 if(this.isApplied){
11978 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11979 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11980 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11981 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11984 this.maskEl.top.enableDisplayMode("block");
11985 this.maskEl.left.enableDisplayMode("block");
11986 this.maskEl.bottom.enableDisplayMode("block");
11987 this.maskEl.right.enableDisplayMode("block");
11989 this.toolTip = new Roo.bootstrap.Tooltip({
11990 cls : 'roo-form-error-popover',
11992 'left' : ['r-l', [-2,0], 'right'],
11993 'right' : ['l-r', [2,0], 'left'],
11994 'bottom' : ['tl-bl', [0,2], 'top'],
11995 'top' : [ 'bl-tl', [0,-2], 'bottom']
11999 this.toolTip.render(Roo.get(document.body));
12001 this.toolTip.el.enableDisplayMode("block");
12003 Roo.get(document.body).on('click', function(){
12007 Roo.get(document.body).on('touchstart', function(){
12011 this.isApplied = true
12014 mask : function(form, target)
12018 this.target = target;
12020 if(!this.form.errorMask || !target.el){
12024 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
12026 Roo.log(scrollable);
12028 var ot = this.target.el.calcOffsetsTo(scrollable);
12030 var scrollTo = ot[1] - this.form.maskOffset;
12032 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
12034 scrollable.scrollTo('top', scrollTo);
12036 var box = this.target.el.getBox();
12038 var zIndex = Roo.bootstrap.Modal.zIndex++;
12041 this.maskEl.top.setStyle('position', 'absolute');
12042 this.maskEl.top.setStyle('z-index', zIndex);
12043 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
12044 this.maskEl.top.setLeft(0);
12045 this.maskEl.top.setTop(0);
12046 this.maskEl.top.show();
12048 this.maskEl.left.setStyle('position', 'absolute');
12049 this.maskEl.left.setStyle('z-index', zIndex);
12050 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
12051 this.maskEl.left.setLeft(0);
12052 this.maskEl.left.setTop(box.y - this.padding);
12053 this.maskEl.left.show();
12055 this.maskEl.bottom.setStyle('position', 'absolute');
12056 this.maskEl.bottom.setStyle('z-index', zIndex);
12057 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
12058 this.maskEl.bottom.setLeft(0);
12059 this.maskEl.bottom.setTop(box.bottom + this.padding);
12060 this.maskEl.bottom.show();
12062 this.maskEl.right.setStyle('position', 'absolute');
12063 this.maskEl.right.setStyle('z-index', zIndex);
12064 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
12065 this.maskEl.right.setLeft(box.right + this.padding);
12066 this.maskEl.right.setTop(box.y - this.padding);
12067 this.maskEl.right.show();
12069 this.toolTip.bindEl = this.target.el;
12071 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
12073 var tip = this.target.blankText;
12075 if(this.target.getValue() !== '' ) {
12077 if (this.target.invalidText.length) {
12078 tip = this.target.invalidText;
12079 } else if (this.target.regexText.length){
12080 tip = this.target.regexText;
12084 this.toolTip.show(tip);
12086 this.intervalID = window.setInterval(function() {
12087 Roo.bootstrap.form.Form.popover.unmask();
12090 window.onwheel = function(){ return false;};
12092 (function(){ this.isMasked = true; }).defer(500, this);
12096 unmask : function()
12098 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
12102 this.maskEl.top.setStyle('position', 'absolute');
12103 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
12104 this.maskEl.top.hide();
12106 this.maskEl.left.setStyle('position', 'absolute');
12107 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
12108 this.maskEl.left.hide();
12110 this.maskEl.bottom.setStyle('position', 'absolute');
12111 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
12112 this.maskEl.bottom.hide();
12114 this.maskEl.right.setStyle('position', 'absolute');
12115 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
12116 this.maskEl.right.hide();
12118 this.toolTip.hide();
12120 this.toolTip.el.hide();
12122 window.onwheel = function(){ return true;};
12124 if(this.intervalID){
12125 window.clearInterval(this.intervalID);
12126 this.intervalID = false;
12129 this.isMasked = false;
12139 * Ext JS Library 1.1.1
12140 * Copyright(c) 2006-2007, Ext JS, LLC.
12142 * Originally Released Under LGPL - original licence link has changed is not relivant.
12145 * <script type="text/javascript">
12148 * @class Roo.form.VTypes
12149 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
12152 Roo.form.VTypes = function(){
12153 // closure these in so they are only created once.
12154 var alpha = /^[a-zA-Z_]+$/;
12155 var alphanum = /^[a-zA-Z0-9_]+$/;
12156 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
12157 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
12159 // All these messages and functions are configurable
12162 * The function used to validate email addresses
12163 * @param {String} value The email address
12165 'email' : function(v){
12166 return email.test(v);
12169 * The error text to display when the email validation function returns false
12172 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
12174 * The keystroke filter mask to be applied on email input
12177 'emailMask' : /[a-z0-9_\.\-@]/i,
12180 * The function used to validate URLs
12181 * @param {String} value The URL
12183 'url' : function(v){
12184 return url.test(v);
12187 * The error text to display when the url validation function returns false
12190 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
12193 * The function used to validate alpha values
12194 * @param {String} value The value
12196 'alpha' : function(v){
12197 return alpha.test(v);
12200 * The error text to display when the alpha validation function returns false
12203 'alphaText' : 'This field should only contain letters and _',
12205 * The keystroke filter mask to be applied on alpha input
12208 'alphaMask' : /[a-z_]/i,
12211 * The function used to validate alphanumeric values
12212 * @param {String} value The value
12214 'alphanum' : function(v){
12215 return alphanum.test(v);
12218 * The error text to display when the alphanumeric validation function returns false
12221 'alphanumText' : 'This field should only contain letters, numbers and _',
12223 * The keystroke filter mask to be applied on alphanumeric input
12226 'alphanumMask' : /[a-z0-9_]/i
12236 * @class Roo.bootstrap.form.Input
12237 * @extends Roo.bootstrap.Component
12238 * Bootstrap Input class
12239 * @cfg {Boolean} disabled is it disabled
12240 * @cfg {String} inputType (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text)
12241 * @cfg {String} name name of the input
12242 * @cfg {string} fieldLabel - the label associated
12243 * @cfg {string} placeholder - placeholder to put in text.
12244 * @cfg {string} before - input group add on before
12245 * @cfg {string} after - input group add on after
12246 * @cfg {string} size - (lg|sm) or leave empty..
12247 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
12248 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
12249 * @cfg {Number} md colspan out of 12 for computer-sized screens
12250 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
12251 * @cfg {string} value default value of the input
12252 * @cfg {Number} labelWidth set the width of label
12253 * @cfg {Number} labellg set the width of label (1-12)
12254 * @cfg {Number} labelmd set the width of label (1-12)
12255 * @cfg {Number} labelsm set the width of label (1-12)
12256 * @cfg {Number} labelxs set the width of label (1-12)
12257 * @cfg {String} labelAlign (top|left)
12258 * @cfg {Boolean} readOnly Specifies that the field should be read-only
12259 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
12260 * @cfg {String} indicatorpos (left|right) default left
12261 * @cfg {String} capture (user|camera) use for file input only. (default empty)
12262 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
12263 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
12264 * @cfg {Roo.bootstrap.Button} before Button to show before
12265 * @cfg {Roo.bootstrap.Button} afterButton to show before
12266 * @cfg {String} align (left|center|right) Default left
12267 * @cfg {Boolean} forceFeedback (true|false) Default false
12270 * Create a new Input
12271 * @param {Object} config The config object
12274 Roo.bootstrap.form.Input = function(config){
12276 Roo.bootstrap.form.Input.superclass.constructor.call(this, config);
12281 * Fires when this field receives input focus.
12282 * @param {Roo.form.Field} this
12287 * Fires when this field loses input focus.
12288 * @param {Roo.form.Field} this
12292 * @event specialkey
12293 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
12294 * {@link Roo.EventObject#getKey} to determine which key was pressed.
12295 * @param {Roo.form.Field} this
12296 * @param {Roo.EventObject} e The event object
12301 * Fires just before the field blurs if the field value has changed.
12302 * @param {Roo.form.Field} this
12303 * @param {Mixed} newValue The new value
12304 * @param {Mixed} oldValue The original value
12309 * Fires after the field has been marked as invalid.
12310 * @param {Roo.form.Field} this
12311 * @param {String} msg The validation message
12316 * Fires after the field has been validated with no errors.
12317 * @param {Roo.form.Field} this
12322 * Fires after the key up
12323 * @param {Roo.form.Field} this
12324 * @param {Roo.EventObject} e The event Object
12329 * Fires after the user pastes into input
12330 * @param {Roo.form.Field} this
12331 * @param {Roo.EventObject} e The event Object
12337 Roo.extend(Roo.bootstrap.form.Input, Roo.bootstrap.Component, {
12339 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
12340 automatic validation (defaults to "keyup").
12342 validationEvent : "keyup",
12344 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
12346 validateOnBlur : true,
12348 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
12350 validationDelay : 250,
12352 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
12354 focusClass : "x-form-focus", // not needed???
12358 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12360 invalidClass : "has-warning",
12363 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
12365 validClass : "has-success",
12368 * @cfg {Boolean} hasFeedback (true|false) default true
12370 hasFeedback : true,
12373 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12375 invalidFeedbackClass : "glyphicon-warning-sign",
12378 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
12380 validFeedbackClass : "glyphicon-ok",
12383 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
12385 selectOnFocus : false,
12388 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
12392 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
12397 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
12399 disableKeyFilter : false,
12402 * @cfg {Boolean} disabled True to disable the field (defaults to false).
12406 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
12410 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
12412 blankText : "Please complete this mandatory field",
12415 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
12419 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
12421 maxLength : Number.MAX_VALUE,
12423 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
12425 minLengthText : "The minimum length for this field is {0}",
12427 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
12429 maxLengthText : "The maximum length for this field is {0}",
12433 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
12434 * If available, this function will be called only after the basic validators all return true, and will be passed the
12435 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
12439 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
12440 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
12441 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
12445 * @cfg {String} regexText -- Depricated - use Invalid Text
12450 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12456 autocomplete: false,
12460 inputType : 'text',
12463 placeholder: false,
12468 preventMark: false,
12469 isFormField : true,
12472 labelAlign : false,
12475 formatedValue : false,
12476 forceFeedback : false,
12478 indicatorpos : 'left',
12488 parentLabelAlign : function()
12491 while (parent.parent()) {
12492 parent = parent.parent();
12493 if (typeof(parent.labelAlign) !='undefined') {
12494 return parent.labelAlign;
12501 getAutoCreate : function()
12503 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12509 if(this.inputType != 'hidden'){
12510 cfg.cls = 'form-group' //input-group
12516 type : this.inputType,
12517 value : this.value,
12518 cls : 'form-control',
12519 placeholder : this.placeholder || '',
12520 autocomplete : this.autocomplete || 'new-password'
12522 if (this.inputType == 'file') {
12523 input.style = 'overflow:hidden'; // why not in CSS?
12526 if(this.capture.length){
12527 input.capture = this.capture;
12530 if(this.accept.length){
12531 input.accept = this.accept + "/*";
12535 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12538 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12539 input.maxLength = this.maxLength;
12542 if (this.disabled) {
12543 input.disabled=true;
12546 if (this.readOnly) {
12547 input.readonly=true;
12551 input.name = this.name;
12555 input.cls += ' input-' + this.size;
12559 ['xs','sm','md','lg'].map(function(size){
12560 if (settings[size]) {
12561 cfg.cls += ' col-' + size + '-' + settings[size];
12565 var inputblock = input;
12569 cls: 'glyphicon form-control-feedback'
12572 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12575 cls : 'has-feedback',
12583 if (this.before || this.after) {
12586 cls : 'input-group',
12590 if (this.before && typeof(this.before) == 'string') {
12592 inputblock.cn.push({
12594 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12598 if (this.before && typeof(this.before) == 'object') {
12599 this.before = Roo.factory(this.before);
12601 inputblock.cn.push({
12603 cls : 'roo-input-before input-group-prepend input-group-' +
12604 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12608 inputblock.cn.push(input);
12610 if (this.after && typeof(this.after) == 'string') {
12611 inputblock.cn.push({
12613 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12617 if (this.after && typeof(this.after) == 'object') {
12618 this.after = Roo.factory(this.after);
12620 inputblock.cn.push({
12622 cls : 'roo-input-after input-group-append input-group-' +
12623 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
12627 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12628 inputblock.cls += ' has-feedback';
12629 inputblock.cn.push(feedback);
12634 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12635 tooltip : 'This field is required'
12637 if (this.allowBlank ) {
12638 indicator.style = this.allowBlank ? ' display:none' : '';
12640 if (align ==='left' && this.fieldLabel.length) {
12642 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12649 cls : 'control-label col-form-label',
12650 html : this.fieldLabel
12661 var labelCfg = cfg.cn[1];
12662 var contentCfg = cfg.cn[2];
12664 if(this.indicatorpos == 'right'){
12669 cls : 'control-label col-form-label',
12673 html : this.fieldLabel
12687 labelCfg = cfg.cn[0];
12688 contentCfg = cfg.cn[1];
12692 if(this.labelWidth > 12){
12693 labelCfg.style = "width: " + this.labelWidth + 'px';
12696 if(this.labelWidth < 13 && this.labelmd == 0){
12697 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12700 if(this.labellg > 0){
12701 labelCfg.cls += ' col-lg-' + this.labellg;
12702 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12705 if(this.labelmd > 0){
12706 labelCfg.cls += ' col-md-' + this.labelmd;
12707 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12710 if(this.labelsm > 0){
12711 labelCfg.cls += ' col-sm-' + this.labelsm;
12712 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12715 if(this.labelxs > 0){
12716 labelCfg.cls += ' col-xs-' + this.labelxs;
12717 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12721 } else if ( this.fieldLabel.length) {
12728 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12729 tooltip : 'This field is required',
12730 style : this.allowBlank ? ' display:none' : ''
12734 //cls : 'input-group-addon',
12735 html : this.fieldLabel
12743 if(this.indicatorpos == 'right'){
12748 //cls : 'input-group-addon',
12749 html : this.fieldLabel
12754 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12755 tooltip : 'This field is required',
12756 style : this.allowBlank ? ' display:none' : ''
12776 if (this.parentType === 'Navbar' && this.parent().bar) {
12777 cfg.cls += ' navbar-form';
12780 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12781 // on BS4 we do this only if not form
12782 cfg.cls += ' navbar-form';
12790 * return the real input element.
12792 inputEl: function ()
12794 return this.el.select('input.form-control',true).first();
12797 tooltipEl : function()
12799 return this.inputEl();
12802 indicatorEl : function()
12804 if (Roo.bootstrap.version == 4) {
12805 return false; // not enabled in v4 yet.
12808 var indicator = this.el.select('i.roo-required-indicator',true).first();
12818 setDisabled : function(v)
12820 var i = this.inputEl().dom;
12822 i.removeAttribute('disabled');
12826 i.setAttribute('disabled','true');
12828 initEvents : function()
12831 this.inputEl().on("keydown" , this.fireKey, this);
12832 this.inputEl().on("focus", this.onFocus, this);
12833 this.inputEl().on("blur", this.onBlur, this);
12835 this.inputEl().relayEvent('keyup', this);
12836 this.inputEl().relayEvent('paste', this);
12838 this.indicator = this.indicatorEl();
12840 if(this.indicator){
12841 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
12844 // reference to original value for reset
12845 this.originalValue = this.getValue();
12846 //Roo.form.TextField.superclass.initEvents.call(this);
12847 if(this.validationEvent == 'keyup'){
12848 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12849 this.inputEl().on('keyup', this.filterValidation, this);
12851 else if(this.validationEvent !== false){
12852 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12855 if(this.selectOnFocus){
12856 this.on("focus", this.preFocus, this);
12859 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12860 this.inputEl().on("keypress", this.filterKeys, this);
12862 this.inputEl().relayEvent('keypress', this);
12865 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
12866 this.el.on("click", this.autoSize, this);
12869 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12870 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12873 if (typeof(this.before) == 'object') {
12874 this.before.render(this.el.select('.roo-input-before',true).first());
12876 if (typeof(this.after) == 'object') {
12877 this.after.render(this.el.select('.roo-input-after',true).first());
12880 this.inputEl().on('change', this.onChange, this);
12883 filterValidation : function(e){
12884 if(!e.isNavKeyPress()){
12885 this.validationTask.delay(this.validationDelay);
12889 * Validates the field value
12890 * @return {Boolean} True if the value is valid, else false
12892 validate : function(){
12893 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12894 if(this.disabled || this.validateValue(this.getRawValue())){
12899 this.markInvalid();
12905 * Validates a value according to the field's validation rules and marks the field as invalid
12906 * if the validation fails
12907 * @param {Mixed} value The value to validate
12908 * @return {Boolean} True if the value is valid, else false
12910 validateValue : function(value)
12912 if(this.getVisibilityEl().hasClass('hidden')){
12916 if(value.length < 1) { // if it's blank
12917 if(this.allowBlank){
12923 if(value.length < this.minLength){
12926 if(value.length > this.maxLength){
12930 var vt = Roo.form.VTypes;
12931 if(!vt[this.vtype](value, this)){
12935 if(typeof this.validator == "function"){
12936 var msg = this.validator(value);
12940 if (typeof(msg) == 'string') {
12941 this.invalidText = msg;
12945 if(this.regex && !this.regex.test(value)){
12953 fireKey : function(e){
12954 //Roo.log('field ' + e.getKey());
12955 if(e.isNavKeyPress()){
12956 this.fireEvent("specialkey", this, e);
12959 focus : function (selectText){
12961 this.inputEl().focus();
12962 if(selectText === true){
12963 this.inputEl().dom.select();
12969 onFocus : function(){
12970 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12971 // this.el.addClass(this.focusClass);
12973 if(!this.hasFocus){
12974 this.hasFocus = true;
12975 this.startValue = this.getValue();
12976 this.fireEvent("focus", this);
12980 beforeBlur : Roo.emptyFn,
12984 onBlur : function(){
12986 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12987 //this.el.removeClass(this.focusClass);
12989 this.hasFocus = false;
12990 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12993 var v = this.getValue();
12994 if(String(v) !== String(this.startValue)){
12995 this.fireEvent('change', this, v, this.startValue);
12997 this.fireEvent("blur", this);
13000 onChange : function(e)
13002 var v = this.getValue();
13003 if(String(v) !== String(this.startValue)){
13004 this.fireEvent('change', this, v, this.startValue);
13010 * Resets the current field value to the originally loaded value and clears any validation messages
13012 reset : function(){
13013 this.setValue(this.originalValue);
13017 * Returns the name of the field
13018 * @return {Mixed} name The name field
13020 getName: function(){
13024 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13025 * @return {Mixed} value The field value
13027 getValue : function(){
13029 var v = this.inputEl().getValue();
13034 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
13035 * @return {Mixed} value The field value
13037 getRawValue : function(){
13038 var v = this.inputEl().getValue();
13044 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
13045 * @param {Mixed} value The value to set
13047 setRawValue : function(v){
13048 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13051 selectText : function(start, end){
13052 var v = this.getRawValue();
13054 start = start === undefined ? 0 : start;
13055 end = end === undefined ? v.length : end;
13056 var d = this.inputEl().dom;
13057 if(d.setSelectionRange){
13058 d.setSelectionRange(start, end);
13059 }else if(d.createTextRange){
13060 var range = d.createTextRange();
13061 range.moveStart("character", start);
13062 range.moveEnd("character", v.length-end);
13069 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
13070 * @param {Mixed} value The value to set
13072 setValue : function(v){
13075 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
13081 processValue : function(value){
13082 if(this.stripCharsRe){
13083 var newValue = value.replace(this.stripCharsRe, '');
13084 if(newValue !== value){
13085 this.setRawValue(newValue);
13092 preFocus : function(){
13094 if(this.selectOnFocus){
13095 this.inputEl().dom.select();
13098 filterKeys : function(e){
13099 var k = e.getKey();
13100 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
13103 var c = e.getCharCode(), cc = String.fromCharCode(c);
13104 if(Roo.isIE && (e.isSpecialKey() || !cc)){
13107 if(!this.maskRe.test(cc)){
13112 * Clear any invalid styles/messages for this field
13114 clearInvalid : function(){
13116 if(!this.el || this.preventMark){ // not rendered
13121 this.el.removeClass([this.invalidClass, 'is-invalid']);
13123 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13125 var feedback = this.el.select('.form-control-feedback', true).first();
13128 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13133 if(this.indicator){
13134 this.indicator.removeClass('visible');
13135 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13138 this.fireEvent('valid', this);
13142 * Mark this field as valid
13144 markValid : function()
13146 if(!this.el || this.preventMark){ // not rendered...
13150 this.el.removeClass([this.invalidClass, this.validClass]);
13151 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13153 var feedback = this.el.select('.form-control-feedback', true).first();
13156 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13159 if(this.indicator){
13160 this.indicator.removeClass('visible');
13161 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13169 if(this.allowBlank && !this.getRawValue().length){
13172 if (Roo.bootstrap.version == 3) {
13173 this.el.addClass(this.validClass);
13175 this.inputEl().addClass('is-valid');
13178 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13180 var feedback = this.el.select('.form-control-feedback', true).first();
13183 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13189 this.fireEvent('valid', this);
13193 * Mark this field as invalid
13194 * @param {String} msg The validation message
13196 markInvalid : function(msg)
13198 if(!this.el || this.preventMark){ // not rendered
13202 this.el.removeClass([this.invalidClass, this.validClass]);
13203 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13205 var feedback = this.el.select('.form-control-feedback', true).first();
13208 this.el.select('.form-control-feedback', true).first().removeClass(
13209 [this.invalidFeedbackClass, this.validFeedbackClass]);
13216 if(this.allowBlank && !this.getRawValue().length){
13220 if(this.indicator){
13221 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
13222 this.indicator.addClass('visible');
13224 if (Roo.bootstrap.version == 3) {
13225 this.el.addClass(this.invalidClass);
13227 this.inputEl().addClass('is-invalid');
13232 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13234 var feedback = this.el.select('.form-control-feedback', true).first();
13237 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13239 if(this.getValue().length || this.forceFeedback){
13240 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13247 this.fireEvent('invalid', this, msg);
13250 SafariOnKeyDown : function(event)
13252 // this is a workaround for a password hang bug on chrome/ webkit.
13253 if (this.inputEl().dom.type != 'password') {
13257 var isSelectAll = false;
13259 if(this.inputEl().dom.selectionEnd > 0){
13260 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
13262 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
13263 event.preventDefault();
13268 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
13270 event.preventDefault();
13271 // this is very hacky as keydown always get's upper case.
13273 var cc = String.fromCharCode(event.getCharCode());
13274 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
13278 adjustWidth : function(tag, w){
13279 tag = tag.toLowerCase();
13280 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
13281 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
13282 if(tag == 'input'){
13285 if(tag == 'textarea'){
13288 }else if(Roo.isOpera){
13289 if(tag == 'input'){
13292 if(tag == 'textarea'){
13300 setFieldLabel : function(v)
13302 if(!this.rendered){
13306 if(this.indicatorEl()){
13307 var ar = this.el.select('label > span',true);
13309 if (ar.elements.length) {
13310 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13311 this.fieldLabel = v;
13315 var br = this.el.select('label',true);
13317 if(br.elements.length) {
13318 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13319 this.fieldLabel = v;
13323 Roo.log('Cannot Found any of label > span || label in input');
13327 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
13328 this.fieldLabel = v;
13343 * @class Roo.bootstrap.form.TextArea
13344 * @extends Roo.bootstrap.form.Input
13345 * Bootstrap TextArea class
13346 * @cfg {Number} cols Specifies the visible width of a text area
13347 * @cfg {Number} rows Specifies the visible number of lines in a text area
13348 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
13349 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
13350 * @cfg {string} html text
13353 * Create a new TextArea
13354 * @param {Object} config The config object
13357 Roo.bootstrap.form.TextArea = function(config){
13358 Roo.bootstrap.form.TextArea.superclass.constructor.call(this, config);
13362 Roo.extend(Roo.bootstrap.form.TextArea, Roo.bootstrap.form.Input, {
13372 getAutoCreate : function(){
13374 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13380 if(this.inputType != 'hidden'){
13381 cfg.cls = 'form-group' //input-group
13389 value : this.value || '',
13390 html: this.html || '',
13391 cls : 'form-control',
13392 placeholder : this.placeholder || ''
13396 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
13397 input.maxLength = this.maxLength;
13401 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
13405 input.cols = this.cols;
13408 if (this.readOnly) {
13409 input.readonly = true;
13413 input.name = this.name;
13417 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
13421 ['xs','sm','md','lg'].map(function(size){
13422 if (settings[size]) {
13423 cfg.cls += ' col-' + size + '-' + settings[size];
13427 var inputblock = input;
13429 if(this.hasFeedback && !this.allowBlank){
13433 cls: 'glyphicon form-control-feedback'
13437 cls : 'has-feedback',
13446 if (this.before || this.after) {
13449 cls : 'input-group',
13453 inputblock.cn.push({
13455 cls : 'input-group-addon',
13460 inputblock.cn.push(input);
13462 if(this.hasFeedback && !this.allowBlank){
13463 inputblock.cls += ' has-feedback';
13464 inputblock.cn.push(feedback);
13468 inputblock.cn.push({
13470 cls : 'input-group-addon',
13477 if (align ==='left' && this.fieldLabel.length) {
13482 cls : 'control-label',
13483 html : this.fieldLabel
13494 if(this.labelWidth > 12){
13495 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13498 if(this.labelWidth < 13 && this.labelmd == 0){
13499 this.labelmd = this.labelWidth;
13502 if(this.labellg > 0){
13503 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13504 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13507 if(this.labelmd > 0){
13508 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13509 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13512 if(this.labelsm > 0){
13513 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13514 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13517 if(this.labelxs > 0){
13518 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13519 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13522 } else if ( this.fieldLabel.length) {
13527 //cls : 'input-group-addon',
13528 html : this.fieldLabel
13546 if (this.disabled) {
13547 input.disabled=true;
13554 * return the real textarea element.
13556 inputEl: function ()
13558 return this.el.select('textarea.form-control',true).first();
13562 * Clear any invalid styles/messages for this field
13564 clearInvalid : function()
13567 if(!this.el || this.preventMark){ // not rendered
13571 var label = this.el.select('label', true).first();
13572 var icon = this.el.select('i.fa-star', true).first();
13577 this.el.removeClass( this.validClass);
13578 this.inputEl().removeClass('is-invalid');
13580 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13582 var feedback = this.el.select('.form-control-feedback', true).first();
13585 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13590 this.fireEvent('valid', this);
13594 * Mark this field as valid
13596 markValid : function()
13598 if(!this.el || this.preventMark){ // not rendered
13602 this.el.removeClass([this.invalidClass, this.validClass]);
13603 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13605 var feedback = this.el.select('.form-control-feedback', true).first();
13608 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13611 if(this.disabled || this.allowBlank){
13615 var label = this.el.select('label', true).first();
13616 var icon = this.el.select('i.fa-star', true).first();
13621 if (Roo.bootstrap.version == 3) {
13622 this.el.addClass(this.validClass);
13624 this.inputEl().addClass('is-valid');
13628 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13630 var feedback = this.el.select('.form-control-feedback', true).first();
13633 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13634 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13639 this.fireEvent('valid', this);
13643 * Mark this field as invalid
13644 * @param {String} msg The validation message
13646 markInvalid : function(msg)
13648 if(!this.el || this.preventMark){ // not rendered
13652 this.el.removeClass([this.invalidClass, this.validClass]);
13653 this.inputEl().removeClass(['is-valid', 'is-invalid']);
13655 var feedback = this.el.select('.form-control-feedback', true).first();
13658 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13661 if(this.disabled || this.allowBlank){
13665 var label = this.el.select('label', true).first();
13666 var icon = this.el.select('i.fa-star', true).first();
13668 if(!this.getValue().length && label && !icon){
13669 this.el.createChild({
13671 cls : 'text-danger fa fa-lg fa-star',
13672 tooltip : 'This field is required',
13673 style : 'margin-right:5px;'
13677 if (Roo.bootstrap.version == 3) {
13678 this.el.addClass(this.invalidClass);
13680 this.inputEl().addClass('is-invalid');
13683 // fixme ... this may be depricated need to test..
13684 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13686 var feedback = this.el.select('.form-control-feedback', true).first();
13689 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13691 if(this.getValue().length || this.forceFeedback){
13692 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13699 this.fireEvent('invalid', this, msg);
13707 * trigger field - base class for combo..
13712 * @class Roo.bootstrap.form.TriggerField
13713 * @extends Roo.bootstrap.form.Input
13714 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13715 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13716 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13717 * for which you can provide a custom implementation. For example:
13719 var trigger = new Roo.bootstrap.form.TriggerField();
13720 trigger.onTriggerClick = myTriggerFn;
13721 trigger.applyTo('my-field');
13724 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13725 * {@link Roo.bootstrap.form.DateField} and {@link Roo.bootstrap.form.ComboBox} are perfect examples of this.
13726 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
13727 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13728 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13731 * Create a new TriggerField.
13732 * @param {Object} config Configuration options (valid {@Roo.bootstrap.form.Input} config options will also be applied
13733 * to the base TextField)
13735 Roo.bootstrap.form.TriggerField = function(config){
13736 this.mimicing = false;
13737 Roo.bootstrap.form.TriggerField.superclass.constructor.call(this, config);
13740 Roo.extend(Roo.bootstrap.form.TriggerField, Roo.bootstrap.form.Input, {
13742 * @cfg {String} triggerClass A CSS class to apply to the trigger
13745 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13750 * @cfg {Boolean} removable (true|false) special filter default false
13754 /** @cfg {Boolean} grow @hide */
13755 /** @cfg {Number} growMin @hide */
13756 /** @cfg {Number} growMax @hide */
13762 autoSize: Roo.emptyFn,
13766 deferHeight : true,
13769 actionMode : 'wrap',
13774 getAutoCreate : function(){
13776 var align = this.labelAlign || this.parentLabelAlign();
13781 cls: 'form-group' //input-group
13788 type : this.inputType,
13789 cls : 'form-control',
13790 autocomplete: 'new-password',
13791 placeholder : this.placeholder || ''
13795 input.name = this.name;
13798 input.cls += ' input-' + this.size;
13801 if (this.disabled) {
13802 input.disabled=true;
13805 var inputblock = input;
13807 if(this.hasFeedback && !this.allowBlank){
13811 cls: 'glyphicon form-control-feedback'
13814 if(this.removable && !this.editable ){
13816 cls : 'has-feedback',
13822 cls : 'roo-combo-removable-btn close'
13829 cls : 'has-feedback',
13838 if(this.removable && !this.editable ){
13840 cls : 'roo-removable',
13846 cls : 'roo-combo-removable-btn close'
13853 if (this.before || this.after) {
13856 cls : 'input-group',
13860 inputblock.cn.push({
13862 cls : 'input-group-addon input-group-prepend input-group-text',
13867 inputblock.cn.push(input);
13869 if(this.hasFeedback && !this.allowBlank){
13870 inputblock.cls += ' has-feedback';
13871 inputblock.cn.push(feedback);
13875 inputblock.cn.push({
13877 cls : 'input-group-addon input-group-append input-group-text',
13886 var ibwrap = inputblock;
13891 cls: 'roo-select2-choices',
13895 cls: 'roo-select2-search-field',
13907 cls: 'roo-select2-container input-group',
13912 cls: 'form-hidden-field'
13918 if(!this.multiple && this.showToggleBtn){
13924 if (this.caret != false) {
13927 cls: 'fa fa-' + this.caret
13934 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13936 Roo.bootstrap.version == 3 ? caret : '',
13939 cls: 'combobox-clear',
13953 combobox.cls += ' roo-select2-container-multi';
13957 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13958 tooltip : 'This field is required'
13960 if (Roo.bootstrap.version == 4) {
13963 style : 'display:none'
13968 if (align ==='left' && this.fieldLabel.length) {
13970 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
13977 cls : 'control-label',
13978 html : this.fieldLabel
13990 var labelCfg = cfg.cn[1];
13991 var contentCfg = cfg.cn[2];
13993 if(this.indicatorpos == 'right'){
13998 cls : 'control-label',
14002 html : this.fieldLabel
14016 labelCfg = cfg.cn[0];
14017 contentCfg = cfg.cn[1];
14020 if(this.labelWidth > 12){
14021 labelCfg.style = "width: " + this.labelWidth + 'px';
14024 if(this.labelWidth < 13 && this.labelmd == 0){
14025 this.labelmd = this.labelWidth;
14028 if(this.labellg > 0){
14029 labelCfg.cls += ' col-lg-' + this.labellg;
14030 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14033 if(this.labelmd > 0){
14034 labelCfg.cls += ' col-md-' + this.labelmd;
14035 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14038 if(this.labelsm > 0){
14039 labelCfg.cls += ' col-sm-' + this.labelsm;
14040 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14043 if(this.labelxs > 0){
14044 labelCfg.cls += ' col-xs-' + this.labelxs;
14045 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14048 } else if ( this.fieldLabel.length) {
14049 // Roo.log(" label");
14054 //cls : 'input-group-addon',
14055 html : this.fieldLabel
14063 if(this.indicatorpos == 'right'){
14071 html : this.fieldLabel
14085 // Roo.log(" no label && no align");
14092 ['xs','sm','md','lg'].map(function(size){
14093 if (settings[size]) {
14094 cfg.cls += ' col-' + size + '-' + settings[size];
14105 onResize : function(w, h){
14106 // Roo.bootstrap.form.TriggerField.superclass.onResize.apply(this, arguments);
14107 // if(typeof w == 'number'){
14108 // var x = w - this.trigger.getWidth();
14109 // this.inputEl().setWidth(this.adjustWidth('input', x));
14110 // this.trigger.setStyle('left', x+'px');
14115 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14118 getResizeEl : function(){
14119 return this.inputEl();
14123 getPositionEl : function(){
14124 return this.inputEl();
14128 alignErrorIcon : function(){
14129 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
14133 initEvents : function(){
14137 Roo.bootstrap.form.TriggerField.superclass.initEvents.call(this);
14138 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
14139 if(!this.multiple && this.showToggleBtn){
14140 this.trigger = this.el.select('span.dropdown-toggle',true).first();
14141 if(this.hideTrigger){
14142 this.trigger.setDisplayed(false);
14144 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
14148 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
14151 if(this.removable && !this.editable && !this.tickable){
14152 var close = this.closeTriggerEl();
14155 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14156 close.on('click', this.removeBtnClick, this, close);
14160 //this.trigger.addClassOnOver('x-form-trigger-over');
14161 //this.trigger.addClassOnClick('x-form-trigger-click');
14164 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
14168 closeTriggerEl : function()
14170 var close = this.el.select('.roo-combo-removable-btn', true).first();
14171 return close ? close : false;
14174 removeBtnClick : function(e, h, el)
14176 e.preventDefault();
14178 if(this.fireEvent("remove", this) !== false){
14180 this.fireEvent("afterremove", this)
14184 createList : function()
14186 this.list = Roo.get(document.body).createChild({
14187 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
14188 cls: 'typeahead typeahead-long dropdown-menu shadow',
14189 style: 'display:none'
14192 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
14197 initTrigger : function(){
14202 onDestroy : function(){
14204 this.trigger.removeAllListeners();
14205 // this.trigger.remove();
14208 // this.wrap.remove();
14210 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
14214 onFocus : function(){
14215 Roo.bootstrap.form.TriggerField.superclass.onFocus.call(this);
14217 if(!this.mimicing){
14218 this.wrap.addClass('x-trigger-wrap-focus');
14219 this.mimicing = true;
14220 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
14221 if(this.monitorTab){
14222 this.el.on("keydown", this.checkTab, this);
14229 checkTab : function(e){
14230 if(e.getKey() == e.TAB){
14231 this.triggerBlur();
14236 onBlur : function(){
14241 mimicBlur : function(e, t){
14243 if(!this.wrap.contains(t) && this.validateBlur()){
14244 this.triggerBlur();
14250 triggerBlur : function(){
14251 this.mimicing = false;
14252 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
14253 if(this.monitorTab){
14254 this.el.un("keydown", this.checkTab, this);
14256 //this.wrap.removeClass('x-trigger-wrap-focus');
14257 Roo.bootstrap.form.TriggerField.superclass.onBlur.call(this);
14261 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
14262 validateBlur : function(e, t){
14267 onDisable : function(){
14268 this.inputEl().dom.disabled = true;
14269 //Roo.bootstrap.form.TriggerField.superclass.onDisable.call(this);
14271 // this.wrap.addClass('x-item-disabled');
14276 onEnable : function(){
14277 this.inputEl().dom.disabled = false;
14278 //Roo.bootstrap.form.TriggerField.superclass.onEnable.call(this);
14280 // this.el.removeClass('x-item-disabled');
14285 onShow : function(){
14286 var ae = this.getActionEl();
14289 ae.dom.style.display = '';
14290 ae.dom.style.visibility = 'visible';
14296 onHide : function(){
14297 var ae = this.getActionEl();
14298 ae.dom.style.display = 'none';
14302 * The function that should handle the trigger's click event. This method does nothing by default until overridden
14303 * by an implementing function.
14305 * @param {EventObject} e
14307 onTriggerClick : Roo.emptyFn
14315 * @class Roo.bootstrap.form.CardUploader
14316 * @extends Roo.bootstrap.Button
14317 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
14318 * @cfg {Number} errorTimeout default 3000
14319 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
14320 * @cfg {Array} html The button text.
14324 * Create a new CardUploader
14325 * @param {Object} config The config object
14328 Roo.bootstrap.form.CardUploader = function(config){
14332 Roo.bootstrap.form.CardUploader.superclass.constructor.call(this, config);
14335 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
14343 * When a image is clicked on - and needs to display a slideshow or similar..
14344 * @param {Roo.bootstrap.Card} this
14345 * @param {Object} The image information data
14351 * When a the download link is clicked
14352 * @param {Roo.bootstrap.Card} this
14353 * @param {Object} The image information data contains
14360 Roo.extend(Roo.bootstrap.form.CardUploader, Roo.bootstrap.form.Input, {
14363 errorTimeout : 3000,
14367 fileCollection : false,
14370 getAutoCreate : function()
14374 cls :'form-group' ,
14379 //cls : 'input-group-addon',
14380 html : this.fieldLabel
14388 value : this.value,
14389 cls : 'd-none form-control'
14394 multiple : 'multiple',
14396 cls : 'd-none roo-card-upload-selector'
14400 cls : 'roo-card-uploader-button-container w-100 mb-2'
14403 cls : 'card-columns roo-card-uploader-container'
14413 getChildContainer : function() /// what children are added to.
14415 return this.containerEl;
14418 getButtonContainer : function() /// what children are added to.
14420 return this.el.select(".roo-card-uploader-button-container").first();
14423 initEvents : function()
14426 Roo.bootstrap.form.Input.prototype.initEvents.call(this);
14430 xns: Roo.bootstrap,
14433 container_method : 'getButtonContainer' ,
14434 html : this.html, // fix changable?
14437 'click' : function(btn, e) {
14446 this.urlAPI = (window.createObjectURL && window) ||
14447 (window.URL && URL.revokeObjectURL && URL) ||
14448 (window.webkitURL && webkitURL);
14453 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14455 this.selectorEl.on('change', this.onFileSelected, this);
14458 this.images.forEach(function(img) {
14461 this.images = false;
14463 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14469 onClick : function(e)
14471 e.preventDefault();
14473 this.selectorEl.dom.click();
14477 onFileSelected : function(e)
14479 e.preventDefault();
14481 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14485 Roo.each(this.selectorEl.dom.files, function(file){
14486 this.addFile(file);
14495 addFile : function(file)
14498 if(typeof(file) === 'string'){
14499 throw "Add file by name?"; // should not happen
14503 if(!file || !this.urlAPI){
14513 var url = _this.urlAPI.createObjectURL( file);
14516 id : Roo.bootstrap.form.CardUploader.ID--,
14517 is_uploaded : false,
14521 mimetype : file.type,
14529 * addCard - add an Attachment to the uploader
14530 * @param data - the data about the image to upload
14534 title : "Title of file",
14535 is_uploaded : false,
14536 src : "http://.....",
14537 srcfile : { the File upload object },
14538 mimetype : file.type,
14541 .. any other data...
14547 addCard : function (data)
14549 // hidden input element?
14550 // if the file is not an image...
14551 //then we need to use something other that and header_image
14556 xns : Roo.bootstrap,
14557 xtype : 'CardFooter',
14560 xns : Roo.bootstrap,
14566 xns : Roo.bootstrap,
14568 html : String.format("<small>{0}</small>", data.title),
14569 cls : 'col-10 text-left',
14574 click : function() {
14576 t.fireEvent( "download", t, data );
14582 xns : Roo.bootstrap,
14584 style: 'max-height: 28px; ',
14590 click : function() {
14591 t.removeCard(data.id)
14603 var cn = this.addxtype(
14606 xns : Roo.bootstrap,
14609 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14610 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
14611 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
14616 initEvents : function() {
14617 Roo.bootstrap.Card.prototype.initEvents.call(this);
14619 this.imgEl = this.el.select('.card-img-top').first();
14621 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14622 this.imgEl.set({ 'pointer' : 'cursor' });
14625 this.getCardFooter().addClass('p-1');
14632 // dont' really need ot update items.
14633 // this.items.push(cn);
14634 this.fileCollection.add(cn);
14636 if (!data.srcfile) {
14637 this.updateInput();
14642 var reader = new FileReader();
14643 reader.addEventListener("load", function() {
14644 data.srcdata = reader.result;
14647 reader.readAsDataURL(data.srcfile);
14652 removeCard : function(id)
14655 var card = this.fileCollection.get(id);
14656 card.data.is_deleted = 1;
14657 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14658 //this.fileCollection.remove(card);
14659 //this.items = this.items.filter(function(e) { return e != card });
14660 // dont' really need ot update items.
14661 card.el.dom.parentNode.removeChild(card.el.dom);
14662 this.updateInput();
14668 this.fileCollection.each(function(card) {
14669 if (card.el.dom && card.el.dom.parentNode) {
14670 card.el.dom.parentNode.removeChild(card.el.dom);
14673 this.fileCollection.clear();
14674 this.updateInput();
14677 updateInput : function()
14680 this.fileCollection.each(function(e) {
14684 this.inputEl().dom.value = JSON.stringify(data);
14694 Roo.bootstrap.form.CardUploader.ID = -1;/*
14696 * Ext JS Library 1.1.1
14697 * Copyright(c) 2006-2007, Ext JS, LLC.
14699 * Originally Released Under LGPL - original licence link has changed is not relivant.
14702 * <script type="text/javascript">
14707 * @class Roo.data.SortTypes
14709 * Defines the default sorting (casting?) comparison functions used when sorting data.
14711 Roo.data.SortTypes = {
14713 * Default sort that does nothing
14714 * @param {Mixed} s The value being converted
14715 * @return {Mixed} The comparison value
14717 none : function(s){
14722 * The regular expression used to strip tags
14726 stripTagsRE : /<\/?[^>]+>/gi,
14729 * Strips all HTML tags to sort on text only
14730 * @param {Mixed} s The value being converted
14731 * @return {String} The comparison value
14733 asText : function(s){
14734 return String(s).replace(this.stripTagsRE, "");
14738 * Strips all HTML tags to sort on text only - Case insensitive
14739 * @param {Mixed} s The value being converted
14740 * @return {String} The comparison value
14742 asUCText : function(s){
14743 return String(s).toUpperCase().replace(this.stripTagsRE, "");
14747 * Case insensitive string
14748 * @param {Mixed} s The value being converted
14749 * @return {String} The comparison value
14751 asUCString : function(s) {
14752 return String(s).toUpperCase();
14757 * @param {Mixed} s The value being converted
14758 * @return {Number} The comparison value
14760 asDate : function(s) {
14764 if(s instanceof Date){
14765 return s.getTime();
14767 return Date.parse(String(s));
14772 * @param {Mixed} s The value being converted
14773 * @return {Float} The comparison value
14775 asFloat : function(s) {
14776 var val = parseFloat(String(s).replace(/,/g, ""));
14785 * @param {Mixed} s The value being converted
14786 * @return {Number} The comparison value
14788 asInt : function(s) {
14789 var val = parseInt(String(s).replace(/,/g, ""));
14797 * Ext JS Library 1.1.1
14798 * Copyright(c) 2006-2007, Ext JS, LLC.
14800 * Originally Released Under LGPL - original licence link has changed is not relivant.
14803 * <script type="text/javascript">
14807 * @class Roo.data.Record
14808 * Instances of this class encapsulate both record <em>definition</em> information, and record
14809 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14810 * to access Records cached in an {@link Roo.data.Store} object.<br>
14812 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14813 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14816 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14818 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14819 * {@link #create}. The parameters are the same.
14820 * @param {Array} data An associative Array of data values keyed by the field name.
14821 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14822 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14823 * not specified an integer id is generated.
14825 Roo.data.Record = function(data, id){
14826 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14831 * Generate a constructor for a specific record layout.
14832 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14833 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14834 * Each field definition object may contain the following properties: <ul>
14835 * <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,
14836 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14837 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14838 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14839 * is being used, then this is a string containing the javascript expression to reference the data relative to
14840 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14841 * to the data item relative to the record element. If the mapping expression is the same as the field name,
14842 * this may be omitted.</p></li>
14843 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14844 * <ul><li>auto (Default, implies no conversion)</li>
14849 * <li>date</li></ul></p></li>
14850 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14851 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14852 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14853 * by the Reader into an object that will be stored in the Record. It is passed the
14854 * following parameters:<ul>
14855 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14857 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14859 * <br>usage:<br><pre><code>
14860 var TopicRecord = Roo.data.Record.create(
14861 {name: 'title', mapping: 'topic_title'},
14862 {name: 'author', mapping: 'username'},
14863 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14864 {name: 'lastPost', mapping: 'post_time', type: 'date'},
14865 {name: 'lastPoster', mapping: 'user2'},
14866 {name: 'excerpt', mapping: 'post_text'}
14869 var myNewRecord = new TopicRecord({
14870 title: 'Do my job please',
14873 lastPost: new Date(),
14874 lastPoster: 'Animal',
14875 excerpt: 'No way dude!'
14877 myStore.add(myNewRecord);
14882 Roo.data.Record.create = function(o){
14883 var f = function(){
14884 f.superclass.constructor.apply(this, arguments);
14886 Roo.extend(f, Roo.data.Record);
14887 var p = f.prototype;
14888 p.fields = new Roo.util.MixedCollection(false, function(field){
14891 for(var i = 0, len = o.length; i < len; i++){
14892 p.fields.add(new Roo.data.Field(o[i]));
14894 f.getField = function(name){
14895 return p.fields.get(name);
14900 Roo.data.Record.AUTO_ID = 1000;
14901 Roo.data.Record.EDIT = 'edit';
14902 Roo.data.Record.REJECT = 'reject';
14903 Roo.data.Record.COMMIT = 'commit';
14905 Roo.data.Record.prototype = {
14907 * Readonly flag - true if this record has been modified.
14916 join : function(store){
14917 this.store = store;
14921 * Set the named field to the specified value.
14922 * @param {String} name The name of the field to set.
14923 * @param {Object} value The value to set the field to.
14925 set : function(name, value){
14926 if(this.data[name] == value){
14930 if(!this.modified){
14931 this.modified = {};
14933 if(typeof this.modified[name] == 'undefined'){
14934 this.modified[name] = this.data[name];
14936 this.data[name] = value;
14937 if(!this.editing && this.store){
14938 this.store.afterEdit(this);
14943 * Get the value of the named field.
14944 * @param {String} name The name of the field to get the value of.
14945 * @return {Object} The value of the field.
14947 get : function(name){
14948 return this.data[name];
14952 beginEdit : function(){
14953 this.editing = true;
14954 this.modified = {};
14958 cancelEdit : function(){
14959 this.editing = false;
14960 delete this.modified;
14964 endEdit : function(){
14965 this.editing = false;
14966 if(this.dirty && this.store){
14967 this.store.afterEdit(this);
14972 * Usually called by the {@link Roo.data.Store} which owns the Record.
14973 * Rejects all changes made to the Record since either creation, or the last commit operation.
14974 * Modified fields are reverted to their original values.
14976 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14977 * of reject operations.
14979 reject : function(){
14980 var m = this.modified;
14982 if(typeof m[n] != "function"){
14983 this.data[n] = m[n];
14986 this.dirty = false;
14987 delete this.modified;
14988 this.editing = false;
14990 this.store.afterReject(this);
14995 * Usually called by the {@link Roo.data.Store} which owns the Record.
14996 * Commits all changes made to the Record since either creation, or the last commit operation.
14998 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14999 * of commit operations.
15001 commit : function(){
15002 this.dirty = false;
15003 delete this.modified;
15004 this.editing = false;
15006 this.store.afterCommit(this);
15011 hasError : function(){
15012 return this.error != null;
15016 clearError : function(){
15021 * Creates a copy of this record.
15022 * @param {String} id (optional) A new record id if you don't want to use this record's id
15025 copy : function(newId) {
15026 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
15030 * Ext JS Library 1.1.1
15031 * Copyright(c) 2006-2007, Ext JS, LLC.
15033 * Originally Released Under LGPL - original licence link has changed is not relivant.
15036 * <script type="text/javascript">
15042 * @class Roo.data.Store
15043 * @extends Roo.util.Observable
15044 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
15045 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
15047 * 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
15048 * has no knowledge of the format of the data returned by the Proxy.<br>
15050 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
15051 * instances from the data object. These records are cached and made available through accessor functions.
15053 * Creates a new Store.
15054 * @param {Object} config A config object containing the objects needed for the Store to access data,
15055 * and read the data into Records.
15057 Roo.data.Store = function(config){
15058 this.data = new Roo.util.MixedCollection(false);
15059 this.data.getKey = function(o){
15062 this.baseParams = {};
15064 this.paramNames = {
15069 "multisort" : "_multisort"
15072 if(config && config.data){
15073 this.inlineData = config.data;
15074 delete config.data;
15077 Roo.apply(this, config);
15079 if(this.reader){ // reader passed
15080 this.reader = Roo.factory(this.reader, Roo.data);
15081 this.reader.xmodule = this.xmodule || false;
15082 if(!this.recordType){
15083 this.recordType = this.reader.recordType;
15085 if(this.reader.onMetaChange){
15086 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
15090 if(this.recordType){
15091 this.fields = this.recordType.prototype.fields;
15093 this.modified = [];
15097 * @event datachanged
15098 * Fires when the data cache has changed, and a widget which is using this Store
15099 * as a Record cache should refresh its view.
15100 * @param {Store} this
15102 datachanged : true,
15104 * @event metachange
15105 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
15106 * @param {Store} this
15107 * @param {Object} meta The JSON metadata
15112 * Fires when Records have been added to the Store
15113 * @param {Store} this
15114 * @param {Roo.data.Record[]} records The array of Records added
15115 * @param {Number} index The index at which the record(s) were added
15120 * Fires when a Record has been removed from the Store
15121 * @param {Store} this
15122 * @param {Roo.data.Record} record The Record that was removed
15123 * @param {Number} index The index at which the record was removed
15128 * Fires when a Record has been updated
15129 * @param {Store} this
15130 * @param {Roo.data.Record} record The Record that was updated
15131 * @param {String} operation The update operation being performed. Value may be one of:
15133 Roo.data.Record.EDIT
15134 Roo.data.Record.REJECT
15135 Roo.data.Record.COMMIT
15141 * Fires when the data cache has been cleared.
15142 * @param {Store} this
15146 * @event beforeload
15147 * Fires before a request is made for a new data object. If the beforeload handler returns false
15148 * the load action will be canceled.
15149 * @param {Store} this
15150 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15154 * @event beforeloadadd
15155 * Fires after a new set of Records has been loaded.
15156 * @param {Store} this
15157 * @param {Roo.data.Record[]} records The Records that were loaded
15158 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15160 beforeloadadd : true,
15163 * Fires after a new set of Records has been loaded, before they are added to the store.
15164 * @param {Store} this
15165 * @param {Roo.data.Record[]} records The Records that were loaded
15166 * @param {Object} options The loading options that were specified (see {@link #load} for details)
15167 * @params {Object} return from reader
15171 * @event loadexception
15172 * Fires if an exception occurs in the Proxy during loading.
15173 * Called with the signature of the Proxy's "loadexception" event.
15174 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
15177 * @param {Object} return from JsonData.reader() - success, totalRecords, records
15178 * @param {Object} load options
15179 * @param {Object} jsonData from your request (normally this contains the Exception)
15181 loadexception : true
15185 this.proxy = Roo.factory(this.proxy, Roo.data);
15186 this.proxy.xmodule = this.xmodule || false;
15187 this.relayEvents(this.proxy, ["loadexception"]);
15189 this.sortToggle = {};
15190 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
15192 Roo.data.Store.superclass.constructor.call(this);
15194 if(this.inlineData){
15195 this.loadData(this.inlineData);
15196 delete this.inlineData;
15200 Roo.extend(Roo.data.Store, Roo.util.Observable, {
15202 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
15203 * without a remote query - used by combo/forms at present.
15207 * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
15210 * @cfg {Array} data Inline data to be loaded when the store is initialized.
15213 * @cfg {Roo.data.DataReader} reader [required] The Reader object which processes the data object and returns
15214 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
15217 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
15218 * on any HTTP request
15221 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
15224 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
15228 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
15229 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
15231 remoteSort : false,
15234 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
15235 * loaded or when a record is removed. (defaults to false).
15237 pruneModifiedRecords : false,
15240 lastOptions : null,
15243 * Add Records to the Store and fires the add event.
15244 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15246 add : function(records){
15247 records = [].concat(records);
15248 for(var i = 0, len = records.length; i < len; i++){
15249 records[i].join(this);
15251 var index = this.data.length;
15252 this.data.addAll(records);
15253 this.fireEvent("add", this, records, index);
15257 * Remove a Record from the Store and fires the remove event.
15258 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
15260 remove : function(record){
15261 var index = this.data.indexOf(record);
15262 this.data.removeAt(index);
15264 if(this.pruneModifiedRecords){
15265 this.modified.remove(record);
15267 this.fireEvent("remove", this, record, index);
15271 * Remove all Records from the Store and fires the clear event.
15273 removeAll : function(){
15275 if(this.pruneModifiedRecords){
15276 this.modified = [];
15278 this.fireEvent("clear", this);
15282 * Inserts Records to the Store at the given index and fires the add event.
15283 * @param {Number} index The start index at which to insert the passed Records.
15284 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
15286 insert : function(index, records){
15287 records = [].concat(records);
15288 for(var i = 0, len = records.length; i < len; i++){
15289 this.data.insert(index, records[i]);
15290 records[i].join(this);
15292 this.fireEvent("add", this, records, index);
15296 * Get the index within the cache of the passed Record.
15297 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
15298 * @return {Number} The index of the passed Record. Returns -1 if not found.
15300 indexOf : function(record){
15301 return this.data.indexOf(record);
15305 * Get the index within the cache of the Record with the passed id.
15306 * @param {String} id The id of the Record to find.
15307 * @return {Number} The index of the Record. Returns -1 if not found.
15309 indexOfId : function(id){
15310 return this.data.indexOfKey(id);
15314 * Get the Record with the specified id.
15315 * @param {String} id The id of the Record to find.
15316 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
15318 getById : function(id){
15319 return this.data.key(id);
15323 * Get the Record at the specified index.
15324 * @param {Number} index The index of the Record to find.
15325 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
15327 getAt : function(index){
15328 return this.data.itemAt(index);
15332 * Returns a range of Records between specified indices.
15333 * @param {Number} startIndex (optional) The starting index (defaults to 0)
15334 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
15335 * @return {Roo.data.Record[]} An array of Records
15337 getRange : function(start, end){
15338 return this.data.getRange(start, end);
15342 storeOptions : function(o){
15343 o = Roo.apply({}, o);
15346 this.lastOptions = o;
15350 * Loads the Record cache from the configured Proxy using the configured Reader.
15352 * If using remote paging, then the first load call must specify the <em>start</em>
15353 * and <em>limit</em> properties in the options.params property to establish the initial
15354 * position within the dataset, and the number of Records to cache on each read from the Proxy.
15356 * <strong>It is important to note that for remote data sources, loading is asynchronous,
15357 * and this call will return before the new data has been loaded. Perform any post-processing
15358 * in a callback function, or in a "load" event handler.</strong>
15360 * @param {Object} options An object containing properties which control loading options:<ul>
15361 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
15362 * <li>params.data {Object} if you are using a MemoryProxy / JsonReader, use this as the data to load stuff..
15365 data : data, // array of key=>value data like JsonReader
15366 total : data.length,
15372 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
15373 * passed the following arguments:<ul>
15374 * <li>r : Roo.data.Record[]</li>
15375 * <li>options: Options object from the load call</li>
15376 * <li>success: Boolean success indicator</li></ul></li>
15377 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
15378 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
15381 load : function(options){
15382 options = options || {};
15383 if(this.fireEvent("beforeload", this, options) !== false){
15384 this.storeOptions(options);
15385 var p = Roo.apply(options.params || {}, this.baseParams);
15386 // if meta was not loaded from remote source.. try requesting it.
15387 if (!this.reader.metaFromRemote) {
15388 p._requestMeta = 1;
15390 if(this.sortInfo && this.remoteSort){
15391 var pn = this.paramNames;
15392 p[pn["sort"]] = this.sortInfo.field;
15393 p[pn["dir"]] = this.sortInfo.direction;
15395 if (this.multiSort) {
15396 var pn = this.paramNames;
15397 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
15400 this.proxy.load(p, this.reader, this.loadRecords, this, options);
15405 * Reloads the Record cache from the configured Proxy using the configured Reader and
15406 * the options from the last load operation performed.
15407 * @param {Object} options (optional) An object containing properties which may override the options
15408 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
15409 * the most recently used options are reused).
15411 reload : function(options){
15412 this.load(Roo.applyIf(options||{}, this.lastOptions));
15416 // Called as a callback by the Reader during a load operation.
15417 loadRecords : function(o, options, success){
15420 if(success !== false){
15421 this.fireEvent("load", this, [], options, o);
15423 if(options.callback){
15424 options.callback.call(options.scope || this, [], options, false);
15428 // if data returned failure - throw an exception.
15429 if (o.success === false) {
15430 // show a message if no listener is registered.
15431 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
15432 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
15434 // loadmask wil be hooked into this..
15435 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
15438 var r = o.records, t = o.totalRecords || r.length;
15440 this.fireEvent("beforeloadadd", this, r, options, o);
15442 if(!options || options.add !== true){
15443 if(this.pruneModifiedRecords){
15444 this.modified = [];
15446 for(var i = 0, len = r.length; i < len; i++){
15450 this.data = this.snapshot;
15451 delete this.snapshot;
15454 this.data.addAll(r);
15455 this.totalLength = t;
15457 this.fireEvent("datachanged", this);
15459 this.totalLength = Math.max(t, this.data.length+r.length);
15463 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15465 var e = new Roo.data.Record({});
15467 e.set(this.parent.displayField, this.parent.emptyTitle);
15468 e.set(this.parent.valueField, '');
15473 this.fireEvent("load", this, r, options, o);
15474 if(options.callback){
15475 options.callback.call(options.scope || this, r, options, true);
15481 * Loads data from a passed data block. A Reader which understands the format of the data
15482 * must have been configured in the constructor.
15483 * @param {Object} data The data block from which to read the Records. The format of the data expected
15484 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15485 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15487 loadData : function(o, append){
15488 var r = this.reader.readRecords(o);
15489 this.loadRecords(r, {add: append}, true);
15493 * using 'cn' the nested child reader read the child array into it's child stores.
15494 * @param {Object} rec The record with a 'children array
15496 loadDataFromChildren : function(rec)
15498 this.loadData(this.reader.toLoadData(rec));
15503 * Gets the number of cached records.
15505 * <em>If using paging, this may not be the total size of the dataset. If the data object
15506 * used by the Reader contains the dataset size, then the getTotalCount() function returns
15507 * the data set size</em>
15509 getCount : function(){
15510 return this.data.length || 0;
15514 * Gets the total number of records in the dataset as returned by the server.
15516 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15517 * the dataset size</em>
15519 getTotalCount : function(){
15520 return this.totalLength || 0;
15524 * Returns the sort state of the Store as an object with two properties:
15526 field {String} The name of the field by which the Records are sorted
15527 direction {String} The sort order, "ASC" or "DESC"
15530 getSortState : function(){
15531 return this.sortInfo;
15535 applySort : function(){
15536 if(this.sortInfo && !this.remoteSort){
15537 var s = this.sortInfo, f = s.field;
15538 var st = this.fields.get(f).sortType;
15539 var fn = function(r1, r2){
15540 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15541 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15543 this.data.sort(s.direction, fn);
15544 if(this.snapshot && this.snapshot != this.data){
15545 this.snapshot.sort(s.direction, fn);
15551 * Sets the default sort column and order to be used by the next load operation.
15552 * @param {String} fieldName The name of the field to sort by.
15553 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15555 setDefaultSort : function(field, dir){
15556 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15560 * Sort the Records.
15561 * If remote sorting is used, the sort is performed on the server, and the cache is
15562 * reloaded. If local sorting is used, the cache is sorted internally.
15563 * @param {String} fieldName The name of the field to sort by.
15564 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15566 sort : function(fieldName, dir){
15567 var f = this.fields.get(fieldName);
15569 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15571 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15572 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15577 this.sortToggle[f.name] = dir;
15578 this.sortInfo = {field: f.name, direction: dir};
15579 if(!this.remoteSort){
15581 this.fireEvent("datachanged", this);
15583 this.load(this.lastOptions);
15588 * Calls the specified function for each of the Records in the cache.
15589 * @param {Function} fn The function to call. The Record is passed as the first parameter.
15590 * Returning <em>false</em> aborts and exits the iteration.
15591 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15593 each : function(fn, scope){
15594 this.data.each(fn, scope);
15598 * Gets all records modified since the last commit. Modified records are persisted across load operations
15599 * (e.g., during paging).
15600 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15602 getModifiedRecords : function(){
15603 return this.modified;
15607 createFilterFn : function(property, value, anyMatch){
15608 if(!value.exec){ // not a regex
15609 value = String(value);
15610 if(value.length == 0){
15613 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15615 return function(r){
15616 return value.test(r.data[property]);
15621 * Sums the value of <i>property</i> for each record between start and end and returns the result.
15622 * @param {String} property A field on your records
15623 * @param {Number} start The record index to start at (defaults to 0)
15624 * @param {Number} end The last record index to include (defaults to length - 1)
15625 * @return {Number} The sum
15627 sum : function(property, start, end){
15628 var rs = this.data.items, v = 0;
15629 start = start || 0;
15630 end = (end || end === 0) ? end : rs.length-1;
15632 for(var i = start; i <= end; i++){
15633 v += (rs[i].data[property] || 0);
15639 * Filter the records by a specified property.
15640 * @param {String} field A field on your records
15641 * @param {String/RegExp} value Either a string that the field
15642 * should start with or a RegExp to test against the field
15643 * @param {Boolean} anyMatch True to match any part not just the beginning
15645 filter : function(property, value, anyMatch){
15646 var fn = this.createFilterFn(property, value, anyMatch);
15647 return fn ? this.filterBy(fn) : this.clearFilter();
15651 * Filter by a function. The specified function will be called with each
15652 * record in this data source. If the function returns true the record is included,
15653 * otherwise it is filtered.
15654 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15655 * @param {Object} scope (optional) The scope of the function (defaults to this)
15657 filterBy : function(fn, scope){
15658 this.snapshot = this.snapshot || this.data;
15659 this.data = this.queryBy(fn, scope||this);
15660 this.fireEvent("datachanged", this);
15664 * Query the records by a specified property.
15665 * @param {String} field A field on your records
15666 * @param {String/RegExp} value Either a string that the field
15667 * should start with or a RegExp to test against the field
15668 * @param {Boolean} anyMatch True to match any part not just the beginning
15669 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15671 query : function(property, value, anyMatch){
15672 var fn = this.createFilterFn(property, value, anyMatch);
15673 return fn ? this.queryBy(fn) : this.data.clone();
15677 * Query by a function. The specified function will be called with each
15678 * record in this data source. If the function returns true the record is included
15680 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15681 * @param {Object} scope (optional) The scope of the function (defaults to this)
15682 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15684 queryBy : function(fn, scope){
15685 var data = this.snapshot || this.data;
15686 return data.filterBy(fn, scope||this);
15690 * Collects unique values for a particular dataIndex from this store.
15691 * @param {String} dataIndex The property to collect
15692 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15693 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15694 * @return {Array} An array of the unique values
15696 collect : function(dataIndex, allowNull, bypassFilter){
15697 var d = (bypassFilter === true && this.snapshot) ?
15698 this.snapshot.items : this.data.items;
15699 var v, sv, r = [], l = {};
15700 for(var i = 0, len = d.length; i < len; i++){
15701 v = d[i].data[dataIndex];
15703 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15712 * Revert to a view of the Record cache with no filtering applied.
15713 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15715 clearFilter : function(suppressEvent){
15716 if(this.snapshot && this.snapshot != this.data){
15717 this.data = this.snapshot;
15718 delete this.snapshot;
15719 if(suppressEvent !== true){
15720 this.fireEvent("datachanged", this);
15726 afterEdit : function(record){
15727 if(this.modified.indexOf(record) == -1){
15728 this.modified.push(record);
15730 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15734 afterReject : function(record){
15735 this.modified.remove(record);
15736 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15740 afterCommit : function(record){
15741 this.modified.remove(record);
15742 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15746 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15747 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15749 commitChanges : function(){
15750 var m = this.modified.slice(0);
15751 this.modified = [];
15752 for(var i = 0, len = m.length; i < len; i++){
15758 * Cancel outstanding changes on all changed records.
15760 rejectChanges : function(){
15761 var m = this.modified.slice(0);
15762 this.modified = [];
15763 for(var i = 0, len = m.length; i < len; i++){
15768 onMetaChange : function(meta, rtype, o){
15769 this.recordType = rtype;
15770 this.fields = rtype.prototype.fields;
15771 delete this.snapshot;
15772 this.sortInfo = meta.sortInfo || this.sortInfo;
15773 this.modified = [];
15774 this.fireEvent('metachange', this, this.reader.meta);
15777 moveIndex : function(data, type)
15779 var index = this.indexOf(data);
15781 var newIndex = index + type;
15785 this.insert(newIndex, data);
15790 * Ext JS Library 1.1.1
15791 * Copyright(c) 2006-2007, Ext JS, LLC.
15793 * Originally Released Under LGPL - original licence link has changed is not relivant.
15796 * <script type="text/javascript">
15800 * @class Roo.data.SimpleStore
15801 * @extends Roo.data.Store
15802 * Small helper class to make creating Stores from Array data easier.
15803 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15804 * @cfg {Array} fields An array of field definition objects, or field name strings.
15805 * @cfg {Object} an existing reader (eg. copied from another store)
15806 * @cfg {Array} data The multi-dimensional array of data
15807 * @cfg {Roo.data.DataProxy} proxy [not-required]
15808 * @cfg {Roo.data.Reader} reader [not-required]
15810 * @param {Object} config
15812 Roo.data.SimpleStore = function(config)
15814 Roo.data.SimpleStore.superclass.constructor.call(this, {
15816 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15819 Roo.data.Record.create(config.fields)
15821 proxy : new Roo.data.MemoryProxy(config.data)
15825 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15827 * Ext JS Library 1.1.1
15828 * Copyright(c) 2006-2007, Ext JS, LLC.
15830 * Originally Released Under LGPL - original licence link has changed is not relivant.
15833 * <script type="text/javascript">
15838 * @extends Roo.data.Store
15839 * @class Roo.data.JsonStore
15840 * Small helper class to make creating Stores for JSON data easier. <br/>
15842 var store = new Roo.data.JsonStore({
15843 url: 'get-images.php',
15845 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15848 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15849 * JsonReader and HttpProxy (unless inline data is provided).</b>
15850 * @cfg {Array} fields An array of field definition objects, or field name strings.
15852 * @param {Object} config
15854 Roo.data.JsonStore = function(c){
15855 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15856 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15857 reader: new Roo.data.JsonReader(c, c.fields)
15860 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15862 * Ext JS Library 1.1.1
15863 * Copyright(c) 2006-2007, Ext JS, LLC.
15865 * Originally Released Under LGPL - original licence link has changed is not relivant.
15868 * <script type="text/javascript">
15872 Roo.data.Field = function(config){
15873 if(typeof config == "string"){
15874 config = {name: config};
15876 Roo.apply(this, config);
15879 this.type = "auto";
15882 var st = Roo.data.SortTypes;
15883 // named sortTypes are supported, here we look them up
15884 if(typeof this.sortType == "string"){
15885 this.sortType = st[this.sortType];
15888 // set default sortType for strings and dates
15889 if(!this.sortType){
15892 this.sortType = st.asUCString;
15895 this.sortType = st.asDate;
15898 this.sortType = st.none;
15903 var stripRe = /[\$,%]/g;
15905 // prebuilt conversion function for this field, instead of
15906 // switching every time we're reading a value
15908 var cv, dateFormat = this.dateFormat;
15913 cv = function(v){ return v; };
15916 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15920 return v !== undefined && v !== null && v !== '' ?
15921 parseInt(String(v).replace(stripRe, ""), 10) : '';
15926 return v !== undefined && v !== null && v !== '' ?
15927 parseFloat(String(v).replace(stripRe, ""), 10) : '';
15932 cv = function(v){ return v === true || v === "true" || v == 1; };
15939 if(v instanceof Date){
15943 if(dateFormat == "timestamp"){
15944 return new Date(v*1000);
15946 return Date.parseDate(v, dateFormat);
15948 var parsed = Date.parse(v);
15949 return parsed ? new Date(parsed) : null;
15958 Roo.data.Field.prototype = {
15966 * Ext JS Library 1.1.1
15967 * Copyright(c) 2006-2007, Ext JS, LLC.
15969 * Originally Released Under LGPL - original licence link has changed is not relivant.
15972 * <script type="text/javascript">
15975 // Base class for reading structured data from a data source. This class is intended to be
15976 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15979 * @class Roo.data.DataReader
15981 * Base class for reading structured data from a data source. This class is intended to be
15982 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15985 Roo.data.DataReader = function(meta, recordType){
15989 this.recordType = recordType instanceof Array ?
15990 Roo.data.Record.create(recordType) : recordType;
15993 Roo.data.DataReader.prototype = {
15996 readerType : 'Data',
15998 * Create an empty record
15999 * @param {Object} data (optional) - overlay some values
16000 * @return {Roo.data.Record} record created.
16002 newRow : function(d) {
16004 this.recordType.prototype.fields.each(function(c) {
16006 case 'int' : da[c.name] = 0; break;
16007 case 'date' : da[c.name] = new Date(); break;
16008 case 'float' : da[c.name] = 0.0; break;
16009 case 'boolean' : da[c.name] = false; break;
16010 default : da[c.name] = ""; break;
16014 return new this.recordType(Roo.apply(da, d));
16020 * Ext JS Library 1.1.1
16021 * Copyright(c) 2006-2007, Ext JS, LLC.
16023 * Originally Released Under LGPL - original licence link has changed is not relivant.
16026 * <script type="text/javascript">
16030 * @class Roo.data.DataProxy
16031 * @extends Roo.util.Observable
16033 * This class is an abstract base class for implementations which provide retrieval of
16034 * unformatted data objects.<br>
16036 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
16037 * (of the appropriate type which knows how to parse the data object) to provide a block of
16038 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
16040 * Custom implementations must implement the load method as described in
16041 * {@link Roo.data.HttpProxy#load}.
16043 Roo.data.DataProxy = function(){
16046 * @event beforeload
16047 * Fires before a network request is made to retrieve a data object.
16048 * @param {Object} This DataProxy object.
16049 * @param {Object} params The params parameter to the load function.
16054 * Fires before the load method's callback is called.
16055 * @param {Object} This DataProxy object.
16056 * @param {Object} o The data object.
16057 * @param {Object} arg The callback argument object passed to the load function.
16061 * @event loadexception
16062 * Fires if an Exception occurs during data retrieval.
16063 * @param {Object} This DataProxy object.
16064 * @param {Object} o The data object.
16065 * @param {Object} arg The callback argument object passed to the load function.
16066 * @param {Object} e The Exception.
16068 loadexception : true
16070 Roo.data.DataProxy.superclass.constructor.call(this);
16073 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
16076 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
16080 * Ext JS Library 1.1.1
16081 * Copyright(c) 2006-2007, Ext JS, LLC.
16083 * Originally Released Under LGPL - original licence link has changed is not relivant.
16086 * <script type="text/javascript">
16089 * @class Roo.data.MemoryProxy
16090 * @extends Roo.data.DataProxy
16091 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
16092 * to the Reader when its load method is called.
16094 * @param {Object} config A config object containing the objects needed for the Store to access data,
16096 Roo.data.MemoryProxy = function(config){
16098 if (typeof(config) != 'undefined' && typeof(config.data) != 'undefined') {
16099 data = config.data;
16101 Roo.data.MemoryProxy.superclass.constructor.call(this);
16105 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
16108 * @cfg {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
16111 * Load data from the requested source (in this case an in-memory
16112 * data object passed to the constructor), read the data object into
16113 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16114 * process that block using the passed callback.
16115 * @param {Object} params This parameter is not used by the MemoryProxy class.
16116 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16117 * object into a block of Roo.data.Records.
16118 * @param {Function} callback The function into which to pass the block of Roo.data.records.
16119 * The function must be passed <ul>
16120 * <li>The Record block object</li>
16121 * <li>The "arg" argument from the load function</li>
16122 * <li>A boolean success indicator</li>
16124 * @param {Object} scope The scope in which to call the callback
16125 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16127 load : function(params, reader, callback, scope, arg){
16128 params = params || {};
16131 result = reader.readRecords(params.data ? params.data :this.data);
16133 this.fireEvent("loadexception", this, arg, null, e);
16134 callback.call(scope, null, arg, false);
16137 callback.call(scope, result, arg, true);
16141 update : function(params, records){
16146 * Ext JS Library 1.1.1
16147 * Copyright(c) 2006-2007, Ext JS, LLC.
16149 * Originally Released Under LGPL - original licence link has changed is not relivant.
16152 * <script type="text/javascript">
16155 * @class Roo.data.HttpProxy
16156 * @extends Roo.data.DataProxy
16157 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
16158 * configured to reference a certain URL.<br><br>
16160 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
16161 * from which the running page was served.<br><br>
16163 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
16165 * Be aware that to enable the browser to parse an XML document, the server must set
16166 * the Content-Type header in the HTTP response to "text/xml".
16168 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
16169 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
16170 * will be used to make the request.
16172 Roo.data.HttpProxy = function(conn){
16173 Roo.data.HttpProxy.superclass.constructor.call(this);
16174 // is conn a conn config or a real conn?
16176 this.useAjax = !conn || !conn.events;
16180 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
16181 // thse are take from connection...
16184 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
16187 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
16188 * extra parameters to each request made by this object. (defaults to undefined)
16191 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
16192 * to each request made by this object. (defaults to undefined)
16195 * @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)
16198 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
16201 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
16207 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
16211 * Return the {@link Roo.data.Connection} object being used by this Proxy.
16212 * @return {Connection} The Connection object. This object may be used to subscribe to events on
16213 * a finer-grained basis than the DataProxy events.
16215 getConnection : function(){
16216 return this.useAjax ? Roo.Ajax : this.conn;
16220 * Load data from the configured {@link Roo.data.Connection}, read the data object into
16221 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
16222 * process that block using the passed callback.
16223 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16224 * for the request to the remote server.
16225 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16226 * object into a block of Roo.data.Records.
16227 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16228 * The function must be passed <ul>
16229 * <li>The Record block object</li>
16230 * <li>The "arg" argument from the load function</li>
16231 * <li>A boolean success indicator</li>
16233 * @param {Object} scope The scope in which to call the callback
16234 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16236 load : function(params, reader, callback, scope, arg){
16237 if(this.fireEvent("beforeload", this, params) !== false){
16239 params : params || {},
16241 callback : callback,
16246 callback : this.loadResponse,
16250 Roo.applyIf(o, this.conn);
16251 if(this.activeRequest){
16252 Roo.Ajax.abort(this.activeRequest);
16254 this.activeRequest = Roo.Ajax.request(o);
16256 this.conn.request(o);
16259 callback.call(scope||this, null, arg, false);
16264 loadResponse : function(o, success, response){
16265 delete this.activeRequest;
16267 this.fireEvent("loadexception", this, o, response);
16268 o.request.callback.call(o.request.scope, null, o.request.arg, false);
16273 result = o.reader.read(response);
16276 o.raw = { errorMsg : response.responseText };
16277 this.fireEvent("loadexception", this, o, response, e);
16278 o.request.callback.call(o.request.scope, o, o.request.arg, false);
16282 this.fireEvent("load", this, o, o.request.arg);
16283 o.request.callback.call(o.request.scope, result, o.request.arg, true);
16287 update : function(dataSet){
16292 updateResponse : function(dataSet){
16297 * Ext JS Library 1.1.1
16298 * Copyright(c) 2006-2007, Ext JS, LLC.
16300 * Originally Released Under LGPL - original licence link has changed is not relivant.
16303 * <script type="text/javascript">
16307 * @class Roo.data.ScriptTagProxy
16308 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
16309 * other than the originating domain of the running page.<br><br>
16311 * <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
16312 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
16314 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
16315 * source code that is used as the source inside a <script> tag.<br><br>
16317 * In order for the browser to process the returned data, the server must wrap the data object
16318 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
16319 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
16320 * depending on whether the callback name was passed:
16323 boolean scriptTag = false;
16324 String cb = request.getParameter("callback");
16327 response.setContentType("text/javascript");
16329 response.setContentType("application/x-json");
16331 Writer out = response.getWriter();
16333 out.write(cb + "(");
16335 out.print(dataBlock.toJsonString());
16342 * @param {Object} config A configuration object.
16344 Roo.data.ScriptTagProxy = function(config){
16345 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
16346 Roo.apply(this, config);
16347 this.head = document.getElementsByTagName("head")[0];
16350 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
16352 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
16354 * @cfg {String} url The URL from which to request the data object.
16357 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
16361 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
16362 * the server the name of the callback function set up by the load call to process the returned data object.
16363 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
16364 * javascript output which calls this named function passing the data object as its only parameter.
16366 callbackParam : "callback",
16368 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
16369 * name to the request.
16374 * Load data from the configured URL, read the data object into
16375 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
16376 * process that block using the passed callback.
16377 * @param {Object} params An object containing properties which are to be used as HTTP parameters
16378 * for the request to the remote server.
16379 * @param {Roo.data.DataReader} reader The Reader object which converts the data
16380 * object into a block of Roo.data.Records.
16381 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
16382 * The function must be passed <ul>
16383 * <li>The Record block object</li>
16384 * <li>The "arg" argument from the load function</li>
16385 * <li>A boolean success indicator</li>
16387 * @param {Object} scope The scope in which to call the callback
16388 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
16390 load : function(params, reader, callback, scope, arg){
16391 if(this.fireEvent("beforeload", this, params) !== false){
16393 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
16395 var url = this.url;
16396 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
16398 url += "&_dc=" + (new Date().getTime());
16400 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
16403 cb : "stcCallback"+transId,
16404 scriptId : "stcScript"+transId,
16408 callback : callback,
16414 window[trans.cb] = function(o){
16415 conn.handleResponse(o, trans);
16418 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
16420 if(this.autoAbort !== false){
16424 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
16426 var script = document.createElement("script");
16427 script.setAttribute("src", url);
16428 script.setAttribute("type", "text/javascript");
16429 script.setAttribute("id", trans.scriptId);
16430 this.head.appendChild(script);
16432 this.trans = trans;
16434 callback.call(scope||this, null, arg, false);
16439 isLoading : function(){
16440 return this.trans ? true : false;
16444 * Abort the current server request.
16446 abort : function(){
16447 if(this.isLoading()){
16448 this.destroyTrans(this.trans);
16453 destroyTrans : function(trans, isLoaded){
16454 this.head.removeChild(document.getElementById(trans.scriptId));
16455 clearTimeout(trans.timeoutId);
16457 window[trans.cb] = undefined;
16459 delete window[trans.cb];
16462 // if hasn't been loaded, wait for load to remove it to prevent script error
16463 window[trans.cb] = function(){
16464 window[trans.cb] = undefined;
16466 delete window[trans.cb];
16473 handleResponse : function(o, trans){
16474 this.trans = false;
16475 this.destroyTrans(trans, true);
16478 result = trans.reader.readRecords(o);
16480 this.fireEvent("loadexception", this, o, trans.arg, e);
16481 trans.callback.call(trans.scope||window, null, trans.arg, false);
16484 this.fireEvent("load", this, o, trans.arg);
16485 trans.callback.call(trans.scope||window, result, trans.arg, true);
16489 handleFailure : function(trans){
16490 this.trans = false;
16491 this.destroyTrans(trans, false);
16492 this.fireEvent("loadexception", this, null, trans.arg);
16493 trans.callback.call(trans.scope||window, null, trans.arg, false);
16497 * Ext JS Library 1.1.1
16498 * Copyright(c) 2006-2007, Ext JS, LLC.
16500 * Originally Released Under LGPL - original licence link has changed is not relivant.
16503 * <script type="text/javascript">
16507 * @class Roo.data.JsonReader
16508 * @extends Roo.data.DataReader
16509 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16510 * based on mappings in a provided Roo.data.Record constructor.
16512 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16513 * in the reply previously.
16518 var RecordDef = Roo.data.Record.create([
16519 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
16520 {name: 'occupation'} // This field will use "occupation" as the mapping.
16522 var myReader = new Roo.data.JsonReader({
16523 totalProperty: "results", // The property which contains the total dataset size (optional)
16524 root: "rows", // The property which contains an Array of row objects
16525 id: "id" // The property within each row object that provides an ID for the record (optional)
16529 * This would consume a JSON file like this:
16531 { 'results': 2, 'rows': [
16532 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16533 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16536 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16537 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16538 * paged from the remote server.
16539 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16540 * @cfg {String} root name of the property which contains the Array of row objects.
16541 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16542 * @cfg {Array} fields Array of field definition objects
16544 * Create a new JsonReader
16545 * @param {Object} meta Metadata configuration options
16546 * @param {Object} recordType Either an Array of field definition objects,
16547 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16549 Roo.data.JsonReader = function(meta, recordType){
16552 // set some defaults:
16553 Roo.applyIf(meta, {
16554 totalProperty: 'total',
16555 successProperty : 'success',
16560 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16562 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16564 readerType : 'Json',
16567 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
16568 * Used by Store query builder to append _requestMeta to params.
16571 metaFromRemote : false,
16573 * This method is only used by a DataProxy which has retrieved data from a remote server.
16574 * @param {Object} response The XHR object which contains the JSON data in its responseText.
16575 * @return {Object} data A data block which is used by an Roo.data.Store object as
16576 * a cache of Roo.data.Records.
16578 read : function(response){
16579 var json = response.responseText;
16581 var o = /* eval:var:o */ eval("("+json+")");
16583 throw {message: "JsonReader.read: Json object not found"};
16589 this.metaFromRemote = true;
16590 this.meta = o.metaData;
16591 this.recordType = Roo.data.Record.create(o.metaData.fields);
16592 this.onMetaChange(this.meta, this.recordType, o);
16594 return this.readRecords(o);
16597 // private function a store will implement
16598 onMetaChange : function(meta, recordType, o){
16605 simpleAccess: function(obj, subsc) {
16612 getJsonAccessor: function(){
16614 return function(expr) {
16616 return(re.test(expr))
16617 ? new Function("obj", "return obj." + expr)
16622 return Roo.emptyFn;
16627 * Create a data block containing Roo.data.Records from an XML document.
16628 * @param {Object} o An object which contains an Array of row objects in the property specified
16629 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16630 * which contains the total size of the dataset.
16631 * @return {Object} data A data block which is used by an Roo.data.Store object as
16632 * a cache of Roo.data.Records.
16634 readRecords : function(o){
16636 * After any data loads, the raw JSON data is available for further custom processing.
16640 var s = this.meta, Record = this.recordType,
16641 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16643 // Generate extraction functions for the totalProperty, the root, the id, and for each field
16645 if(s.totalProperty) {
16646 this.getTotal = this.getJsonAccessor(s.totalProperty);
16648 if(s.successProperty) {
16649 this.getSuccess = this.getJsonAccessor(s.successProperty);
16651 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16653 var g = this.getJsonAccessor(s.id);
16654 this.getId = function(rec) {
16656 return (r === undefined || r === "") ? null : r;
16659 this.getId = function(){return null;};
16662 for(var jj = 0; jj < fl; jj++){
16664 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16665 this.ef[jj] = this.getJsonAccessor(map);
16669 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16670 if(s.totalProperty){
16671 var vt = parseInt(this.getTotal(o), 10);
16676 if(s.successProperty){
16677 var vs = this.getSuccess(o);
16678 if(vs === false || vs === 'false'){
16683 for(var i = 0; i < c; i++){
16686 var id = this.getId(n);
16687 for(var j = 0; j < fl; j++){
16689 var v = this.ef[j](n);
16691 Roo.log('missing convert for ' + f.name);
16695 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16699 raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
16705 var record = new Record(values, id);
16707 records[i] = record;
16713 totalRecords : totalRecords
16716 // used when loading children.. @see loadDataFromChildren
16717 toLoadData: function(rec)
16719 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16720 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16721 return { data : data, total : data.length };
16726 * Ext JS Library 1.1.1
16727 * Copyright(c) 2006-2007, Ext JS, LLC.
16729 * Originally Released Under LGPL - original licence link has changed is not relivant.
16732 * <script type="text/javascript">
16736 * @class Roo.data.ArrayReader
16737 * @extends Roo.data.DataReader
16738 * Data reader class to create an Array of Roo.data.Record objects from an Array.
16739 * Each element of that Array represents a row of data fields. The
16740 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16741 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16745 var RecordDef = Roo.data.Record.create([
16746 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
16747 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
16749 var myReader = new Roo.data.ArrayReader({
16750 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
16754 * This would consume an Array like this:
16756 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16760 * Create a new JsonReader
16761 * @param {Object} meta Metadata configuration options.
16762 * @param {Object|Array} recordType Either an Array of field definition objects
16764 * @cfg {Array} fields Array of field definition objects
16765 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16766 * as specified to {@link Roo.data.Record#create},
16767 * or an {@link Roo.data.Record} object
16770 * created using {@link Roo.data.Record#create}.
16772 Roo.data.ArrayReader = function(meta, recordType)
16774 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16777 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16780 * Create a data block containing Roo.data.Records from an XML document.
16781 * @param {Object} o An Array of row objects which represents the dataset.
16782 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16783 * a cache of Roo.data.Records.
16785 readRecords : function(o)
16787 var sid = this.meta ? this.meta.id : null;
16788 var recordType = this.recordType, fields = recordType.prototype.fields;
16791 for(var i = 0; i < root.length; i++){
16794 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16795 for(var j = 0, jlen = fields.length; j < jlen; j++){
16796 var f = fields.items[j];
16797 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16798 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16800 values[f.name] = v;
16802 var record = new recordType(values, id);
16804 records[records.length] = record;
16808 totalRecords : records.length
16811 // used when loading children.. @see loadDataFromChildren
16812 toLoadData: function(rec)
16814 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16815 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16826 * @class Roo.bootstrap.form.ComboBox
16827 * @extends Roo.bootstrap.form.TriggerField
16828 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16829 * @cfg {Boolean} append (true|false) default false
16830 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16831 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16832 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16833 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16834 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16835 * @cfg {Boolean} animate default true
16836 * @cfg {Boolean} emptyResultText only for touch device
16837 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16838 * @cfg {String} emptyTitle default ''
16839 * @cfg {Number} width fixed with? experimental
16841 * Create a new ComboBox.
16842 * @param {Object} config Configuration options
16844 Roo.bootstrap.form.ComboBox = function(config){
16845 Roo.bootstrap.form.ComboBox.superclass.constructor.call(this, config);
16849 * Fires when the dropdown list is expanded
16850 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16855 * Fires when the dropdown list is collapsed
16856 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16860 * @event beforeselect
16861 * Fires before a list item is selected. Return false to cancel the selection.
16862 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16863 * @param {Roo.data.Record} record The data record returned from the underlying store
16864 * @param {Number} index The index of the selected item in the dropdown list
16866 'beforeselect' : true,
16869 * Fires when a list item is selected
16870 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16871 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16872 * @param {Number} index The index of the selected item in the dropdown list
16876 * @event beforequery
16877 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16878 * The event object passed has these properties:
16879 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16880 * @param {String} query The query
16881 * @param {Boolean} forceAll true to force "all" query
16882 * @param {Boolean} cancel true to cancel the query
16883 * @param {Object} e The query event object
16885 'beforequery': true,
16888 * Fires when the 'add' icon is pressed (add a listener to enable add button)
16889 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16894 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16895 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16896 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16901 * Fires when the remove value from the combobox array
16902 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16906 * @event afterremove
16907 * Fires when the remove value from the combobox array
16908 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16910 'afterremove' : true,
16912 * @event specialfilter
16913 * Fires when specialfilter
16914 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16916 'specialfilter' : true,
16919 * Fires when tick the element
16920 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16924 * @event touchviewdisplay
16925 * Fires when touch view require special display (default is using displayField)
16926 * @param {Roo.bootstrap.form.ComboBox} combo This combo box
16927 * @param {Object} cfg set html .
16929 'touchviewdisplay' : true
16934 this.tickItems = [];
16936 this.selectedIndex = -1;
16937 if(this.mode == 'local'){
16938 if(config.queryDelay === undefined){
16939 this.queryDelay = 10;
16941 if(config.minChars === undefined){
16947 Roo.extend(Roo.bootstrap.form.ComboBox, Roo.bootstrap.form.TriggerField, {
16950 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16951 * rendering into an Roo.Editor, defaults to false)
16954 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16955 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16958 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16961 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16962 * the dropdown list (defaults to undefined, with no header element)
16966 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
16970 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16972 listWidth: undefined,
16974 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16975 * mode = 'remote' or 'text' if mode = 'local')
16977 displayField: undefined,
16980 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16981 * mode = 'remote' or 'value' if mode = 'local').
16982 * Note: use of a valueField requires the user make a selection
16983 * in order for a value to be mapped.
16985 valueField: undefined,
16987 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16992 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16993 * field's data value (defaults to the underlying DOM element's name)
16995 hiddenName: undefined,
16997 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
17001 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
17003 selectedClass: 'active',
17006 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
17010 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
17011 * anchor positions (defaults to 'tl-bl')
17013 listAlign: 'tl-bl?',
17015 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
17019 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
17020 * query specified by the allQuery config option (defaults to 'query')
17022 triggerAction: 'query',
17024 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
17025 * (defaults to 4, does not apply if editable = false)
17029 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
17030 * delay (typeAheadDelay) if it matches a known value (defaults to false)
17034 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
17035 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
17039 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
17040 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
17044 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
17045 * when editable = true (defaults to false)
17047 selectOnFocus:false,
17049 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
17051 queryParam: 'query',
17053 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
17054 * when mode = 'remote' (defaults to 'Loading...')
17056 loadingText: 'Loading...',
17058 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
17062 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
17066 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
17067 * traditional select (defaults to true)
17071 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
17075 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
17079 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
17080 * listWidth has a higher value)
17084 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
17085 * allow the user to set arbitrary text into the field (defaults to false)
17087 forceSelection:false,
17089 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
17090 * if typeAhead = true (defaults to 250)
17092 typeAheadDelay : 250,
17094 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
17095 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
17097 valueNotFoundText : undefined,
17099 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
17101 blockFocus : false,
17104 * @cfg {Boolean} disableClear Disable showing of clear button.
17106 disableClear : false,
17108 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
17110 alwaysQuery : false,
17113 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
17118 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
17120 invalidClass : "has-warning",
17123 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
17125 validClass : "has-success",
17128 * @cfg {Boolean} specialFilter (true|false) special filter default false
17130 specialFilter : false,
17133 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
17135 mobileTouchView : true,
17138 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
17140 useNativeIOS : false,
17143 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
17145 mobile_restrict_height : false,
17147 ios_options : false,
17159 btnPosition : 'right',
17160 triggerList : true,
17161 showToggleBtn : true,
17163 emptyResultText: 'Empty',
17164 triggerText : 'Select',
17168 // element that contains real text value.. (when hidden is used..)
17170 getAutoCreate : function()
17175 * Render classic select for iso
17178 if(Roo.isIOS && this.useNativeIOS){
17179 cfg = this.getAutoCreateNativeIOS();
17187 if(Roo.isTouch && this.mobileTouchView){
17188 cfg = this.getAutoCreateTouchView();
17195 if(!this.tickable){
17196 cfg = Roo.bootstrap.form.ComboBox.superclass.getAutoCreate.call(this);
17201 * ComboBox with tickable selections
17204 var align = this.labelAlign || this.parentLabelAlign();
17207 cls : 'form-group roo-combobox-tickable' //input-group
17210 var btn_text_select = '';
17211 var btn_text_done = '';
17212 var btn_text_cancel = '';
17214 if (this.btn_text_show) {
17215 btn_text_select = 'Select';
17216 btn_text_done = 'Done';
17217 btn_text_cancel = 'Cancel';
17222 cls : 'tickable-buttons',
17227 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
17228 //html : this.triggerText
17229 html: btn_text_select
17235 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
17237 html: btn_text_done
17243 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
17245 html: btn_text_cancel
17251 buttons.cn.unshift({
17253 cls: 'roo-select2-search-field-input'
17259 Roo.each(buttons.cn, function(c){
17261 c.cls += ' btn-' + _this.size;
17264 if (_this.disabled) {
17271 style : 'display: contents',
17276 cls: 'form-hidden-field'
17280 cls: 'roo-select2-choices',
17284 cls: 'roo-select2-search-field',
17295 cls: 'roo-select2-container input-group roo-select2-container-multi',
17301 // cls: 'typeahead typeahead-long dropdown-menu',
17302 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
17307 if(this.hasFeedback && !this.allowBlank){
17311 cls: 'glyphicon form-control-feedback'
17314 combobox.cn.push(feedback);
17321 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
17322 tooltip : 'This field is required'
17324 if (Roo.bootstrap.version == 4) {
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
23942 * Create a new TimeField
23943 * @param {Object} config The config object
23946 Roo.bootstrap.form.TimeField = function(config){
23947 Roo.bootstrap.form.TimeField.superclass.constructor.call(this, config);
23951 * Fires when this field show.
23952 * @param {Roo.bootstrap.form.DateField} thisthis
23953 * @param {Mixed} date The date value
23958 * Fires when this field hide.
23959 * @param {Roo.bootstrap.form.DateField} this
23960 * @param {Mixed} date The date value
23965 * Fires when select a date.
23966 * @param {Roo.bootstrap.form.DateField} this
23967 * @param {Mixed} date The date value
23973 Roo.extend(Roo.bootstrap.form.TimeField, Roo.bootstrap.form.Input, {
23976 * @cfg {String} format
23977 * The default time format string which can be overriden for localization support. The format must be
23978 * valid according to {@link Date#parseDate} (defaults to 'H:i').
23982 getAutoCreate : function()
23984 this.after = '<i class="fa far fa-clock"></i>';
23985 return Roo.bootstrap.form.TimeField.superclass.getAutoCreate.call(this);
23989 onRender: function(ct, position)
23992 Roo.bootstrap.form.TimeField.superclass.onRender.call(this, ct, position);
23994 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.TimeField.template);
23996 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23998 this.pop = this.picker().select('>.datepicker-time',true).first();
23999 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24001 this.picker().on('mousedown', this.onMousedown, this);
24002 this.picker().on('click', this.onClick, this);
24004 this.picker().addClass('datepicker-dropdown');
24009 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
24010 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
24011 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
24012 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
24013 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
24014 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
24018 fireKey: function(e){
24019 if (!this.picker().isVisible()){
24020 if (e.keyCode == 27) { // allow escape to hide and re-show picker
24026 e.preventDefault();
24034 this.onTogglePeriod();
24037 this.onIncrementMinutes();
24040 this.onDecrementMinutes();
24049 onClick: function(e) {
24050 e.stopPropagation();
24051 e.preventDefault();
24054 picker : function()
24056 return this.pickerEl;
24059 fillTime: function()
24061 var time = this.pop.select('tbody', true).first();
24063 time.dom.innerHTML = '';
24078 cls: 'hours-up fa fas fa-chevron-up'
24098 cls: 'minutes-up fa fas fa-chevron-up'
24119 cls: 'timepicker-hour',
24134 cls: 'timepicker-minute',
24149 cls: 'btn btn-primary period',
24171 cls: 'hours-down fa fas fa-chevron-down'
24191 cls: 'minutes-down fa fas fa-chevron-down'
24209 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
24216 var hours = this.time.getHours();
24217 var minutes = this.time.getMinutes();
24230 hours = hours - 12;
24234 hours = '0' + hours;
24238 minutes = '0' + minutes;
24241 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
24242 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
24243 this.pop.select('button', true).first().dom.innerHTML = period;
24249 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
24251 var cls = ['bottom'];
24253 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
24260 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
24264 //this.picker().setXY(20000,20000);
24265 this.picker().addClass(cls.join('-'));
24269 Roo.each(cls, function(c){
24274 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
24275 //_this.picker().setTop(_this.inputEl().getHeight());
24279 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
24281 //_this.picker().setTop(0 - _this.picker().getHeight());
24286 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
24290 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
24298 onFocus : function()
24300 Roo.bootstrap.form.TimeField.superclass.onFocus.call(this);
24304 onBlur : function()
24306 Roo.bootstrap.form.TimeField.superclass.onBlur.call(this);
24312 this.picker().show();
24317 this.fireEvent('show', this, this.date);
24322 this.picker().hide();
24325 this.fireEvent('hide', this, this.date);
24328 setTime : function()
24331 this.setValue(this.time.format(this.format));
24333 this.fireEvent('select', this, this.date);
24338 onMousedown: function(e){
24339 e.stopPropagation();
24340 e.preventDefault();
24343 onIncrementHours: function()
24345 Roo.log('onIncrementHours');
24346 this.time = this.time.add(Date.HOUR, 1);
24351 onDecrementHours: function()
24353 Roo.log('onDecrementHours');
24354 this.time = this.time.add(Date.HOUR, -1);
24358 onIncrementMinutes: function()
24360 Roo.log('onIncrementMinutes');
24361 this.time = this.time.add(Date.MINUTE, 1);
24365 onDecrementMinutes: function()
24367 Roo.log('onDecrementMinutes');
24368 this.time = this.time.add(Date.MINUTE, -1);
24372 onTogglePeriod: function()
24374 Roo.log('onTogglePeriod');
24375 this.time = this.time.add(Date.HOUR, 12);
24383 Roo.apply(Roo.bootstrap.form.TimeField, {
24387 cls: 'datepicker dropdown-menu',
24391 cls: 'datepicker-time',
24395 cls: 'table-condensed',
24424 cls: 'btn btn-info ok',
24452 * @class Roo.bootstrap.form.MonthField
24453 * @extends Roo.bootstrap.form.Input
24454 * Bootstrap MonthField class
24456 * @cfg {String} language default en
24459 * Create a new MonthField
24460 * @param {Object} config The config object
24463 Roo.bootstrap.form.MonthField = function(config){
24464 Roo.bootstrap.form.MonthField.superclass.constructor.call(this, config);
24469 * Fires when this field show.
24470 * @param {Roo.bootstrap.form.MonthField} this
24471 * @param {Mixed} date The date value
24476 * Fires when this field hide.
24477 * @param {Roo.bootstrap.form.MonthField} this
24478 * @param {Mixed} date The date value
24483 * Fires when select a date.
24484 * @param {Roo.bootstrap.form.MonthField} this
24485 * @param {String} oldvalue The old value
24486 * @param {String} newvalue The new value
24492 Roo.extend(Roo.bootstrap.form.MonthField, Roo.bootstrap.form.Input, {
24494 onRender: function(ct, position)
24497 Roo.bootstrap.form.MonthField.superclass.onRender.call(this, ct, position);
24499 this.language = this.language || 'en';
24500 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : this.language.split('-')[0];
24501 this.language = this.language in Roo.bootstrap.form.MonthField.dates ? this.language : "en";
24503 this.isRTL = Roo.bootstrap.form.MonthField.dates[this.language].rtl || false;
24504 this.isInline = false;
24505 this.isInput = true;
24506 this.component = this.el.select('.add-on', true).first() || false;
24507 this.component = (this.component && this.component.length === 0) ? false : this.component;
24508 this.hasInput = this.component && this.inputEL().length;
24510 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.form.MonthField.template);
24512 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24514 this.picker().on('mousedown', this.onMousedown, this);
24515 this.picker().on('click', this.onClick, this);
24517 this.picker().addClass('datepicker-dropdown');
24519 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24520 v.setStyle('width', '189px');
24527 if(this.isInline) {
24533 setValue: function(v, suppressEvent)
24535 var o = this.getValue();
24537 Roo.bootstrap.form.MonthField.superclass.setValue.call(this, v);
24541 if(suppressEvent !== true){
24542 this.fireEvent('select', this, o, v);
24547 getValue: function()
24552 onClick: function(e)
24554 e.stopPropagation();
24555 e.preventDefault();
24557 var target = e.getTarget();
24559 if(target.nodeName.toLowerCase() === 'i'){
24560 target = Roo.get(target).dom.parentNode;
24563 var nodeName = target.nodeName;
24564 var className = target.className;
24565 var html = target.innerHTML;
24567 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24571 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].monthsShort.indexOf(html);
24573 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24579 picker : function()
24581 return this.pickerEl;
24584 fillMonths: function()
24587 var months = this.picker().select('>.datepicker-months td', true).first();
24589 months.dom.innerHTML = '';
24595 html: Roo.bootstrap.form.MonthField.dates[this.language].monthsShort[i++]
24598 months.createChild(month);
24607 if(typeof(this.vIndex) == 'undefined' && this.value.length){
24608 this.vIndex = Roo.bootstrap.form.MonthField.dates[this.language].months.indexOf(this.value);
24611 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24612 e.removeClass('active');
24614 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24615 e.addClass('active');
24622 if(this.isInline) {
24626 this.picker().removeClass(['bottom', 'top']);
24628 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24630 * place to the top of element!
24634 this.picker().addClass('top');
24635 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24640 this.picker().addClass('bottom');
24642 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24645 onFocus : function()
24647 Roo.bootstrap.form.MonthField.superclass.onFocus.call(this);
24651 onBlur : function()
24653 Roo.bootstrap.form.MonthField.superclass.onBlur.call(this);
24655 var d = this.inputEl().getValue();
24664 this.picker().show();
24665 this.picker().select('>.datepicker-months', true).first().show();
24669 this.fireEvent('show', this, this.date);
24674 if(this.isInline) {
24677 this.picker().hide();
24678 this.fireEvent('hide', this, this.date);
24682 onMousedown: function(e)
24684 e.stopPropagation();
24685 e.preventDefault();
24690 Roo.bootstrap.form.MonthField.superclass.keyup.call(this);
24694 fireKey: function(e)
24696 if (!this.picker().isVisible()){
24697 if (e.keyCode == 27) {// allow escape to hide and re-show picker
24708 e.preventDefault();
24712 dir = e.keyCode == 37 ? -1 : 1;
24714 this.vIndex = this.vIndex + dir;
24716 if(this.vIndex < 0){
24720 if(this.vIndex > 11){
24724 if(isNaN(this.vIndex)){
24728 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24734 dir = e.keyCode == 38 ? -1 : 1;
24736 this.vIndex = this.vIndex + dir * 4;
24738 if(this.vIndex < 0){
24742 if(this.vIndex > 11){
24746 if(isNaN(this.vIndex)){
24750 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24755 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24756 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24760 e.preventDefault();
24763 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24764 this.setValue(Roo.bootstrap.form.MonthField.dates[this.language].months[this.vIndex]);
24780 this.picker().remove();
24785 Roo.apply(Roo.bootstrap.form.MonthField, {
24804 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24805 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24810 Roo.apply(Roo.bootstrap.form.MonthField, {
24814 cls: 'datepicker dropdown-menu roo-dynamic',
24818 cls: 'datepicker-months',
24822 cls: 'table-condensed',
24824 Roo.bootstrap.form.DateField.content
24844 * @class Roo.bootstrap.form.CheckBox
24845 * @extends Roo.bootstrap.form.Input
24846 * Bootstrap CheckBox class
24848 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24849 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24850 * @cfg {String} boxLabel The text that appears beside the checkbox
24851 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24852 * @cfg {Boolean} checked initnal the element
24853 * @cfg {Boolean} inline inline the element (default false)
24854 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24855 * @cfg {String} tooltip label tooltip
24858 * Create a new CheckBox
24859 * @param {Object} config The config object
24862 Roo.bootstrap.form.CheckBox = function(config){
24863 Roo.bootstrap.form.CheckBox.superclass.constructor.call(this, config);
24868 * Fires when the element is checked or unchecked.
24869 * @param {Roo.bootstrap.form.CheckBox} this This input
24870 * @param {Boolean} checked The new checked value
24875 * Fires when the element is click.
24876 * @param {Roo.bootstrap.form.CheckBox} this This input
24883 Roo.extend(Roo.bootstrap.form.CheckBox, Roo.bootstrap.form.Input, {
24885 inputType: 'checkbox',
24894 // checkbox success does not make any sense really..
24899 getAutoCreate : function()
24901 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24907 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24910 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
24916 type : this.inputType,
24917 value : this.inputValue,
24918 cls : 'roo-' + this.inputType, //'form-box',
24919 placeholder : this.placeholder || ''
24923 if(this.inputType != 'radio'){
24927 cls : 'roo-hidden-value',
24928 value : this.checked ? this.inputValue : this.valueOff
24933 if (this.weight) { // Validity check?
24934 cfg.cls += " " + this.inputType + "-" + this.weight;
24937 if (this.disabled) {
24938 input.disabled=true;
24942 input.checked = this.checked;
24947 input.name = this.name;
24949 if(this.inputType != 'radio'){
24950 hidden.name = this.name;
24951 input.name = '_hidden_' + this.name;
24956 input.cls += ' input-' + this.size;
24961 ['xs','sm','md','lg'].map(function(size){
24962 if (settings[size]) {
24963 cfg.cls += ' col-' + size + '-' + settings[size];
24967 var inputblock = input;
24969 if (this.before || this.after) {
24972 cls : 'input-group',
24977 inputblock.cn.push({
24979 cls : 'input-group-addon',
24984 inputblock.cn.push(input);
24986 if(this.inputType != 'radio'){
24987 inputblock.cn.push(hidden);
24991 inputblock.cn.push({
24993 cls : 'input-group-addon',
24999 var boxLabelCfg = false;
25005 //'for': id, // box label is handled by onclick - so no for...
25007 html: this.boxLabel
25010 boxLabelCfg.tooltip = this.tooltip;
25016 if (align ==='left' && this.fieldLabel.length) {
25017 // Roo.log("left and has label");
25022 cls : 'control-label',
25023 html : this.fieldLabel
25034 cfg.cn[1].cn.push(boxLabelCfg);
25037 if(this.labelWidth > 12){
25038 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
25041 if(this.labelWidth < 13 && this.labelmd == 0){
25042 this.labelmd = this.labelWidth;
25045 if(this.labellg > 0){
25046 cfg.cn[0].cls += ' col-lg-' + this.labellg;
25047 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
25050 if(this.labelmd > 0){
25051 cfg.cn[0].cls += ' col-md-' + this.labelmd;
25052 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
25055 if(this.labelsm > 0){
25056 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
25057 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
25060 if(this.labelxs > 0){
25061 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
25062 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
25065 } else if ( this.fieldLabel.length) {
25066 // Roo.log(" label");
25070 tag: this.boxLabel ? 'span' : 'label',
25072 cls: 'control-label box-input-label',
25073 //cls : 'input-group-addon',
25074 html : this.fieldLabel
25081 cfg.cn.push(boxLabelCfg);
25086 // Roo.log(" no label && no align");
25087 cfg.cn = [ inputblock ] ;
25089 cfg.cn.push(boxLabelCfg);
25097 if(this.inputType != 'radio'){
25098 cfg.cn.push(hidden);
25106 * return the real input element.
25108 inputEl: function ()
25110 return this.el.select('input.roo-' + this.inputType,true).first();
25112 hiddenEl: function ()
25114 return this.el.select('input.roo-hidden-value',true).first();
25117 labelEl: function()
25119 return this.el.select('label.control-label',true).first();
25121 /* depricated... */
25125 return this.labelEl();
25128 boxLabelEl: function()
25130 return this.el.select('label.box-label',true).first();
25133 initEvents : function()
25135 // Roo.bootstrap.form.CheckBox.superclass.initEvents.call(this);
25137 this.inputEl().on('click', this.onClick, this);
25139 if (this.boxLabel) {
25140 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
25143 this.startValue = this.getValue();
25146 Roo.bootstrap.form.CheckBox.register(this);
25150 onClick : function(e)
25152 if(this.fireEvent('click', this, e) !== false){
25153 this.setChecked(!this.checked);
25158 setChecked : function(state,suppressEvent)
25160 this.startValue = this.getValue();
25162 if(this.inputType == 'radio'){
25164 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25165 e.dom.checked = false;
25168 this.inputEl().dom.checked = true;
25170 this.inputEl().dom.value = this.inputValue;
25172 if(suppressEvent !== true){
25173 this.fireEvent('check', this, true);
25181 this.checked = state;
25183 this.inputEl().dom.checked = state;
25186 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
25188 if(suppressEvent !== true){
25189 this.fireEvent('check', this, state);
25195 getValue : function()
25197 if(this.inputType == 'radio'){
25198 return this.getGroupValue();
25201 return this.hiddenEl().dom.value;
25205 getGroupValue : function()
25207 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
25211 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
25214 setValue : function(v,suppressEvent)
25216 if(this.inputType == 'radio'){
25217 this.setGroupValue(v, suppressEvent);
25221 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
25226 setGroupValue : function(v, suppressEvent)
25228 this.startValue = this.getValue();
25230 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25231 e.dom.checked = false;
25233 if(e.dom.value == v){
25234 e.dom.checked = true;
25238 if(suppressEvent !== true){
25239 this.fireEvent('check', this, true);
25247 validate : function()
25249 if(this.getVisibilityEl().hasClass('hidden')){
25255 (this.inputType == 'radio' && this.validateRadio()) ||
25256 (this.inputType == 'checkbox' && this.validateCheckbox())
25262 this.markInvalid();
25266 validateRadio : function()
25268 if(this.getVisibilityEl().hasClass('hidden')){
25272 if(this.allowBlank){
25278 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25279 if(!e.dom.checked){
25291 validateCheckbox : function()
25294 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
25295 //return (this.getValue() == this.inputValue) ? true : false;
25298 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25306 for(var i in group){
25307 if(group[i].el.isVisible(true)){
25315 for(var i in group){
25320 r = (group[i].getValue() == group[i].inputValue) ? true : false;
25327 * Mark this field as valid
25329 markValid : function()
25333 this.fireEvent('valid', this);
25335 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25338 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25345 if(this.inputType == 'radio'){
25346 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25347 var fg = e.findParent('.form-group', false, true);
25348 if (Roo.bootstrap.version == 3) {
25349 fg.removeClass([_this.invalidClass, _this.validClass]);
25350 fg.addClass(_this.validClass);
25352 fg.removeClass(['is-valid', 'is-invalid']);
25353 fg.addClass('is-valid');
25361 var fg = this.el.findParent('.form-group', false, true);
25362 if (Roo.bootstrap.version == 3) {
25363 fg.removeClass([this.invalidClass, this.validClass]);
25364 fg.addClass(this.validClass);
25366 fg.removeClass(['is-valid', 'is-invalid']);
25367 fg.addClass('is-valid');
25372 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25378 for(var i in group){
25379 var fg = group[i].el.findParent('.form-group', false, true);
25380 if (Roo.bootstrap.version == 3) {
25381 fg.removeClass([this.invalidClass, this.validClass]);
25382 fg.addClass(this.validClass);
25384 fg.removeClass(['is-valid', 'is-invalid']);
25385 fg.addClass('is-valid');
25391 * Mark this field as invalid
25392 * @param {String} msg The validation message
25394 markInvalid : function(msg)
25396 if(this.allowBlank){
25402 this.fireEvent('invalid', this, msg);
25404 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25407 label = Roo.bootstrap.form.FieldLabel.get(this.groupId + '-group');
25411 label.markInvalid();
25414 if(this.inputType == 'radio'){
25416 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25417 var fg = e.findParent('.form-group', false, true);
25418 if (Roo.bootstrap.version == 3) {
25419 fg.removeClass([_this.invalidClass, _this.validClass]);
25420 fg.addClass(_this.invalidClass);
25422 fg.removeClass(['is-invalid', 'is-valid']);
25423 fg.addClass('is-invalid');
25431 var fg = this.el.findParent('.form-group', false, true);
25432 if (Roo.bootstrap.version == 3) {
25433 fg.removeClass([_this.invalidClass, _this.validClass]);
25434 fg.addClass(_this.invalidClass);
25436 fg.removeClass(['is-invalid', 'is-valid']);
25437 fg.addClass('is-invalid');
25442 var group = Roo.bootstrap.form.CheckBox.get(this.groupId);
25448 for(var i in group){
25449 var fg = group[i].el.findParent('.form-group', false, true);
25450 if (Roo.bootstrap.version == 3) {
25451 fg.removeClass([_this.invalidClass, _this.validClass]);
25452 fg.addClass(_this.invalidClass);
25454 fg.removeClass(['is-invalid', 'is-valid']);
25455 fg.addClass('is-invalid');
25461 clearInvalid : function()
25463 Roo.bootstrap.form.Input.prototype.clearInvalid.call(this);
25465 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
25467 var label = Roo.bootstrap.form.FieldLabel.get(this.name + '-group');
25469 if (label && label.iconEl) {
25470 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
25471 label.iconEl.removeClass(['is-invalid', 'is-valid']);
25475 disable : function()
25477 if(this.inputType != 'radio'){
25478 Roo.bootstrap.form.CheckBox.superclass.disable.call(this);
25485 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25486 _this.getActionEl().addClass(this.disabledClass);
25487 e.dom.disabled = true;
25491 this.disabled = true;
25492 this.fireEvent("disable", this);
25496 enable : function()
25498 if(this.inputType != 'radio'){
25499 Roo.bootstrap.form.CheckBox.superclass.enable.call(this);
25506 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25507 _this.getActionEl().removeClass(this.disabledClass);
25508 e.dom.disabled = false;
25512 this.disabled = false;
25513 this.fireEvent("enable", this);
25517 setBoxLabel : function(v)
25522 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25528 Roo.apply(Roo.bootstrap.form.CheckBox, {
25533 * register a CheckBox Group
25534 * @param {Roo.bootstrap.form.CheckBox} the CheckBox to add
25536 register : function(checkbox)
25538 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25539 this.groups[checkbox.groupId] = {};
25542 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25546 this.groups[checkbox.groupId][checkbox.name] = checkbox;
25550 * fetch a CheckBox Group based on the group ID
25551 * @param {string} the group ID
25552 * @returns {Roo.bootstrap.form.CheckBox} the CheckBox group
25554 get: function(groupId) {
25555 if (typeof(this.groups[groupId]) == 'undefined') {
25559 return this.groups[groupId] ;
25572 * @class Roo.bootstrap.form.Radio
25573 * @extends Roo.bootstrap.Component
25574 * Bootstrap Radio class
25575 * @cfg {String} boxLabel - the label associated
25576 * @cfg {String} value - the value of radio
25579 * Create a new Radio
25580 * @param {Object} config The config object
25582 Roo.bootstrap.form.Radio = function(config){
25583 Roo.bootstrap.form.Radio.superclass.constructor.call(this, config);
25587 Roo.extend(Roo.bootstrap.form.Radio, Roo.bootstrap.Component, {
25593 getAutoCreate : function()
25597 cls : 'form-group radio',
25602 html : this.boxLabel
25610 initEvents : function()
25612 this.parent().register(this);
25614 this.el.on('click', this.onClick, this);
25618 onClick : function(e)
25620 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25621 this.setChecked(true);
25625 setChecked : function(state, suppressEvent)
25627 this.parent().setValue(this.value, suppressEvent);
25631 setBoxLabel : function(v)
25636 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25651 * @class Roo.bootstrap.form.SecurePass
25652 * @extends Roo.bootstrap.form.Input
25653 * Bootstrap SecurePass class
25657 * Create a new SecurePass
25658 * @param {Object} config The config object
25661 Roo.bootstrap.form.SecurePass = function (config) {
25662 // these go here, so the translation tool can replace them..
25664 PwdEmpty: "Please type a password, and then retype it to confirm.",
25665 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25666 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25667 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25668 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25669 FNInPwd: "Your password can't contain your first name. Please type a different password.",
25670 LNInPwd: "Your password can't contain your last name. Please type a different password.",
25671 TooWeak: "Your password is Too Weak."
25673 this.meterLabel = "Password strength:";
25674 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25675 this.meterClass = [
25676 "roo-password-meter-tooweak",
25677 "roo-password-meter-weak",
25678 "roo-password-meter-medium",
25679 "roo-password-meter-strong",
25680 "roo-password-meter-grey"
25685 Roo.bootstrap.form.SecurePass.superclass.constructor.call(this, config);
25688 Roo.extend(Roo.bootstrap.form.SecurePass, Roo.bootstrap.form.Input, {
25690 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25692 * PwdEmpty: "Please type a password, and then retype it to confirm.",
25693 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25694 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25695 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25696 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25697 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
25698 * LNInPwd: "Your password can't contain your last name. Please type a different password."
25708 * @cfg {String/Object} Label for the strength meter (defaults to
25709 * 'Password strength:')
25714 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25715 * ['Weak', 'Medium', 'Strong'])
25718 pwdStrengths: false,
25731 initEvents: function ()
25733 Roo.bootstrap.form.SecurePass.superclass.initEvents.call(this);
25735 if (this.el.is('input[type=password]') && Roo.isSafari) {
25736 this.el.on('keydown', this.SafariOnKeyDown, this);
25739 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25742 onRender: function (ct, position)
25744 Roo.bootstrap.form.SecurePass.superclass.onRender.call(this, ct, position);
25745 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25746 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25748 this.trigger.createChild({
25753 cls: 'roo-password-meter-grey col-xs-12',
25756 //width: this.meterWidth + 'px'
25760 cls: 'roo-password-meter-text'
25766 if (this.hideTrigger) {
25767 this.trigger.setDisplayed(false);
25769 this.setSize(this.width || '', this.height || '');
25772 onDestroy: function ()
25774 if (this.trigger) {
25775 this.trigger.removeAllListeners();
25776 this.trigger.remove();
25779 this.wrap.remove();
25781 Roo.bootstrap.form.TriggerField.superclass.onDestroy.call(this);
25784 checkStrength: function ()
25786 var pwd = this.inputEl().getValue();
25787 if (pwd == this._lastPwd) {
25792 if (this.ClientSideStrongPassword(pwd)) {
25794 } else if (this.ClientSideMediumPassword(pwd)) {
25796 } else if (this.ClientSideWeakPassword(pwd)) {
25802 Roo.log('strength1: ' + strength);
25804 //var pm = this.trigger.child('div/div/div').dom;
25805 var pm = this.trigger.child('div/div');
25806 pm.removeClass(this.meterClass);
25807 pm.addClass(this.meterClass[strength]);
25810 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25812 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25814 this._lastPwd = pwd;
25818 Roo.bootstrap.form.SecurePass.superclass.reset.call(this);
25820 this._lastPwd = '';
25822 var pm = this.trigger.child('div/div');
25823 pm.removeClass(this.meterClass);
25824 pm.addClass('roo-password-meter-grey');
25827 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25830 this.inputEl().dom.type='password';
25833 validateValue: function (value)
25835 if (!Roo.bootstrap.form.SecurePass.superclass.validateValue.call(this, value)) {
25838 if (value.length == 0) {
25839 if (this.allowBlank) {
25840 this.clearInvalid();
25844 this.markInvalid(this.errors.PwdEmpty);
25845 this.errorMsg = this.errors.PwdEmpty;
25853 if (!value.match(/[\x21-\x7e]+/)) {
25854 this.markInvalid(this.errors.PwdBadChar);
25855 this.errorMsg = this.errors.PwdBadChar;
25858 if (value.length < 6) {
25859 this.markInvalid(this.errors.PwdShort);
25860 this.errorMsg = this.errors.PwdShort;
25863 if (value.length > 16) {
25864 this.markInvalid(this.errors.PwdLong);
25865 this.errorMsg = this.errors.PwdLong;
25869 if (this.ClientSideStrongPassword(value)) {
25871 } else if (this.ClientSideMediumPassword(value)) {
25873 } else if (this.ClientSideWeakPassword(value)) {
25880 if (strength < 2) {
25881 //this.markInvalid(this.errors.TooWeak);
25882 this.errorMsg = this.errors.TooWeak;
25887 console.log('strength2: ' + strength);
25889 //var pm = this.trigger.child('div/div/div').dom;
25891 var pm = this.trigger.child('div/div');
25892 pm.removeClass(this.meterClass);
25893 pm.addClass(this.meterClass[strength]);
25895 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
25897 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
25899 this.errorMsg = '';
25903 CharacterSetChecks: function (type)
25906 this.fResult = false;
25909 isctype: function (character, type)
25912 case this.kCapitalLetter:
25913 if (character >= 'A' && character <= 'Z') {
25918 case this.kSmallLetter:
25919 if (character >= 'a' && character <= 'z') {
25925 if (character >= '0' && character <= '9') {
25930 case this.kPunctuation:
25931 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25942 IsLongEnough: function (pwd, size)
25944 return !(pwd == null || isNaN(size) || pwd.length < size);
25947 SpansEnoughCharacterSets: function (word, nb)
25949 if (!this.IsLongEnough(word, nb))
25954 var characterSetChecks = new Array(
25955 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25956 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25959 for (var index = 0; index < word.length; ++index) {
25960 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25961 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25962 characterSetChecks[nCharSet].fResult = true;
25969 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25970 if (characterSetChecks[nCharSet].fResult) {
25975 if (nCharSets < nb) {
25981 ClientSideStrongPassword: function (pwd)
25983 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25986 ClientSideMediumPassword: function (pwd)
25988 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25991 ClientSideWeakPassword: function (pwd)
25993 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25997 Roo.htmleditor = {};
26000 * @class Roo.htmleditor.Filter
26001 * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
26002 * @cfg {DomElement} node The node to iterate and filter
26003 * @cfg {boolean|String|Array} tag Tags to replace
26005 * Create a new Filter.
26006 * @param {Object} config Configuration options
26011 Roo.htmleditor.Filter = function(cfg) {
26012 Roo.apply(this.cfg);
26013 // this does not actually call walk as it's really just a abstract class
26017 Roo.htmleditor.Filter.prototype = {
26023 // overrride to do replace comments.
26024 replaceComment : false,
26026 // overrride to do replace or do stuff with tags..
26027 replaceTag : false,
26029 walk : function(dom)
26031 Roo.each( Array.from(dom.childNodes), function( e ) {
26034 case e.nodeType == 8 && this.replaceComment !== false: // comment
26035 this.replaceComment(e);
26038 case e.nodeType != 1: //not a node.
26041 case this.tag === true: // everything
26042 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
26043 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
26044 if (this.replaceTag && false === this.replaceTag(e)) {
26047 if (e.hasChildNodes()) {
26052 default: // tags .. that do not match.
26053 if (e.hasChildNodes()) {
26064 * @class Roo.htmleditor.FilterAttributes
26065 * clean attributes and styles including http:// etc.. in attribute
26067 * Run a new Attribute Filter
26068 * @param {Object} config Configuration options
26070 Roo.htmleditor.FilterAttributes = function(cfg)
26072 Roo.apply(this, cfg);
26073 this.attrib_black = this.attrib_black || [];
26074 this.attrib_white = this.attrib_white || [];
26076 this.attrib_clean = this.attrib_clean || [];
26077 this.style_white = this.style_white || [];
26078 this.style_black = this.style_black || [];
26079 this.walk(cfg.node);
26082 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
26084 tag: true, // all tags
26086 attrib_black : false, // array
26087 attrib_clean : false,
26088 attrib_white : false,
26090 style_white : false,
26091 style_black : false,
26094 replaceTag : function(node)
26096 if (!node.attributes || !node.attributes.length) {
26100 for (var i = node.attributes.length-1; i > -1 ; i--) {
26101 var a = node.attributes[i];
26103 if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
26104 node.removeAttribute(a.name);
26110 if (a.name.toLowerCase().substr(0,2)=='on') {
26111 node.removeAttribute(a.name);
26116 if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
26117 node.removeAttribute(a.name);
26120 if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
26121 this.cleanAttr(node,a.name,a.value); // fixme..
26124 if (a.name == 'style') {
26125 this.cleanStyle(node,a.name,a.value);
26128 /// clean up MS crap..
26129 // tecnically this should be a list of valid class'es..
26132 if (a.name == 'class') {
26133 if (a.value.match(/^Mso/)) {
26134 node.removeAttribute('class');
26137 if (a.value.match(/^body$/)) {
26138 node.removeAttribute('class');
26148 return true; // clean children
26151 cleanAttr: function(node, n,v)
26154 if (v.match(/^\./) || v.match(/^\//)) {
26157 if (v.match(/^(http|https):\/\//)
26158 || v.match(/^mailto:/)
26159 || v.match(/^ftp:/)
26160 || v.match(/^data:/)
26164 if (v.match(/^#/)) {
26167 if (v.match(/^\{/)) { // allow template editing.
26170 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26171 node.removeAttribute(n);
26174 cleanStyle : function(node, n,v)
26176 if (v.match(/expression/)) { //XSS?? should we even bother..
26177 node.removeAttribute(n);
26181 var parts = v.split(/;/);
26184 Roo.each(parts, function(p) {
26185 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26189 var l = p.split(':').shift().replace(/\s+/g,'');
26190 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26192 if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
26196 // only allow 'c whitelisted system attributes'
26197 if ( this.style_white.length && style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
26205 if (clean.length) {
26206 node.setAttribute(n, clean.join(';'));
26208 node.removeAttribute(n);
26217 * @class Roo.htmleditor.FilterBlack
26218 * remove blacklisted elements.
26220 * Run a new Blacklisted Filter
26221 * @param {Object} config Configuration options
26224 Roo.htmleditor.FilterBlack = function(cfg)
26226 Roo.apply(this, cfg);
26227 this.walk(cfg.node);
26230 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
26232 tag : true, // all elements.
26234 replaceTag : function(n)
26236 n.parentNode.removeChild(n);
26240 * @class Roo.htmleditor.FilterComment
26243 * Run a new Comments Filter
26244 * @param {Object} config Configuration options
26246 Roo.htmleditor.FilterComment = function(cfg)
26248 this.walk(cfg.node);
26251 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
26254 replaceComment : function(n)
26256 n.parentNode.removeChild(n);
26259 * @class Roo.htmleditor.FilterKeepChildren
26260 * remove tags but keep children
26262 * Run a new Keep Children Filter
26263 * @param {Object} config Configuration options
26266 Roo.htmleditor.FilterKeepChildren = function(cfg)
26268 Roo.apply(this, cfg);
26269 if (this.tag === false) {
26270 return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
26272 this.walk(cfg.node);
26275 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
26279 replaceTag : function(node)
26281 // walk children...
26283 var ar = Array.from(node.childNodes);
26285 for (var i = 0; i < ar.length; i++) {
26286 if (ar[i].nodeType == 1) {
26288 (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
26289 || // array and it matches
26290 (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
26292 this.replaceTag(ar[i]); // child is blacklisted as well...
26297 ar = Array.from(node.childNodes);
26298 for (var i = 0; i < ar.length; i++) {
26300 node.removeChild(ar[i]);
26301 // what if we need to walk these???
26302 node.parentNode.insertBefore(ar[i], node);
26303 if (this.tag !== false) {
26308 node.parentNode.removeChild(node);
26309 return false; // don't walk children
26314 * @class Roo.htmleditor.FilterParagraph
26315 * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
26316 * like on 'push' to remove the <p> tags and replace them with line breaks.
26318 * Run a new Paragraph Filter
26319 * @param {Object} config Configuration options
26322 Roo.htmleditor.FilterParagraph = function(cfg)
26324 // no need to apply config.
26325 this.walk(cfg.node);
26328 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
26335 replaceTag : function(node)
26338 if (node.childNodes.length == 1 &&
26339 node.childNodes[0].nodeType == 3 &&
26340 node.childNodes[0].textContent.trim().length < 1
26342 // remove and replace with '<BR>';
26343 node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
26344 return false; // no need to walk..
26346 var ar = Array.from(node.childNodes);
26347 for (var i = 0; i < ar.length; i++) {
26348 node.removeChild(ar[i]);
26349 // what if we need to walk these???
26350 node.parentNode.insertBefore(ar[i], node);
26352 // now what about this?
26356 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26357 node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
26358 node.parentNode.removeChild(node);
26365 * @class Roo.htmleditor.FilterSpan
26366 * filter span's with no attributes out..
26368 * Run a new Span Filter
26369 * @param {Object} config Configuration options
26372 Roo.htmleditor.FilterSpan = function(cfg)
26374 // no need to apply config.
26375 this.walk(cfg.node);
26378 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
26384 replaceTag : function(node)
26386 if (node.attributes && node.attributes.length > 0) {
26387 return true; // walk if there are any.
26389 Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
26395 * @class Roo.htmleditor.FilterTableWidth
26396 try and remove table width data - as that frequently messes up other stuff.
26398 * was cleanTableWidths.
26400 * Quite often pasting from word etc.. results in tables with column and widths.
26401 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26404 * Run a new Table Filter
26405 * @param {Object} config Configuration options
26408 Roo.htmleditor.FilterTableWidth = function(cfg)
26410 // no need to apply config.
26411 this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
26412 this.walk(cfg.node);
26415 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
26420 replaceTag: function(node) {
26424 if (node.hasAttribute('width')) {
26425 node.removeAttribute('width');
26429 if (node.hasAttribute("style")) {
26432 var styles = node.getAttribute("style").split(";");
26434 Roo.each(styles, function(s) {
26435 if (!s.match(/:/)) {
26438 var kv = s.split(":");
26439 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26442 // what ever is left... we allow.
26445 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26446 if (!nstyle.length) {
26447 node.removeAttribute('style');
26451 return true; // continue doing children..
26454 * @class Roo.htmleditor.FilterWord
26455 * try and clean up all the mess that Word generates.
26457 * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters
26460 * Run a new Span Filter
26461 * @param {Object} config Configuration options
26464 Roo.htmleditor.FilterWord = function(cfg)
26466 // no need to apply config.
26467 this.replaceDocBullets(cfg.node);
26469 // this.walk(cfg.node);
26474 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
26480 * Clean up MS wordisms...
26482 replaceTag : function(node)
26485 // no idea what this does - span with text, replaceds with just text.
26487 node.nodeName == 'SPAN' &&
26488 !node.hasAttributes() &&
26489 node.childNodes.length == 1 &&
26490 node.firstChild.nodeName == "#text"
26492 var textNode = node.firstChild;
26493 node.removeChild(textNode);
26494 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26495 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26497 node.parentNode.insertBefore(textNode, node);
26498 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
26499 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26502 node.parentNode.removeChild(node);
26503 return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
26508 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26509 node.parentNode.removeChild(node);
26510 return false; // dont do chidlren
26512 //Roo.log(node.tagName);
26513 // remove - but keep children..
26514 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26515 //Roo.log('-- removed');
26516 while (node.childNodes.length) {
26517 var cn = node.childNodes[0];
26518 node.removeChild(cn);
26519 node.parentNode.insertBefore(cn, node);
26520 // move node to parent - and clean it..
26521 if (cn.nodeType == 1) {
26522 this.replaceTag(cn);
26526 node.parentNode.removeChild(node);
26527 /// no need to iterate chidlren = it's got none..
26528 //this.iterateChildren(node, this.cleanWord);
26529 return false; // no need to iterate children.
26532 if (node.className.length) {
26534 var cn = node.className.split(/\W+/);
26536 Roo.each(cn, function(cls) {
26537 if (cls.match(/Mso[a-zA-Z]+/)) {
26542 node.className = cna.length ? cna.join(' ') : '';
26544 node.removeAttribute("class");
26548 if (node.hasAttribute("lang")) {
26549 node.removeAttribute("lang");
26552 if (node.hasAttribute("style")) {
26554 var styles = node.getAttribute("style").split(";");
26556 Roo.each(styles, function(s) {
26557 if (!s.match(/:/)) {
26560 var kv = s.split(":");
26561 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26564 // what ever is left... we allow.
26567 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26568 if (!nstyle.length) {
26569 node.removeAttribute('style');
26572 return true; // do children
26578 styleToObject: function(node)
26580 var styles = (node.getAttribute("style") || '').split(";");
26582 Roo.each(styles, function(s) {
26583 if (!s.match(/:/)) {
26586 var kv = s.split(":");
26588 // what ever is left... we allow.
26589 ret[kv[0].trim()] = kv[1];
26595 replaceDocBullets : function(doc)
26597 // this is a bit odd - but it appears some indents use ql-indent-1
26599 var listpara = doc.getElementsByClassName('ql-indent-1');
26600 while(listpara.length) {
26601 this.replaceDocBullet(listpara.item(0));
26604 var listpara = doc.getElementsByClassName('MsoListParagraph');
26605 while(listpara.length) {
26606 this.replaceDocBullet(listpara.item(0));
26610 replaceDocBullet : function(p)
26612 // gather all the siblings.
26614 parent = p.parentNode,
26615 doc = parent.ownerDocument,
26620 if (ns.nodeType != 1) {
26621 ns = ns.nextSibling;
26624 if (!ns.className.match(/(MsoListParagraph|ql-indent-1)/i)) {
26628 ns = ns.nextSibling;
26632 var ul = parent.ownerDocument.createElement('ul'); // what about number lists...
26633 parent.insertBefore(ul, p);
26635 var stack = [ ul ];
26636 var last_li = false;
26638 items.forEach(function(n, ipos) {
26639 //Roo.log("got innertHMLT=" + n.innerHTML);
26641 var spans = n.getElementsByTagName('span');
26642 if (!spans.length) {
26643 //Roo.log("No spans found");
26645 parent.removeChild(n);
26646 return; // skip it...
26652 for(var i = 0; i < spans.length; i++) {
26654 style = this.styleToObject(spans[i]);
26655 if (typeof(style['mso-list']) == 'undefined') {
26659 spans[i].parentNode.removeChild(spans[i]); // remove the fake bullet.
26662 //Roo.log("NOW GOT innertHMLT=" + n.innerHTML);
26663 style = this.styleToObject(n); // mo-list is from the parent node.
26664 if (typeof(style['mso-list']) == 'undefined') {
26665 //Roo.log("parent is missing level");
26666 parent.removeChild(n);
26670 var nlvl = (style['mso-list'].split(' ')[1].replace(/level/,'') *1) - 1 ;
26676 var nul = doc.createElement('ul'); // what about number lists...
26677 last_li.appendChild(nul);
26683 var nli = stack[nlvl].appendChild(doc.createElement('li'));
26685 nli.innerHTML = n.innerHTML;
26686 //Roo.log("innerHTML = " + n.innerHTML);
26687 parent.removeChild(n);
26689 // copy children of p into nli
26690 /*while(n.firstChild) {
26691 var fc = n.firstChild;
26693 nli.appendChild(fc);
26708 * @class Roo.htmleditor.FilterStyleToTag
26709 * part of the word stuff... - certain 'styles' should be converted to tags.
26711 * font-weight: bold -> bold
26712 * ?? super / subscrit etc..
26715 * Run a new style to tag filter.
26716 * @param {Object} config Configuration options
26718 Roo.htmleditor.FilterStyleToTag = function(cfg)
26722 B : [ 'fontWeight' , 'bold'],
26723 I : [ 'fontStyle' , 'italic'],
26724 //pre : [ 'font-style' , 'italic'],
26725 // h1.. h6 ?? font-size?
26726 SUP : [ 'verticalAlign' , 'super' ],
26727 SUB : [ 'verticalAlign' , 'sub' ]
26732 Roo.apply(this, cfg);
26735 this.walk(cfg.node);
26742 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
26744 tag: true, // all tags
26749 replaceTag : function(node)
26753 if (node.getAttribute("style") === null) {
26757 for (var k in this.tags) {
26758 if (node.style[this.tags[k][0]] == this.tags[k][1]) {
26760 node.style.removeProperty(this.tags[k][0]);
26763 if (!inject.length) {
26766 var cn = Array.from(node.childNodes);
26768 Roo.each(inject, function(t) {
26769 var nc = node.ownerDocument.createElement(t);
26770 nn.appendChild(nc);
26773 for(var i = 0;i < cn.length;cn++) {
26774 node.removeChild(cn[i]);
26775 nn.appendChild(cn[i]);
26777 return true /// iterate thru
26781 * @class Roo.htmleditor.FilterLongBr
26782 * BR/BR/BR - keep a maximum of 2...
26784 * Run a new Long BR Filter
26785 * @param {Object} config Configuration options
26788 Roo.htmleditor.FilterLongBr = function(cfg)
26790 // no need to apply config.
26791 this.walk(cfg.node);
26794 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
26801 replaceTag : function(node)
26804 var ps = node.nextSibling;
26805 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26806 ps = ps.nextSibling;
26809 if (!ps && [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) {
26810 node.parentNode.removeChild(node); // remove last BR inside one fo these tags
26814 if (!ps || ps.nodeType != 1) {
26818 if (!ps || ps.tagName != 'BR') {
26827 if (!node.previousSibling) {
26830 var ps = node.previousSibling;
26832 while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
26833 ps = ps.previousSibling;
26835 if (!ps || ps.nodeType != 1) {
26838 // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
26839 if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
26843 node.parentNode.removeChild(node); // remove me...
26845 return false; // no need to do children
26852 * @class Roo.htmleditor.FilterBlock
26853 * removes id / data-block and contenteditable that are associated with blocks
26854 * usage should be done on a cloned copy of the dom
26856 * Run a new Attribute Filter { node : xxxx }}
26857 * @param {Object} config Configuration options
26859 Roo.htmleditor.FilterBlock = function(cfg)
26861 Roo.apply(this, cfg);
26862 var qa = cfg.node.querySelectorAll;
26863 this.removeAttributes('data-block');
26864 this.removeAttributes('contenteditable');
26865 this.removeAttributes('id');
26869 Roo.apply(Roo.htmleditor.FilterBlock.prototype,
26871 node: true, // all tags
26874 removeAttributes : function(attr)
26876 var ar = this.node.querySelectorAll('*[' + attr + ']');
26877 for (var i =0;i<ar.length;i++) {
26878 ar[i].removeAttribute(attr);
26887 * @class Roo.htmleditor.KeyEnter
26888 * Handle Enter press..
26889 * @cfg {Roo.HtmlEditorCore} core the editor.
26891 * Create a new Filter.
26892 * @param {Object} config Configuration options
26899 Roo.htmleditor.KeyEnter = function(cfg) {
26900 Roo.apply(this, cfg);
26901 // this does not actually call walk as it's really just a abstract class
26903 Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
26906 //Roo.htmleditor.KeyEnter.i = 0;
26909 Roo.htmleditor.KeyEnter.prototype = {
26913 keypress : function(e)
26915 if (e.charCode != 13 && e.charCode != 10) {
26916 Roo.log([e.charCode,e]);
26919 e.preventDefault();
26920 // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
26921 var doc = this.core.doc;
26925 var sel = this.core.getSelection();
26926 var range = sel.getRangeAt(0);
26927 var n = range.commonAncestorContainer;
26928 var pc = range.closest([ 'ol', 'ul']);
26929 var pli = range.closest('li');
26930 if (!pc || e.ctrlKey) {
26931 sel.insertNode('br', 'after');
26933 this.core.undoManager.addEvent();
26934 this.core.fireEditorEvent(e);
26938 // deal with <li> insetion
26939 if (pli.innerText.trim() == '' &&
26940 pli.previousSibling &&
26941 pli.previousSibling.nodeName == 'LI' &&
26942 pli.previousSibling.innerText.trim() == '') {
26943 pli.parentNode.removeChild(pli.previousSibling);
26944 sel.cursorAfter(pc);
26945 this.core.undoManager.addEvent();
26946 this.core.fireEditorEvent(e);
26950 var li = doc.createElement('LI');
26951 li.innerHTML = ' ';
26952 if (!pli || !pli.firstSibling) {
26953 pc.appendChild(li);
26955 pli.parentNode.insertBefore(li, pli.firstSibling);
26957 sel.cursorText (li.firstChild);
26959 this.core.undoManager.addEvent();
26960 this.core.fireEditorEvent(e);
26972 * @class Roo.htmleditor.Block
26973 * Base class for html editor blocks - do not use it directly .. extend it..
26974 * @cfg {DomElement} node The node to apply stuff to.
26975 * @cfg {String} friendly_name the name that appears in the context bar about this block
26976 * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
26979 * Create a new Filter.
26980 * @param {Object} config Configuration options
26983 Roo.htmleditor.Block = function(cfg)
26985 // do nothing .. should not be called really.
26988 * factory method to get the block from an element (using cache if necessary)
26990 * @param {HtmlElement} the dom element
26992 Roo.htmleditor.Block.factory = function(node)
26994 var cc = Roo.htmleditor.Block.cache;
26995 var id = Roo.get(node).id;
26996 if (typeof(cc[id]) != 'undefined' && (!cc[id].node || cc[id].node.closest('body'))) {
26997 Roo.htmleditor.Block.cache[id].readElement(node);
26998 return Roo.htmleditor.Block.cache[id];
27000 var db = node.getAttribute('data-block');
27002 db = node.nodeName.toLowerCase().toUpperCaseFirst();
27004 var cls = Roo.htmleditor['Block' + db];
27005 if (typeof(cls) == 'undefined') {
27006 //Roo.log(node.getAttribute('data-block'));
27007 Roo.log("OOps missing block : " + 'Block' + db);
27010 Roo.htmleditor.Block.cache[id] = new cls({ node: node });
27011 return Roo.htmleditor.Block.cache[id]; /// should trigger update element
27015 * initalize all Elements from content that are 'blockable'
27017 * @param the body element
27019 Roo.htmleditor.Block.initAll = function(body, type)
27021 if (typeof(type) == 'undefined') {
27022 var ia = Roo.htmleditor.Block.initAll;
27028 Roo.each(Roo.get(body).query(type), function(e) {
27029 Roo.htmleditor.Block.factory(e);
27032 // question goes here... do we need to clear out this cache sometimes?
27033 // or show we make it relivant to the htmleditor.
27034 Roo.htmleditor.Block.cache = {};
27036 Roo.htmleditor.Block.prototype = {
27040 // used by context menu
27041 friendly_name : 'Based Block',
27043 // text for button to delete this element
27044 deleteTitle : false,
27048 * Update a node with values from this object
27049 * @param {DomElement} node
27051 updateElement : function(node)
27053 Roo.DomHelper.update(node === undefined ? this.node : node, this.toObject());
27056 * convert to plain HTML for calling insertAtCursor..
27058 toHTML : function()
27060 return Roo.DomHelper.markup(this.toObject());
27063 * used by readEleemnt to extract data from a node
27064 * may need improving as it's pretty basic
27066 * @param {DomElement} node
27067 * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
27068 * @param {String} attribute (use html - for contents, style for using next param as style, or false to return the node)
27069 * @param {String} style the style property - eg. text-align
27071 getVal : function(node, tag, attr, style)
27074 if (tag !== true && n.tagName != tag.toUpperCase()) {
27075 // in theory we could do figure[3] << 3rd figure? or some more complex search..?
27076 // but kiss for now.
27077 n = node.getElementsByTagName(tag).item(0);
27082 if (attr === false) {
27085 if (attr == 'html') {
27086 return n.innerHTML;
27088 if (attr == 'style') {
27089 return n.style[style];
27092 return n.hasAttribute(attr) ? n.getAttribute(attr) : '';
27096 * create a DomHelper friendly object - for use with
27097 * Roo.DomHelper.markup / overwrite / etc..
27100 toObject : function()
27105 * Read a node that has a 'data-block' property - and extract the values from it.
27106 * @param {DomElement} node - the node
27108 readElement : function(node)
27119 * @class Roo.htmleditor.BlockFigure
27120 * Block that has an image and a figcaption
27121 * @cfg {String} image_src the url for the image
27122 * @cfg {String} align (left|right) alignment for the block default left
27123 * @cfg {String} caption the text to appear below (and in the alt tag)
27124 * @cfg {String} caption_display (block|none) display or not the caption
27125 * @cfg {String|number} image_width the width of the image number or %?
27126 * @cfg {String|number} image_height the height of the image number or %?
27129 * Create a new Filter.
27130 * @param {Object} config Configuration options
27133 Roo.htmleditor.BlockFigure = function(cfg)
27136 this.readElement(cfg.node);
27137 this.updateElement(cfg.node);
27139 Roo.apply(this, cfg);
27141 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
27148 caption_display : 'block',
27154 // margin: '2%', not used
27156 text_align: 'left', // (left|right) alignment for the text caption default left. - not used at present
27159 // used by context menu
27160 friendly_name : 'Image with caption',
27161 deleteTitle : "Delete Image and Caption",
27163 contextMenu : function(toolbar)
27166 var block = function() {
27167 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27171 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27173 var syncValue = toolbar.editorcore.syncValue;
27179 xtype : 'TextItem',
27181 xns : rooui.Toolbar //Boostrap?
27185 text: 'Change Image URL',
27188 click: function (btn, state)
27192 Roo.MessageBox.show({
27193 title : "Image Source URL",
27194 msg : "Enter the url for the image",
27195 buttons: Roo.MessageBox.OKCANCEL,
27196 fn: function(btn, val){
27203 toolbar.editorcore.onEditorEvent();
27207 //multiline: multiline,
27209 value : b.image_src
27213 xns : rooui.Toolbar
27218 text: 'Change Link URL',
27221 click: function (btn, state)
27225 Roo.MessageBox.show({
27226 title : "Link URL",
27227 msg : "Enter the url for the link - leave blank to have no link",
27228 buttons: Roo.MessageBox.OKCANCEL,
27229 fn: function(btn, val){
27236 toolbar.editorcore.onEditorEvent();
27240 //multiline: multiline,
27246 xns : rooui.Toolbar
27250 text: 'Show Video URL',
27253 click: function (btn, state)
27255 Roo.MessageBox.alert("Video URL",
27256 block().video_url == '' ? 'This image is not linked ot a video' :
27257 'The image is linked to: <a target="_new" href="' + block().video_url + '">' + block().video_url + '</a>');
27260 xns : rooui.Toolbar
27265 xtype : 'TextItem',
27267 xns : rooui.Toolbar //Boostrap?
27270 xtype : 'ComboBox',
27271 allowBlank : false,
27272 displayField : 'val',
27275 triggerAction : 'all',
27277 valueField : 'val',
27281 select : function (combo, r, index)
27283 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27285 b.width = r.get('val');
27288 toolbar.editorcore.onEditorEvent();
27293 xtype : 'SimpleStore',
27304 xtype : 'TextItem',
27306 xns : rooui.Toolbar //Boostrap?
27309 xtype : 'ComboBox',
27310 allowBlank : false,
27311 displayField : 'val',
27314 triggerAction : 'all',
27316 valueField : 'val',
27320 select : function (combo, r, index)
27322 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27324 b.align = r.get('val');
27327 toolbar.editorcore.onEditorEvent();
27332 xtype : 'SimpleStore',
27346 text: 'Hide Caption',
27347 name : 'caption_display',
27349 enableToggle : true,
27350 setValue : function(v) {
27351 // this trigger toggle.
27353 this.setText(v ? "Hide Caption" : "Show Caption");
27354 this.setPressed(v != 'block');
27357 toggle: function (btn, state)
27360 b.caption_display = b.caption_display == 'block' ? 'none' : 'block';
27361 this.setText(b.caption_display == 'block' ? "Hide Caption" : "Show Caption");
27364 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27365 toolbar.editorcore.onEditorEvent();
27368 xns : rooui.Toolbar
27374 * create a DomHelper friendly object - for use with
27375 * Roo.DomHelper.markup / overwrite / etc..
27377 toObject : function()
27379 var d = document.createElement('div');
27380 d.innerHTML = this.caption;
27382 var m = this.width != '100%' && this.align == 'center' ? '0 auto' : 0;
27384 var iw = this.align == 'center' ? this.width : '100%';
27387 contenteditable : 'false',
27388 src : this.image_src,
27389 alt : d.innerText.replace(/\n/g, " ").replace(/\s+/g, ' ').trim(), // removeHTML and reduce spaces..
27392 maxWidth : iw + ' !important', // this is not getting rendered?
27398 '<div class="{0}" width="420" height="315" src="{1}" frameborder="0" allowfullscreen>' +
27400 '<img class="{0}-thumbnail" src="{3}/Images/{4}/{5}#image-{4}" />' +
27405 if (this.href.length > 0) {
27409 contenteditable : 'true',
27417 if (this.video_url.length > 0) {
27422 allowfullscreen : true,
27423 width : 420, // these are for video tricks - that we replace the outer
27425 src : this.video_url,
27431 // we remove caption totally if its hidden... - will delete data.. but otherwise we end up with fake caption
27432 var captionhtml = this.caption_display == 'none' ? '' : (this.caption.length ? this.caption : "Caption");
27437 'data-block' : 'Figure',
27438 'data-width' : this.width,
27439 contenteditable : 'false',
27443 float : this.align ,
27444 maxWidth : this.align == 'center' ? '100% !important' : (this.width + ' !important'),
27445 width : this.align == 'center' ? '100%' : this.width,
27447 padding: this.align == 'center' ? '0' : '0 10px' ,
27448 textAlign : this.align // seems to work for email..
27453 align : this.align,
27459 'data-display' : this.caption_display,
27461 textAlign : 'left',
27463 lineHeight : '24px',
27464 display : this.caption_display,
27465 maxWidth : (this.align == 'center' ? this.width : '100%' ) + ' !important',
27467 width: this.align == 'center' ? this.width : '100%'
27471 cls : this.cls.length > 0 ? (this.cls + '-thumbnail' ) : '',
27476 marginTop : '16px',
27482 // we can not rely on yahoo syndication to use CSS elements - so have to use '<i>' to encase stuff.
27484 contenteditable : true,
27500 readElement : function(node)
27502 // this should not really come from the link...
27503 this.video_url = this.getVal(node, 'div', 'src');
27504 this.cls = this.getVal(node, 'div', 'class');
27505 this.href = this.getVal(node, 'a', 'href');
27508 this.image_src = this.getVal(node, 'img', 'src');
27510 this.align = this.getVal(node, 'figure', 'align');
27511 var figcaption = this.getVal(node, 'figcaption', false);
27512 if (figcaption !== '') {
27513 this.caption = this.getVal(figcaption, 'i', 'html');
27517 this.caption_display = this.getVal(node, 'figcaption', 'data-display');
27518 //this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
27519 this.width = this.getVal(node, true, 'data-width');
27520 //this.margin = this.getVal(node, 'figure', 'style', 'margin');
27523 removeNode : function()
27540 * @class Roo.htmleditor.BlockTable
27541 * Block that manages a table
27544 * Create a new Filter.
27545 * @param {Object} config Configuration options
27548 Roo.htmleditor.BlockTable = function(cfg)
27551 this.readElement(cfg.node);
27552 this.updateElement(cfg.node);
27554 Roo.apply(this, cfg);
27557 for(var r = 0; r < this.no_row; r++) {
27559 for(var c = 0; c < this.no_col; c++) {
27560 this.rows[r][c] = this.emptyCell();
27567 Roo.extend(Roo.htmleditor.BlockTable, Roo.htmleditor.Block, {
27576 // used by context menu
27577 friendly_name : 'Table',
27578 deleteTitle : 'Delete Table',
27579 // context menu is drawn once..
27581 contextMenu : function(toolbar)
27584 var block = function() {
27585 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
27589 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
27591 var syncValue = toolbar.editorcore.syncValue;
27597 xtype : 'TextItem',
27599 xns : rooui.Toolbar //Boostrap?
27602 xtype : 'ComboBox',
27603 allowBlank : false,
27604 displayField : 'val',
27607 triggerAction : 'all',
27609 valueField : 'val',
27613 select : function (combo, r, index)
27615 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27617 b.width = r.get('val');
27620 toolbar.editorcore.onEditorEvent();
27625 xtype : 'SimpleStore',
27637 xtype : 'TextItem',
27638 text : "Columns: ",
27639 xns : rooui.Toolbar //Boostrap?
27646 click : function (_self, e)
27648 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27649 block().removeColumn();
27651 toolbar.editorcore.onEditorEvent();
27654 xns : rooui.Toolbar
27660 click : function (_self, e)
27662 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27663 block().addColumn();
27665 toolbar.editorcore.onEditorEvent();
27668 xns : rooui.Toolbar
27672 xtype : 'TextItem',
27674 xns : rooui.Toolbar //Boostrap?
27681 click : function (_self, e)
27683 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
27684 block().removeRow();
27686 toolbar.editorcore.onEditorEvent();
27689 xns : rooui.Toolbar
27695 click : function (_self, e)
27699 toolbar.editorcore.onEditorEvent();
27702 xns : rooui.Toolbar
27707 text: 'Reset Column Widths',
27710 click : function (_self, e)
27712 block().resetWidths();
27714 toolbar.editorcore.onEditorEvent();
27717 xns : rooui.Toolbar
27728 * create a DomHelper friendly object - for use with
27729 * Roo.DomHelper.markup / overwrite / etc..
27730 * ?? should it be called with option to hide all editing features?
27732 toObject : function()
27737 contenteditable : 'false', // this stops cell selection from picking the table.
27738 'data-block' : 'Table',
27741 border : 'solid 1px #000', // ??? hard coded?
27742 'border-collapse' : 'collapse'
27745 { tag : 'tbody' , cn : [] }
27749 // do we have a head = not really
27751 Roo.each(this.rows, function( row ) {
27756 border : 'solid 1px #000',
27762 ret.cn[0].cn.push(tr);
27763 // does the row have any properties? ?? height?
27765 Roo.each(row, function( cell ) {
27769 contenteditable : 'true',
27770 'data-block' : 'Td',
27774 if (cell.colspan > 1) {
27775 td.colspan = cell.colspan ;
27776 nc += cell.colspan;
27780 if (cell.rowspan > 1) {
27781 td.rowspan = cell.rowspan ;
27790 ncols = Math.max(nc, ncols);
27794 // add the header row..
27803 readElement : function(node)
27805 node = node ? node : this.node ;
27806 this.width = this.getVal(node, true, 'style', 'width') || '100%';
27810 var trs = Array.from(node.rows);
27811 trs.forEach(function(tr) {
27813 this.rows.push(row);
27817 Array.from(tr.cells).forEach(function(td) {
27820 colspan : td.hasAttribute('colspan') ? td.getAttribute('colspan')*1 : 1,
27821 rowspan : td.hasAttribute('rowspan') ? td.getAttribute('rowspan')*1 : 1,
27822 style : td.hasAttribute('style') ? td.getAttribute('style') : '',
27823 html : td.innerHTML
27825 no_column += add.colspan;
27832 this.no_col = Math.max(this.no_col, no_column);
27839 normalizeRows: function()
27843 this.rows.forEach(function(row) {
27846 row = this.normalizeRow(row);
27848 row.forEach(function(c) {
27849 while (typeof(ret[rid][cid]) != 'undefined') {
27852 if (typeof(ret[rid]) == 'undefined') {
27858 if (c.rowspan < 2) {
27862 for(var i = 1 ;i < c.rowspan; i++) {
27863 if (typeof(ret[rid+i]) == 'undefined') {
27866 ret[rid+i][cid] = c;
27874 normalizeRow: function(row)
27877 row.forEach(function(c) {
27878 if (c.colspan < 2) {
27882 for(var i =0 ;i < c.colspan; i++) {
27890 deleteColumn : function(sel)
27892 if (!sel || sel.type != 'col') {
27895 if (this.no_col < 2) {
27899 this.rows.forEach(function(row) {
27900 var cols = this.normalizeRow(row);
27901 var col = cols[sel.col];
27902 if (col.colspan > 1) {
27912 removeColumn : function()
27914 this.deleteColumn({
27916 col : this.no_col-1
27918 this.updateElement();
27922 addColumn : function()
27925 this.rows.forEach(function(row) {
27926 row.push(this.emptyCell());
27929 this.updateElement();
27932 deleteRow : function(sel)
27934 if (!sel || sel.type != 'row') {
27938 if (this.no_row < 2) {
27942 var rows = this.normalizeRows();
27945 rows[sel.row].forEach(function(col) {
27946 if (col.rowspan > 1) {
27949 col.remove = 1; // flage it as removed.
27954 this.rows.forEach(function(row) {
27956 row.forEach(function(c) {
27957 if (typeof(c.remove) == 'undefined') {
27962 if (newrow.length > 0) {
27966 this.rows = newrows;
27971 this.updateElement();
27974 removeRow : function()
27978 row : this.no_row-1
27984 addRow : function()
27988 for (var i = 0; i < this.no_col; i++ ) {
27990 row.push(this.emptyCell());
27993 this.rows.push(row);
27994 this.updateElement();
27998 // the default cell object... at present...
27999 emptyCell : function() {
28000 return (new Roo.htmleditor.BlockTd({})).toObject();
28005 removeNode : function()
28012 resetWidths : function()
28014 Array.from(this.node.getElementsByTagName('td')).forEach(function(n) {
28015 var nn = Roo.htmleditor.Block.factory(n);
28017 nn.updateElement(n);
28030 * since selections really work on the table cell, then editing really should work from there
28032 * The original plan was to support merging etc... - but that may not be needed yet..
28034 * So this simple version will support:
28036 * adjust the width +/-
28037 * reset the width...
28046 * @class Roo.htmleditor.BlockTable
28047 * Block that manages a table
28050 * Create a new Filter.
28051 * @param {Object} config Configuration options
28054 Roo.htmleditor.BlockTd = function(cfg)
28057 this.readElement(cfg.node);
28058 this.updateElement(cfg.node);
28060 Roo.apply(this, cfg);
28065 Roo.extend(Roo.htmleditor.BlockTd, Roo.htmleditor.Block, {
28070 textAlign : 'left',
28077 // used by context menu
28078 friendly_name : 'Table Cell',
28079 deleteTitle : false, // use our customer delete
28081 // context menu is drawn once..
28083 contextMenu : function(toolbar)
28086 var cell = function() {
28087 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode);
28090 var table = function() {
28091 return Roo.htmleditor.Block.factory(toolbar.tb.selectedNode.closest('table'));
28095 var saveSel = function()
28097 lr = toolbar.editorcore.getSelection().getRangeAt(0);
28099 var restoreSel = function()
28103 toolbar.editorcore.focus();
28104 var cr = toolbar.editorcore.getSelection();
28105 cr.removeAllRanges();
28107 toolbar.editorcore.onEditorEvent();
28108 }).defer(10, this);
28114 var rooui = typeof(Roo.bootstrap) == 'undefined' ? Roo : Roo.bootstrap;
28116 var syncValue = toolbar.editorcore.syncValue;
28123 text : 'Edit Table',
28125 click : function() {
28126 var t = toolbar.tb.selectedNode.closest('table');
28127 toolbar.editorcore.selectNode(t);
28128 toolbar.editorcore.onEditorEvent();
28137 xtype : 'TextItem',
28138 text : "Column Width: ",
28139 xns : rooui.Toolbar
28146 click : function (_self, e)
28148 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28149 cell().shrinkColumn();
28151 toolbar.editorcore.onEditorEvent();
28154 xns : rooui.Toolbar
28160 click : function (_self, e)
28162 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28163 cell().growColumn();
28165 toolbar.editorcore.onEditorEvent();
28168 xns : rooui.Toolbar
28172 xtype : 'TextItem',
28173 text : "Vertical Align: ",
28174 xns : rooui.Toolbar //Boostrap?
28177 xtype : 'ComboBox',
28178 allowBlank : false,
28179 displayField : 'val',
28182 triggerAction : 'all',
28184 valueField : 'val',
28188 select : function (combo, r, index)
28190 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28192 b.valign = r.get('val');
28195 toolbar.editorcore.onEditorEvent();
28200 xtype : 'SimpleStore',
28204 ['bottom'] // there are afew more...
28212 xtype : 'TextItem',
28213 text : "Merge Cells: ",
28214 xns : rooui.Toolbar
28223 click : function (_self, e)
28225 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28226 cell().mergeRight();
28227 //block().growColumn();
28229 toolbar.editorcore.onEditorEvent();
28232 xns : rooui.Toolbar
28239 click : function (_self, e)
28241 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28242 cell().mergeBelow();
28243 //block().growColumn();
28245 toolbar.editorcore.onEditorEvent();
28248 xns : rooui.Toolbar
28251 xtype : 'TextItem',
28253 xns : rooui.Toolbar
28261 click : function (_self, e)
28263 //toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28266 toolbar.editorcore.selectNode(toolbar.tb.selectedNode);
28267 toolbar.editorcore.onEditorEvent();
28271 xns : rooui.Toolbar
28275 xns : rooui.Toolbar
28284 xns : rooui.Toolbar,
28293 click : function (_self, e)
28297 cell().deleteColumn();
28299 toolbar.editorcore.selectNode(t.node);
28300 toolbar.editorcore.onEditorEvent();
28309 click : function (_self, e)
28312 cell().deleteRow();
28315 toolbar.editorcore.selectNode(t.node);
28316 toolbar.editorcore.onEditorEvent();
28323 xtype : 'Separator',
28330 click : function (_self, e)
28333 var nn = t.node.nextSibling || t.node.previousSibling;
28334 t.node.parentNode.removeChild(t.node);
28336 toolbar.editorcore.selectNode(nn, true);
28338 toolbar.editorcore.onEditorEvent();
28348 // align... << fixme
28356 * create a DomHelper friendly object - for use with
28357 * Roo.DomHelper.markup / overwrite / etc..
28358 * ?? should it be called with option to hide all editing features?
28361 * create a DomHelper friendly object - for use with
28362 * Roo.DomHelper.markup / overwrite / etc..
28363 * ?? should it be called with option to hide all editing features?
28365 toObject : function()
28370 contenteditable : 'true', // this stops cell selection from picking the table.
28371 'data-block' : 'Td',
28372 valign : this.valign,
28374 'text-align' : this.textAlign,
28375 border : 'solid 1px rgb(0, 0, 0)', // ??? hard coded?
28376 'border-collapse' : 'collapse',
28377 padding : '6px', // 8 for desktop / 4 for mobile
28378 'vertical-align': this.valign
28382 if (this.width != '') {
28383 ret.width = this.width;
28384 ret.style.width = this.width;
28388 if (this.colspan > 1) {
28389 ret.colspan = this.colspan ;
28391 if (this.rowspan > 1) {
28392 ret.rowspan = this.rowspan ;
28401 readElement : function(node)
28403 node = node ? node : this.node ;
28404 this.width = node.style.width;
28405 this.colspan = Math.max(1,1*node.getAttribute('colspan'));
28406 this.rowspan = Math.max(1,1*node.getAttribute('rowspan'));
28407 this.html = node.innerHTML;
28412 // the default cell object... at present...
28413 emptyCell : function() {
28417 textAlign : 'left',
28418 html : " " // is this going to be editable now?
28423 removeNode : function()
28425 return this.node.closest('table');
28433 toTableArray : function()
28436 var tab = this.node.closest('tr').closest('table');
28437 Array.from(tab.rows).forEach(function(r, ri){
28441 this.colWidths = [];
28442 var all_auto = true;
28443 Array.from(tab.rows).forEach(function(r, ri){
28446 Array.from(r.cells).forEach(function(ce, ci){
28451 colspan : ce.colSpan,
28452 rowspan : ce.rowSpan
28454 if (ce.isEqualNode(this.node)) {
28457 // if we have been filled up by a row?
28458 if (typeof(ret[rn][cn]) != 'undefined') {
28459 while(typeof(ret[rn][cn]) != 'undefined') {
28465 if (typeof(this.colWidths[cn]) == 'undefined') {
28466 this.colWidths[cn] = ce.style.width;
28467 if (this.colWidths[cn] != '') {
28473 if (c.colspan < 2 && c.rowspan < 2 ) {
28478 for(var j = 0; j < c.rowspan; j++) {
28479 if (typeof(ret[rn+j]) == 'undefined') {
28480 continue; // we have a problem..
28483 for(var i = 0; i < c.colspan; i++) {
28484 ret[rn+j][cn+i] = c;
28493 // initalize widths.?
28494 // either all widths or no widths..
28496 this.colWidths[0] = false; // no widths flag.
28507 mergeRight: function()
28510 // get the contents of the next cell along..
28511 var tr = this.node.closest('tr');
28512 var i = Array.prototype.indexOf.call(tr.childNodes, this.node);
28513 if (i >= tr.childNodes.length - 1) {
28514 return; // no cells on right to merge with.
28516 var table = this.toTableArray();
28518 if (typeof(table[this.cellData.row][this.cellData.col+this.cellData.colspan]) == 'undefined') {
28519 return; // nothing right?
28521 var rc = table[this.cellData.row][this.cellData.col+this.cellData.colspan];
28522 // right cell - must be same rowspan and on the same row.
28523 if (rc.rowspan != this.cellData.rowspan || rc.row != this.cellData.row) {
28524 return; // right hand side is not same rowspan.
28529 this.node.innerHTML += ' ' + rc.cell.innerHTML;
28530 tr.removeChild(rc.cell);
28531 this.colspan += rc.colspan;
28532 this.node.setAttribute('colspan', this.colspan);
28537 mergeBelow : function()
28539 var table = this.toTableArray();
28540 if (typeof(table[this.cellData.row+this.cellData.rowspan]) == 'undefined') {
28541 return; // no row below
28543 if (typeof(table[this.cellData.row+this.cellData.rowspan][this.cellData.col]) == 'undefined') {
28544 return; // nothing right?
28546 var rc = table[this.cellData.row+this.cellData.rowspan][this.cellData.col];
28548 if (rc.colspan != this.cellData.colspan || rc.col != this.cellData.col) {
28549 return; // right hand side is not same rowspan.
28551 this.node.innerHTML = this.node.innerHTML + rc.cell.innerHTML ;
28552 rc.cell.parentNode.removeChild(rc.cell);
28553 this.rowspan += rc.rowspan;
28554 this.node.setAttribute('rowspan', this.rowspan);
28559 if (this.node.rowSpan < 2 && this.node.colSpan < 2) {
28562 var table = this.toTableArray();
28563 var cd = this.cellData;
28567 for(var r = cd.row; r < cd.row + cd.rowspan; r++) {
28571 for(var c = cd.col; c < cd.col + cd.colspan; c++) {
28572 if (r == cd.row && c == cd.col) {
28573 this.node.removeAttribute('rowspan');
28574 this.node.removeAttribute('colspan');
28578 var ntd = this.node.cloneNode(); // which col/row should be 0..
28579 ntd.removeAttribute('id'); //
28580 //ntd.style.width = '';
28581 ntd.innerHTML = '';
28582 table[r][c] = { cell : ntd, col : c, row: r , colspan : 1 , rowspan : 1 };
28586 this.redrawAllCells(table);
28594 redrawAllCells: function(table)
28598 var tab = this.node.closest('tr').closest('table');
28599 var ctr = tab.rows[0].parentNode;
28600 Array.from(tab.rows).forEach(function(r, ri){
28602 Array.from(r.cells).forEach(function(ce, ci){
28603 ce.parentNode.removeChild(ce);
28605 r.parentNode.removeChild(r);
28607 for(var r = 0 ; r < table.length; r++) {
28608 var re = tab.rows[r];
28610 var re = tab.ownerDocument.createElement('tr');
28611 ctr.appendChild(re);
28612 for(var c = 0 ; c < table[r].length; c++) {
28613 if (table[r][c].cell === false) {
28617 re.appendChild(table[r][c].cell);
28619 table[r][c].cell = false;
28624 updateWidths : function(table)
28626 for(var r = 0 ; r < table.length; r++) {
28628 for(var c = 0 ; c < table[r].length; c++) {
28629 if (table[r][c].cell === false) {
28633 if (this.colWidths[0] != false && table[r][c].colspan < 2) {
28634 var el = Roo.htmleditor.Block.factory(table[r][c].cell);
28635 el.width = Math.floor(this.colWidths[c]) +'%';
28636 el.updateElement(el.node);
28638 table[r][c].cell = false; // done
28642 normalizeWidths : function(table)
28645 if (this.colWidths[0] === false) {
28646 var nw = 100.0 / this.colWidths.length;
28647 this.colWidths.forEach(function(w,i) {
28648 this.colWidths[i] = nw;
28653 var t = 0, missing = [];
28655 this.colWidths.forEach(function(w,i) {
28657 this.colWidths[i] = this.colWidths[i] == '' ? 0 : (this.colWidths[i]+'').replace(/[^0-9]+/g,'')*1;
28658 var add = this.colWidths[i];
28667 var nc = this.colWidths.length;
28668 if (missing.length) {
28669 var mult = (nc - missing.length) / (1.0 * nc);
28671 var ew = (100 -t) / (1.0 * missing.length);
28672 this.colWidths.forEach(function(w,i) {
28674 this.colWidths[i] = w * mult;
28678 this.colWidths[i] = ew;
28680 // have to make up numbers..
28683 // now we should have all the widths..
28688 shrinkColumn : function()
28690 var table = this.toTableArray();
28691 this.normalizeWidths(table);
28692 var col = this.cellData.col;
28693 var nw = this.colWidths[col] * 0.8;
28697 var otherAdd = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
28698 this.colWidths.forEach(function(w,i) {
28700 this.colWidths[i] = nw;
28703 this.colWidths[i] += otherAdd
28705 this.updateWidths(table);
28708 growColumn : function()
28710 var table = this.toTableArray();
28711 this.normalizeWidths(table);
28712 var col = this.cellData.col;
28713 var nw = this.colWidths[col] * 1.2;
28717 var otherSub = (this.colWidths[col] * 0.2) / (this.colWidths.length -1);
28718 this.colWidths.forEach(function(w,i) {
28720 this.colWidths[i] = nw;
28723 this.colWidths[i] -= otherSub
28725 this.updateWidths(table);
28728 deleteRow : function()
28730 // delete this rows 'tr'
28731 // if any of the cells in this row have a rowspan > 1 && row!= this row..
28732 // then reduce the rowspan.
28733 var table = this.toTableArray();
28734 // this.cellData.row;
28735 for (var i =0;i< table[this.cellData.row].length ; i++) {
28736 var c = table[this.cellData.row][i];
28737 if (c.row != this.cellData.row) {
28740 c.cell.setAttribute('rowspan', c.rowspan);
28743 if (c.rowspan > 1) {
28745 c.cell.setAttribute('rowspan', c.rowspan);
28748 table.splice(this.cellData.row,1);
28749 this.redrawAllCells(table);
28752 deleteColumn : function()
28754 var table = this.toTableArray();
28756 for (var i =0;i< table.length ; i++) {
28757 var c = table[i][this.cellData.col];
28758 if (c.col != this.cellData.col) {
28759 table[i][this.cellData.col].colspan--;
28760 } else if (c.colspan > 1) {
28762 c.cell.setAttribute('colspan', c.colspan);
28764 table[i].splice(this.cellData.col,1);
28767 this.redrawAllCells(table);
28775 //<script type="text/javascript">
28778 * Based Ext JS Library 1.1.1
28779 * Copyright(c) 2006-2007, Ext JS, LLC.
28785 * @class Roo.HtmlEditorCore
28786 * @extends Roo.Component
28787 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
28789 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
28792 Roo.HtmlEditorCore = function(config){
28795 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
28800 * @event initialize
28801 * Fires when the editor is fully initialized (including the iframe)
28802 * @param {Roo.HtmlEditorCore} this
28807 * Fires when the editor is first receives the focus. Any insertion must wait
28808 * until after this event.
28809 * @param {Roo.HtmlEditorCore} this
28813 * @event beforesync
28814 * Fires before the textarea is updated with content from the editor iframe. Return false
28815 * to cancel the sync.
28816 * @param {Roo.HtmlEditorCore} this
28817 * @param {String} html
28821 * @event beforepush
28822 * Fires before the iframe editor is updated with content from the textarea. Return false
28823 * to cancel the push.
28824 * @param {Roo.HtmlEditorCore} this
28825 * @param {String} html
28830 * Fires when the textarea is updated with content from the editor iframe.
28831 * @param {Roo.HtmlEditorCore} this
28832 * @param {String} html
28837 * Fires when the iframe editor is updated with content from the textarea.
28838 * @param {Roo.HtmlEditorCore} this
28839 * @param {String} html
28844 * @event editorevent
28845 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
28846 * @param {Roo.HtmlEditorCore} this
28853 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
28855 // defaults : white / black...
28856 this.applyBlacklists();
28863 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
28867 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
28873 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
28878 * @cfg {Number} height (in pixels)
28882 * @cfg {Number} width (in pixels)
28886 * @cfg {boolean} autoClean - default true - loading and saving will remove quite a bit of formating,
28887 * if you are doing an email editor, this probably needs disabling, it's designed
28892 * @cfg {boolean} enableBlocks - default true - if the block editor (table and figure should be enabled)
28894 enableBlocks : true,
28896 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
28899 stylesheets: false,
28901 * @cfg {String} language default en - language of text (usefull for rtl languages)
28907 * @cfg {boolean} allowComments - default false - allow comments in HTML source
28908 * - by default they are stripped - if you are editing email you may need this.
28910 allowComments: false,
28914 // private properties
28915 validationEvent : false,
28917 initialized : false,
28919 sourceEditMode : false,
28920 onFocus : Roo.emptyFn,
28922 hideMode:'offsets',
28926 // blacklist + whitelisted elements..
28933 undoManager : false,
28935 * Protected method that will not generally be called directly. It
28936 * is called when the editor initializes the iframe with HTML contents. Override this method if you
28937 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
28939 getDocMarkup : function(){
28943 // inherit styels from page...??
28944 if (this.stylesheets === false) {
28946 Roo.get(document.head).select('style').each(function(node) {
28947 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28950 Roo.get(document.head).select('link').each(function(node) {
28951 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
28954 } else if (!this.stylesheets.length) {
28956 st = '<style type="text/css">' +
28957 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28960 for (var i in this.stylesheets) {
28961 if (typeof(this.stylesheets[i]) != 'string') {
28964 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
28969 st += '<style type="text/css">' +
28970 'IMG { cursor: pointer } ' +
28973 st += '<meta name="google" content="notranslate">';
28975 var cls = 'notranslate roo-htmleditor-body';
28977 if(this.bodyCls.length){
28978 cls += ' ' + this.bodyCls;
28981 return '<html class="notranslate" translate="no"><head>' + st +
28982 //<style type="text/css">' +
28983 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
28985 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
28989 onRender : function(ct, position)
28992 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
28993 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
28996 this.el.dom.style.border = '0 none';
28997 this.el.dom.setAttribute('tabIndex', -1);
28998 this.el.addClass('x-hidden hide');
29002 if(Roo.isIE){ // fix IE 1px bogus margin
29003 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
29007 this.frameId = Roo.id();
29011 var iframe = this.owner.wrap.createChild({
29013 cls: 'form-control', // bootstrap..
29015 name: this.frameId,
29016 frameBorder : 'no',
29017 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
29022 this.iframe = iframe.dom;
29024 this.assignDocWin();
29026 this.doc.designMode = 'on';
29029 this.doc.write(this.getDocMarkup());
29033 var task = { // must defer to wait for browser to be ready
29035 //console.log("run task?" + this.doc.readyState);
29036 this.assignDocWin();
29037 if(this.doc.body || this.doc.readyState == 'complete'){
29039 this.doc.designMode="on";
29044 Roo.TaskMgr.stop(task);
29045 this.initEditor.defer(10, this);
29052 Roo.TaskMgr.start(task);
29057 onResize : function(w, h)
29059 Roo.log('resize: ' +w + ',' + h );
29060 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
29064 if(typeof w == 'number'){
29066 this.iframe.style.width = w + 'px';
29068 if(typeof h == 'number'){
29070 this.iframe.style.height = h + 'px';
29072 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
29079 * Toggles the editor between standard and source edit mode.
29080 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
29082 toggleSourceEdit : function(sourceEditMode){
29084 this.sourceEditMode = sourceEditMode === true;
29086 if(this.sourceEditMode){
29088 Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']); //FIXME - what's the BS styles for these
29091 Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
29092 //this.iframe.className = '';
29095 //this.setSize(this.owner.wrap.getSize());
29096 //this.fireEvent('editmodechange', this, this.sourceEditMode);
29103 * Protected method that will not generally be called directly. If you need/want
29104 * custom HTML cleanup, this is the method you should override.
29105 * @param {String} html The HTML to be cleaned
29106 * return {String} The cleaned HTML
29108 cleanHtml : function(html)
29110 html = String(html);
29111 if(html.length > 5){
29112 if(Roo.isSafari){ // strip safari nonsense
29113 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
29116 if(html == ' '){
29123 * HTML Editor -> Textarea
29124 * Protected method that will not generally be called directly. Syncs the contents
29125 * of the editor iframe with the textarea.
29127 syncValue : function()
29129 //Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
29130 if(this.initialized){
29132 if (this.undoManager) {
29133 this.undoManager.addEvent();
29137 var bd = (this.doc.body || this.doc.documentElement);
29140 var sel = this.win.getSelection();
29142 var div = document.createElement('div');
29143 div.innerHTML = bd.innerHTML;
29144 var gtx = div.getElementsByClassName('gtx-trans-icon'); // google translate - really annoying and difficult to get rid of.
29145 if (gtx.length > 0) {
29146 var rm = gtx.item(0).parentNode;
29147 rm.parentNode.removeChild(rm);
29151 if (this.enableBlocks) {
29152 new Roo.htmleditor.FilterBlock({ node : div });
29155 var tidy = new Roo.htmleditor.TidySerializer({
29158 var html = tidy.serialize(div);
29162 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
29163 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
29165 html = '<div style="'+m[0]+'">' + html + '</div>';
29168 html = this.cleanHtml(html);
29169 // fix up the special chars.. normaly like back quotes in word...
29170 // however we do not want to do this with chinese..
29171 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
29173 var cc = match.charCodeAt();
29175 // Get the character value, handling surrogate pairs
29176 if (match.length == 2) {
29177 // It's a surrogate pair, calculate the Unicode code point
29178 var high = match.charCodeAt(0) - 0xD800;
29179 var low = match.charCodeAt(1) - 0xDC00;
29180 cc = (high * 0x400) + low + 0x10000;
29182 (cc >= 0x4E00 && cc < 0xA000 ) ||
29183 (cc >= 0x3400 && cc < 0x4E00 ) ||
29184 (cc >= 0xf900 && cc < 0xfb00 )
29189 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
29190 return "&#" + cc + ";";
29197 if(this.owner.fireEvent('beforesync', this, html) !== false){
29198 this.el.dom.value = html;
29199 this.owner.fireEvent('sync', this, html);
29205 * TEXTAREA -> EDITABLE
29206 * Protected method that will not generally be called directly. Pushes the value of the textarea
29207 * into the iframe editor.
29209 pushValue : function()
29211 //Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
29212 if(this.initialized){
29213 var v = this.el.dom.value.trim();
29216 if(this.owner.fireEvent('beforepush', this, v) !== false){
29217 var d = (this.doc.body || this.doc.documentElement);
29220 this.el.dom.value = d.innerHTML;
29221 this.owner.fireEvent('push', this, v);
29223 if (this.autoClean) {
29224 new Roo.htmleditor.FilterParagraph({node : this.doc.body}); // paragraphs
29225 new Roo.htmleditor.FilterSpan({node : this.doc.body}); // empty spans
29227 if (this.enableBlocks) {
29228 Roo.htmleditor.Block.initAll(this.doc.body);
29231 this.updateLanguage();
29233 var lc = this.doc.body.lastChild;
29234 if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
29235 // add an extra line at the end.
29236 this.doc.body.appendChild(this.doc.createElement('br'));
29244 deferFocus : function(){
29245 this.focus.defer(10, this);
29249 focus : function(){
29250 if(this.win && !this.sourceEditMode){
29257 assignDocWin: function()
29259 var iframe = this.iframe;
29262 this.doc = iframe.contentWindow.document;
29263 this.win = iframe.contentWindow;
29265 // if (!Roo.get(this.frameId)) {
29268 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29269 // this.win = Roo.get(this.frameId).dom.contentWindow;
29271 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
29275 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
29276 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
29281 initEditor : function(){
29282 //console.log("INIT EDITOR");
29283 this.assignDocWin();
29287 this.doc.designMode="on";
29289 this.doc.write(this.getDocMarkup());
29292 var dbody = (this.doc.body || this.doc.documentElement);
29293 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
29294 // this copies styles from the containing element into thsi one..
29295 // not sure why we need all of this..
29296 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
29298 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
29299 //ss['background-attachment'] = 'fixed'; // w3c
29300 dbody.bgProperties = 'fixed'; // ie
29301 dbody.setAttribute("translate", "no");
29303 //Roo.DomHelper.applyStyles(dbody, ss);
29304 Roo.EventManager.on(this.doc, {
29306 'mouseup': this.onEditorEvent,
29307 'dblclick': this.onEditorEvent,
29308 'click': this.onEditorEvent,
29309 'keyup': this.onEditorEvent,
29314 Roo.EventManager.on(this.doc, {
29315 'paste': this.onPasteEvent,
29319 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
29322 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
29323 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
29325 this.initialized = true;
29328 // initialize special key events - enter
29329 new Roo.htmleditor.KeyEnter({core : this});
29333 this.owner.fireEvent('initialize', this);
29336 // this is to prevent a href clicks resulting in a redirect?
29338 onPasteEvent : function(e,v)
29340 // I think we better assume paste is going to be a dirty load of rubish from word..
29342 // even pasting into a 'email version' of this widget will have to clean up that mess.
29343 var cd = (e.browserEvent.clipboardData || window.clipboardData);
29345 // check what type of paste - if it's an image, then handle it differently.
29346 if (cd.files && cd.files.length > 0) {
29348 var urlAPI = (window.createObjectURL && window) ||
29349 (window.URL && URL.revokeObjectURL && URL) ||
29350 (window.webkitURL && webkitURL);
29352 var url = urlAPI.createObjectURL( cd.files[0]);
29353 this.insertAtCursor('<img src=" + url + ">');
29356 if (cd.types.indexOf('text/html') < 0 ) {
29360 var html = cd.getData('text/html'); // clipboard event
29361 if (cd.types.indexOf('text/rtf') > -1) {
29362 var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
29363 images = parser.doc ? parser.doc.getElementsByType('pict') : [];
29368 images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable|footerf)/); }) // ignore headers/footers etc.
29369 .map(function(g) { return g.toDataURL(); })
29370 .filter(function(g) { return g != 'about:blank'; });
29373 html = this.cleanWordChars(html);
29375 var d = (new DOMParser().parseFromString(html, 'text/html')).body;
29378 var sn = this.getParentElement();
29379 // check if d contains a table, and prevent nesting??
29380 //Roo.log(d.getElementsByTagName('table'));
29382 //Roo.log(sn.closest('table'));
29383 if (d.getElementsByTagName('table').length && sn && sn.closest('table')) {
29384 e.preventDefault();
29385 this.insertAtCursor("You can not nest tables");
29386 //Roo.log("prevent?"); // fixme -
29390 if (images.length > 0) {
29391 Roo.each(d.getElementsByTagName('img'), function(img, i) {
29392 img.setAttribute('src', images[i]);
29395 if (this.autoClean) {
29396 new Roo.htmleditor.FilterWord({ node : d });
29398 new Roo.htmleditor.FilterStyleToTag({ node : d });
29399 new Roo.htmleditor.FilterAttributes({
29401 attrib_white : ['href', 'src', 'name', 'align', 'colspan', 'rowspan', 'data-display', 'data-width'],
29402 attrib_clean : ['href', 'src' ]
29404 new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
29405 // should be fonts..
29406 new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT', 'O:P' ]} );
29407 new Roo.htmleditor.FilterParagraph({ node : d });
29408 new Roo.htmleditor.FilterSpan({ node : d });
29409 new Roo.htmleditor.FilterLongBr({ node : d });
29410 new Roo.htmleditor.FilterComment({ node : d });
29414 if (this.enableBlocks) {
29416 Array.from(d.getElementsByTagName('img')).forEach(function(img) {
29417 if (img.closest('figure')) { // assume!! that it's aready
29420 var fig = new Roo.htmleditor.BlockFigure({
29421 image_src : img.src
29423 fig.updateElement(img); // replace it..
29429 this.insertAtCursor(d.innerHTML.replace(/ /g,' '));
29430 if (this.enableBlocks) {
29431 Roo.htmleditor.Block.initAll(this.doc.body);
29435 e.preventDefault();
29437 // default behaveiour should be our local cleanup paste? (optional?)
29438 // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
29439 //this.owner.fireEvent('paste', e, v);
29442 onDestroy : function(){
29448 //for (var i =0; i < this.toolbars.length;i++) {
29449 // // fixme - ask toolbars for heights?
29450 // this.toolbars[i].onDestroy();
29453 //this.wrap.dom.innerHTML = '';
29454 //this.wrap.remove();
29459 onFirstFocus : function(){
29461 this.assignDocWin();
29462 this.undoManager = new Roo.lib.UndoManager(100,(this.doc.body || this.doc.documentElement));
29464 this.activated = true;
29467 if(Roo.isGecko){ // prevent silly gecko errors
29469 var s = this.win.getSelection();
29470 if(!s.focusNode || s.focusNode.nodeType != 3){
29471 var r = s.getRangeAt(0);
29472 r.selectNodeContents((this.doc.body || this.doc.documentElement));
29477 this.execCmd('useCSS', true);
29478 this.execCmd('styleWithCSS', false);
29481 this.owner.fireEvent('activate', this);
29485 adjustFont: function(btn){
29486 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
29487 //if(Roo.isSafari){ // safari
29490 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
29491 if(Roo.isSafari){ // safari
29492 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
29493 v = (v < 10) ? 10 : v;
29494 v = (v > 48) ? 48 : v;
29495 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
29500 v = Math.max(1, v+adjust);
29502 this.execCmd('FontSize', v );
29505 onEditorEvent : function(e)
29509 if (e && (e.ctrlKey || e.metaKey) && e.keyCode === 90) {
29510 return; // we do not handle this.. (undo manager does..)
29512 // in theory this detects if the last element is not a br, then we try and do that.
29513 // its so clicking in space at bottom triggers adding a br and moving the cursor.
29515 e.target.nodeName == 'BODY' &&
29516 e.type == "mouseup" &&
29517 this.doc.body.lastChild
29519 var lc = this.doc.body.lastChild;
29520 // gtx-trans is google translate plugin adding crap.
29521 while ((lc.nodeType == 3 && lc.nodeValue == '') || lc.id == 'gtx-trans') {
29522 lc = lc.previousSibling;
29524 if (lc.nodeType == 1 && lc.nodeName != 'BR') {
29525 // if last element is <BR> - then dont do anything.
29527 var ns = this.doc.createElement('br');
29528 this.doc.body.appendChild(ns);
29529 range = this.doc.createRange();
29530 range.setStartAfter(ns);
29531 range.collapse(true);
29532 var sel = this.win.getSelection();
29533 sel.removeAllRanges();
29534 sel.addRange(range);
29540 this.fireEditorEvent(e);
29541 // this.updateToolbar();
29542 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
29545 fireEditorEvent: function(e)
29547 this.owner.fireEvent('editorevent', this, e);
29550 insertTag : function(tg)
29552 // could be a bit smarter... -> wrap the current selected tRoo..
29553 if (tg.toLowerCase() == 'span' ||
29554 tg.toLowerCase() == 'code' ||
29555 tg.toLowerCase() == 'sup' ||
29556 tg.toLowerCase() == 'sub'
29559 range = this.createRange(this.getSelection());
29560 var wrappingNode = this.doc.createElement(tg.toLowerCase());
29561 wrappingNode.appendChild(range.extractContents());
29562 range.insertNode(wrappingNode);
29569 this.execCmd("formatblock", tg);
29570 this.undoManager.addEvent();
29573 insertText : function(txt)
29577 var range = this.createRange();
29578 range.deleteContents();
29579 //alert(Sender.getAttribute('label'));
29581 range.insertNode(this.doc.createTextNode(txt));
29582 this.undoManager.addEvent();
29588 * Executes a Midas editor command on the editor document and performs necessary focus and
29589 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
29590 * @param {String} cmd The Midas command
29591 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29593 relayCmd : function(cmd, value)
29597 case 'justifyleft':
29598 case 'justifyright':
29599 case 'justifycenter':
29600 // if we are in a cell, then we will adjust the
29601 var n = this.getParentElement();
29602 var td = n.closest('td');
29604 var bl = Roo.htmleditor.Block.factory(td);
29605 bl.textAlign = cmd.replace('justify','');
29606 bl.updateElement();
29607 this.owner.fireEvent('editorevent', this);
29610 this.execCmd('styleWithCSS', true); //
29614 // if there is no selection, then we insert, and set the curson inside it..
29615 this.execCmd('styleWithCSS', false);
29625 this.execCmd(cmd, value);
29626 this.owner.fireEvent('editorevent', this);
29627 //this.updateToolbar();
29628 this.owner.deferFocus();
29632 * Executes a Midas editor command directly on the editor document.
29633 * For visual commands, you should use {@link #relayCmd} instead.
29634 * <b>This should only be called after the editor is initialized.</b>
29635 * @param {String} cmd The Midas command
29636 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
29638 execCmd : function(cmd, value){
29639 this.doc.execCommand(cmd, false, value === undefined ? null : value);
29646 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
29648 * @param {String} text | dom node..
29650 insertAtCursor : function(text)
29653 if(!this.activated){
29657 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
29661 // from jquery ui (MIT licenced)
29663 var win = this.win;
29665 if (win.getSelection && win.getSelection().getRangeAt) {
29667 // delete the existing?
29669 this.createRange(this.getSelection()).deleteContents();
29670 range = win.getSelection().getRangeAt(0);
29671 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
29672 range.insertNode(node);
29673 range = range.cloneRange();
29674 range.collapse(false);
29676 win.getSelection().removeAllRanges();
29677 win.getSelection().addRange(range);
29681 } else if (win.document.selection && win.document.selection.createRange) {
29682 // no firefox support
29683 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29684 win.document.selection.createRange().pasteHTML(txt);
29687 // no firefox support
29688 var txt = typeof(text) == 'string' ? text : text.outerHTML;
29689 this.execCmd('InsertHTML', txt);
29697 mozKeyPress : function(e){
29699 var c = e.getCharCode(), cmd;
29702 c = String.fromCharCode(c).toLowerCase();
29716 // this.cleanUpPaste.defer(100, this);
29722 this.relayCmd(cmd);
29723 //this.win.focus();
29724 //this.execCmd(cmd);
29725 //this.deferFocus();
29726 e.preventDefault();
29734 fixKeys : function(){ // load time branching for fastest keydown performance
29738 return function(e){
29739 var k = e.getKey(), r;
29742 r = this.doc.selection.createRange();
29745 r.pasteHTML('    ');
29750 /// this is handled by Roo.htmleditor.KeyEnter
29753 r = this.doc.selection.createRange();
29755 var target = r.parentElement();
29756 if(!target || target.tagName.toLowerCase() != 'li'){
29758 r.pasteHTML('<br/>');
29765 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29766 // this.cleanUpPaste.defer(100, this);
29772 }else if(Roo.isOpera){
29773 return function(e){
29774 var k = e.getKey();
29778 this.execCmd('InsertHTML','    ');
29782 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29783 // this.cleanUpPaste.defer(100, this);
29788 }else if(Roo.isSafari){
29789 return function(e){
29790 var k = e.getKey();
29794 this.execCmd('InsertText','\t');
29798 this.mozKeyPress(e);
29800 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
29801 // this.cleanUpPaste.defer(100, this);
29809 getAllAncestors: function()
29811 var p = this.getSelectedNode();
29814 a.push(p); // push blank onto stack..
29815 p = this.getParentElement();
29819 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
29823 a.push(this.doc.body);
29827 lastSelNode : false,
29830 getSelection : function()
29832 this.assignDocWin();
29833 return Roo.lib.Selection.wrap(Roo.isIE ? this.doc.selection : this.win.getSelection(), this.doc);
29836 * Select a dom node
29837 * @param {DomElement} node the node to select
29839 selectNode : function(node, collapse)
29841 var nodeRange = node.ownerDocument.createRange();
29843 nodeRange.selectNode(node);
29845 nodeRange.selectNodeContents(node);
29847 if (collapse === true) {
29848 nodeRange.collapse(true);
29851 var s = this.win.getSelection();
29852 s.removeAllRanges();
29853 s.addRange(nodeRange);
29856 getSelectedNode: function()
29858 // this may only work on Gecko!!!
29860 // should we cache this!!!!
29864 var range = this.createRange(this.getSelection()).cloneRange();
29867 var parent = range.parentElement();
29869 var testRange = range.duplicate();
29870 testRange.moveToElementText(parent);
29871 if (testRange.inRange(range)) {
29874 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
29877 parent = parent.parentElement;
29882 // is ancestor a text element.
29883 var ac = range.commonAncestorContainer;
29884 if (ac.nodeType == 3) {
29885 ac = ac.parentNode;
29888 var ar = ac.childNodes;
29891 var other_nodes = [];
29892 var has_other_nodes = false;
29893 for (var i=0;i<ar.length;i++) {
29894 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
29897 // fullly contained node.
29899 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
29904 // probably selected..
29905 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
29906 other_nodes.push(ar[i]);
29910 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
29915 has_other_nodes = true;
29917 if (!nodes.length && other_nodes.length) {
29918 nodes= other_nodes;
29920 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
29928 createRange: function(sel)
29930 // this has strange effects when using with
29931 // top toolbar - not sure if it's a great idea.
29932 //this.editor.contentWindow.focus();
29933 if (typeof sel != "undefined") {
29935 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
29937 return this.doc.createRange();
29940 return this.doc.createRange();
29943 getParentElement: function()
29946 this.assignDocWin();
29947 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
29949 var range = this.createRange(sel);
29952 var p = range.commonAncestorContainer;
29953 while (p.nodeType == 3) { // text node
29964 * Range intersection.. the hard stuff...
29968 * [ -- selected range --- ]
29972 * if end is before start or hits it. fail.
29973 * if start is after end or hits it fail.
29975 * if either hits (but other is outside. - then it's not
29981 // @see http://www.thismuchiknow.co.uk/?p=64.
29982 rangeIntersectsNode : function(range, node)
29984 var nodeRange = node.ownerDocument.createRange();
29986 nodeRange.selectNode(node);
29988 nodeRange.selectNodeContents(node);
29991 var rangeStartRange = range.cloneRange();
29992 rangeStartRange.collapse(true);
29994 var rangeEndRange = range.cloneRange();
29995 rangeEndRange.collapse(false);
29997 var nodeStartRange = nodeRange.cloneRange();
29998 nodeStartRange.collapse(true);
30000 var nodeEndRange = nodeRange.cloneRange();
30001 nodeEndRange.collapse(false);
30003 return rangeStartRange.compareBoundaryPoints(
30004 Range.START_TO_START, nodeEndRange) == -1 &&
30005 rangeEndRange.compareBoundaryPoints(
30006 Range.START_TO_START, nodeStartRange) == 1;
30010 rangeCompareNode : function(range, node)
30012 var nodeRange = node.ownerDocument.createRange();
30014 nodeRange.selectNode(node);
30016 nodeRange.selectNodeContents(node);
30020 range.collapse(true);
30022 nodeRange.collapse(true);
30024 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
30025 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
30027 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
30029 var nodeIsBefore = ss == 1;
30030 var nodeIsAfter = ee == -1;
30032 if (nodeIsBefore && nodeIsAfter) {
30035 if (!nodeIsBefore && nodeIsAfter) {
30036 return 1; //right trailed.
30039 if (nodeIsBefore && !nodeIsAfter) {
30040 return 2; // left trailed.
30046 cleanWordChars : function(input) {// change the chars to hex code
30049 [ 8211, "–" ],
30050 [ 8212, "—" ],
30058 var output = input;
30059 Roo.each(swapCodes, function(sw) {
30060 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
30062 output = output.replace(swapper, sw[1]);
30072 cleanUpChild : function (node)
30075 new Roo.htmleditor.FilterComment({node : node});
30076 new Roo.htmleditor.FilterAttributes({
30078 attrib_black : this.ablack,
30079 attrib_clean : this.aclean,
30080 style_white : this.cwhite,
30081 style_black : this.cblack
30083 new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
30084 new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
30090 * Clean up MS wordisms...
30091 * @deprecated - use filter directly
30093 cleanWord : function(node)
30095 new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
30102 * @deprecated - use filters
30104 cleanTableWidths : function(node)
30106 new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
30113 applyBlacklists : function()
30115 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
30116 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
30118 this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean : Roo.HtmlEditorCore.aclean;
30119 this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack : Roo.HtmlEditorCore.ablack;
30120 this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove : Roo.HtmlEditorCore.tag_remove;
30124 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
30125 if (b.indexOf(tag) > -1) {
30128 this.white.push(tag);
30132 Roo.each(w, function(tag) {
30133 if (b.indexOf(tag) > -1) {
30136 if (this.white.indexOf(tag) > -1) {
30139 this.white.push(tag);
30144 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
30145 if (w.indexOf(tag) > -1) {
30148 this.black.push(tag);
30152 Roo.each(b, function(tag) {
30153 if (w.indexOf(tag) > -1) {
30156 if (this.black.indexOf(tag) > -1) {
30159 this.black.push(tag);
30164 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
30165 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
30169 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
30170 if (b.indexOf(tag) > -1) {
30173 this.cwhite.push(tag);
30177 Roo.each(w, function(tag) {
30178 if (b.indexOf(tag) > -1) {
30181 if (this.cwhite.indexOf(tag) > -1) {
30184 this.cwhite.push(tag);
30189 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
30190 if (w.indexOf(tag) > -1) {
30193 this.cblack.push(tag);
30197 Roo.each(b, function(tag) {
30198 if (w.indexOf(tag) > -1) {
30201 if (this.cblack.indexOf(tag) > -1) {
30204 this.cblack.push(tag);
30209 setStylesheets : function(stylesheets)
30211 if(typeof(stylesheets) == 'string'){
30212 Roo.get(this.iframe.contentDocument.head).createChild({
30214 rel : 'stylesheet',
30223 Roo.each(stylesheets, function(s) {
30228 Roo.get(_this.iframe.contentDocument.head).createChild({
30230 rel : 'stylesheet',
30240 updateLanguage : function()
30242 if (!this.iframe || !this.iframe.contentDocument) {
30245 Roo.get(this.iframe.contentDocument.body).attr("lang", this.language);
30249 removeStylesheets : function()
30253 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
30258 setStyle : function(style)
30260 Roo.get(this.iframe.contentDocument.head).createChild({
30269 // hide stuff that is not compatible
30283 * @event specialkey
30287 * @cfg {String} fieldClass @hide
30290 * @cfg {String} focusClass @hide
30293 * @cfg {String} autoCreate @hide
30296 * @cfg {String} inputType @hide
30299 * @cfg {String} invalidClass @hide
30302 * @cfg {String} invalidText @hide
30305 * @cfg {String} msgFx @hide
30308 * @cfg {String} validateOnBlur @hide
30312 Roo.HtmlEditorCore.white = [
30313 'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
30315 'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD', 'DIR', 'DIV',
30316 'DL', 'DT', 'H1', 'H2', 'H3', 'H4',
30317 'H5', 'H6', 'HR', 'ISINDEX', 'LISTING', 'MARQUEE',
30318 'MENU', 'MULTICOL', 'OL', 'P', 'PLAINTEXT', 'PRE',
30319 'TABLE', 'UL', 'XMP',
30321 'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH',
30324 'DIR', 'MENU', 'OL', 'UL', 'DL',
30330 Roo.HtmlEditorCore.black = [
30331 // 'embed', 'object', // enable - backend responsiblity to clean thiese
30333 'BASE', 'BASEFONT', 'BGSOUND', 'BLINK', 'BODY',
30334 'FRAME', 'FRAMESET', 'HEAD', 'HTML', 'ILAYER',
30335 'IFRAME', 'LAYER', 'LINK', 'META', 'OBJECT',
30336 'SCRIPT', 'STYLE' ,'TITLE', 'XML',
30337 //'FONT' // CLEAN LATER..
30338 'COLGROUP', 'COL' // messy tables.
30342 Roo.HtmlEditorCore.clean = [ // ?? needed???
30343 'SCRIPT', 'STYLE', 'TITLE', 'XML'
30345 Roo.HtmlEditorCore.tag_remove = [
30350 Roo.HtmlEditorCore.ablack = [
30354 Roo.HtmlEditorCore.aclean = [
30355 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
30359 Roo.HtmlEditorCore.pwhite= [
30360 'http', 'https', 'mailto'
30363 // white listed style attributes.
30364 Roo.HtmlEditorCore.cwhite= [
30365 // 'text-align', /// default is to allow most things..
30371 // black listed style attributes.
30372 Roo.HtmlEditorCore.cblack= [
30373 // 'font-size' -- this can be set by the project
30387 * @class Roo.bootstrap.form.HtmlEditor
30388 * @extends Roo.bootstrap.form.TextArea
30389 * Bootstrap HtmlEditor class
30392 * Create a new HtmlEditor
30393 * @param {Object} config The config object
30396 Roo.bootstrap.form.HtmlEditor = function(config){
30397 Roo.bootstrap.form.HtmlEditor.superclass.constructor.call(this, config);
30398 if (!this.toolbars) {
30399 this.toolbars = [];
30402 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
30405 * @event initialize
30406 * Fires when the editor is fully initialized (including the iframe)
30407 * @param {HtmlEditor} this
30412 * Fires when the editor is first receives the focus. Any insertion must wait
30413 * until after this event.
30414 * @param {HtmlEditor} this
30418 * @event beforesync
30419 * Fires before the textarea is updated with content from the editor iframe. Return false
30420 * to cancel the sync.
30421 * @param {HtmlEditor} this
30422 * @param {String} html
30426 * @event beforepush
30427 * Fires before the iframe editor is updated with content from the textarea. Return false
30428 * to cancel the push.
30429 * @param {HtmlEditor} this
30430 * @param {String} html
30435 * Fires when the textarea is updated with content from the editor iframe.
30436 * @param {HtmlEditor} this
30437 * @param {String} html
30442 * Fires when the iframe editor is updated with content from the textarea.
30443 * @param {HtmlEditor} this
30444 * @param {String} html
30448 * @event editmodechange
30449 * Fires when the editor switches edit modes
30450 * @param {HtmlEditor} this
30451 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
30453 editmodechange: true,
30455 * @event editorevent
30456 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
30457 * @param {HtmlEditor} this
30461 * @event firstfocus
30462 * Fires when on first focus - needed by toolbars..
30463 * @param {HtmlEditor} this
30468 * Auto save the htmlEditor value as a file into Events
30469 * @param {HtmlEditor} this
30473 * @event savedpreview
30474 * preview the saved version of htmlEditor
30475 * @param {HtmlEditor} this
30482 Roo.extend(Roo.bootstrap.form.HtmlEditor, Roo.bootstrap.form.TextArea, {
30486 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
30491 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
30496 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
30501 * @cfg {Number} height (in pixels)
30505 * @cfg {Number} width (in pixels)
30510 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
30513 stylesheets: false,
30518 // private properties
30519 validationEvent : false,
30521 initialized : false,
30524 onFocus : Roo.emptyFn,
30526 hideMode:'offsets',
30528 tbContainer : false,
30532 toolbarContainer :function() {
30533 return this.wrap.select('.x-html-editor-tb',true).first();
30537 * Protected method that will not generally be called directly. It
30538 * is called when the editor creates its toolbar. Override this method if you need to
30539 * add custom toolbar buttons.
30540 * @param {HtmlEditor} editor
30542 createToolbar : function(){
30543 Roo.log('renewing');
30544 Roo.log("create toolbars");
30546 this.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard({editor: this} ) ];
30547 this.toolbars[0].render(this.toolbarContainer());
30551 // if (!editor.toolbars || !editor.toolbars.length) {
30552 // editor.toolbars = [ new Roo.bootstrap.form.HtmlEditorToolbarStandard() ]; // can be empty?
30555 // for (var i =0 ; i < editor.toolbars.length;i++) {
30556 // editor.toolbars[i] = Roo.factory(
30557 // typeof(editor.toolbars[i]) == 'string' ?
30558 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
30559 // Roo.bootstrap.form.HtmlEditor);
30560 // editor.toolbars[i].init(editor);
30566 onRender : function(ct, position)
30568 // Roo.log("Call onRender: " + this.xtype);
30570 Roo.bootstrap.form.HtmlEditor.superclass.onRender.call(this, ct, position);
30572 this.wrap = this.inputEl().wrap({
30573 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
30576 this.editorcore.onRender(ct, position);
30578 if (this.resizable) {
30579 this.resizeEl = new Roo.Resizable(this.wrap, {
30583 minHeight : this.height,
30584 height: this.height,
30585 handles : this.resizable,
30588 resize : function(r, w, h) {
30589 _t.onResize(w,h); // -something
30595 this.createToolbar(this);
30598 if(!this.width && this.resizable){
30599 this.setSize(this.wrap.getSize());
30601 if (this.resizeEl) {
30602 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
30603 // should trigger onReize..
30609 onResize : function(w, h)
30611 Roo.log('resize: ' +w + ',' + h );
30612 Roo.bootstrap.form.HtmlEditor.superclass.onResize.apply(this, arguments);
30616 if(this.inputEl() ){
30617 if(typeof w == 'number'){
30618 var aw = w - this.wrap.getFrameWidth('lr');
30619 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
30622 if(typeof h == 'number'){
30623 var tbh = -11; // fixme it needs to tool bar size!
30624 for (var i =0; i < this.toolbars.length;i++) {
30625 // fixme - ask toolbars for heights?
30626 tbh += this.toolbars[i].el.getHeight();
30627 //if (this.toolbars[i].footer) {
30628 // tbh += this.toolbars[i].footer.el.getHeight();
30636 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
30637 ah -= 5; // knock a few pixes off for look..
30638 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
30642 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
30643 this.editorcore.onResize(ew,eh);
30648 * Toggles the editor between standard and source edit mode.
30649 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
30651 toggleSourceEdit : function(sourceEditMode)
30653 this.editorcore.toggleSourceEdit(sourceEditMode);
30655 if(this.editorcore.sourceEditMode){
30656 Roo.log('editor - showing textarea');
30659 // Roo.log(this.syncValue());
30661 this.inputEl().removeClass(['hide', 'x-hidden']);
30662 this.inputEl().dom.removeAttribute('tabIndex');
30663 this.inputEl().focus();
30665 Roo.log('editor - hiding textarea');
30667 // Roo.log(this.pushValue());
30670 this.inputEl().addClass(['hide', 'x-hidden']);
30671 this.inputEl().dom.setAttribute('tabIndex', -1);
30672 //this.deferFocus();
30675 if(this.resizable){
30676 this.setSize(this.wrap.getSize());
30679 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
30682 // private (for BoxComponent)
30683 adjustSize : Roo.BoxComponent.prototype.adjustSize,
30685 // private (for BoxComponent)
30686 getResizeEl : function(){
30690 // private (for BoxComponent)
30691 getPositionEl : function(){
30696 initEvents : function(){
30697 this.originalValue = this.getValue();
30701 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30704 // markInvalid : Roo.emptyFn,
30706 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
30709 // clearInvalid : Roo.emptyFn,
30711 setValue : function(v){
30712 Roo.bootstrap.form.HtmlEditor.superclass.setValue.call(this, v);
30713 this.editorcore.pushValue();
30718 deferFocus : function(){
30719 this.focus.defer(10, this);
30723 focus : function(){
30724 this.editorcore.focus();
30730 onDestroy : function(){
30736 for (var i =0; i < this.toolbars.length;i++) {
30737 // fixme - ask toolbars for heights?
30738 this.toolbars[i].onDestroy();
30741 this.wrap.dom.innerHTML = '';
30742 this.wrap.remove();
30747 onFirstFocus : function(){
30748 //Roo.log("onFirstFocus");
30749 this.editorcore.onFirstFocus();
30750 for (var i =0; i < this.toolbars.length;i++) {
30751 this.toolbars[i].onFirstFocus();
30757 syncValue : function()
30759 this.editorcore.syncValue();
30762 pushValue : function()
30764 this.editorcore.pushValue();
30768 // hide stuff that is not compatible
30782 * @event specialkey
30786 * @cfg {String} fieldClass @hide
30789 * @cfg {String} focusClass @hide
30792 * @cfg {String} autoCreate @hide
30795 * @cfg {String} inputType @hide
30799 * @cfg {String} invalidText @hide
30802 * @cfg {String} msgFx @hide
30805 * @cfg {String} validateOnBlur @hide
30814 Roo.namespace('Roo.bootstrap.form.HtmlEditor');
30816 * @class Roo.bootstrap.form.HtmlEditorToolbarStandard
30817 * @parent Roo.bootstrap.form.HtmlEditor
30818 * @extends Roo.bootstrap.nav.Simplebar
30824 new Roo.bootstrap.form.HtmlEditor({
30827 new Roo.bootstrap.form.HtmlEditorToolbarStandard({
30828 disable : { fonts: 1 , format: 1, ..., ... , ...],
30834 * @cfg {Object} disable List of elements to disable..
30835 * @cfg {Array} btns List of additional buttons.
30839 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
30842 Roo.bootstrap.form.HtmlEditorToolbarStandard = function(config)
30845 Roo.apply(this, config);
30847 // default disabled, based on 'good practice'..
30848 this.disable = this.disable || {};
30849 Roo.applyIf(this.disable, {
30852 specialElements : true
30854 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.constructor.call(this, config);
30856 this.editor = config.editor;
30857 this.editorcore = config.editor.editorcore;
30859 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
30861 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
30862 // dont call parent... till later.
30864 Roo.extend(Roo.bootstrap.form.HtmlEditorToolbarStandard, Roo.bootstrap.nav.Simplebar, {
30869 editorcore : false,
30874 "h1","h2","h3","h4","h5","h6",
30876 "abbr", "acronym", "address", "cite", "samp", "var",
30880 onRender : function(ct, position)
30882 // Roo.log("Call onRender: " + this.xtype);
30884 Roo.bootstrap.form.HtmlEditorToolbarStandard.superclass.onRender.call(this, ct, position);
30886 this.el.dom.style.marginBottom = '0';
30888 var editorcore = this.editorcore;
30889 var editor= this.editor;
30892 var btn = function(id,cmd , toggle, handler, html){
30894 var event = toggle ? 'toggle' : 'click';
30899 xns: Roo.bootstrap,
30903 enableToggle:toggle !== false,
30905 pressed : toggle ? false : null,
30908 a.listeners[toggle ? 'toggle' : 'click'] = function() {
30909 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
30915 // var cb_box = function...
30920 xns: Roo.bootstrap,
30925 xns: Roo.bootstrap,
30929 Roo.each(this.formats, function(f) {
30930 style.menu.items.push({
30932 xns: Roo.bootstrap,
30933 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
30938 editorcore.insertTag(this.tagname);
30945 children.push(style);
30947 btn('bold',false,true);
30948 btn('italic',false,true);
30949 btn('align-left', 'justifyleft',true);
30950 btn('align-center', 'justifycenter',true);
30951 btn('align-right' , 'justifyright',true);
30952 btn('link', false, false, function(btn) {
30953 //Roo.log("create link?");
30954 var url = prompt(this.createLinkText, this.defaultLinkValue);
30955 if(url && url != 'http:/'+'/'){
30956 this.editorcore.relayCmd('createlink', url);
30959 btn('list','insertunorderedlist',true);
30960 btn('pencil', false,true, function(btn){
30962 this.toggleSourceEdit(btn.pressed);
30965 if (this.editor.btns.length > 0) {
30966 for (var i = 0; i<this.editor.btns.length; i++) {
30967 children.push(this.editor.btns[i]);
30975 xns: Roo.bootstrap,
30980 xns: Roo.bootstrap,
30985 cog.menu.items.push({
30987 xns: Roo.bootstrap,
30988 html : Clean styles,
30993 editorcore.insertTag(this.tagname);
31002 this.xtype = 'NavSimplebar';
31004 for(var i=0;i< children.length;i++) {
31006 this.buttons.add(this.addxtypeChild(children[i]));
31010 editor.on('editorevent', this.updateToolbar, this);
31012 onBtnClick : function(id)
31014 this.editorcore.relayCmd(id);
31015 this.editorcore.focus();
31019 * Protected method that will not generally be called directly. It triggers
31020 * a toolbar update by reading the markup state of the current selection in the editor.
31022 updateToolbar: function(){
31024 if(!this.editorcore.activated){
31025 this.editor.onFirstFocus(); // is this neeed?
31029 var btns = this.buttons;
31030 var doc = this.editorcore.doc;
31031 btns.get('bold').setActive(doc.queryCommandState('bold'));
31032 btns.get('italic').setActive(doc.queryCommandState('italic'));
31033 //btns.get('underline').setActive(doc.queryCommandState('underline'));
31035 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
31036 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
31037 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
31039 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
31040 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
31043 var ans = this.editorcore.getAllAncestors();
31044 if (this.formatCombo) {
31047 var store = this.formatCombo.store;
31048 this.formatCombo.setValue("");
31049 for (var i =0; i < ans.length;i++) {
31050 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
31052 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
31060 // hides menus... - so this cant be on a menu...
31061 Roo.bootstrap.MenuMgr.hideAll();
31063 Roo.bootstrap.menu.Manager.hideAll();
31064 //this.editorsyncValue();
31066 onFirstFocus: function() {
31067 this.buttons.each(function(item){
31071 toggleSourceEdit : function(sourceEditMode){
31074 if(sourceEditMode){
31075 Roo.log("disabling buttons");
31076 this.buttons.each( function(item){
31077 if(item.cmd != 'pencil'){
31083 Roo.log("enabling buttons");
31084 if(this.editorcore.initialized){
31085 this.buttons.each( function(item){
31091 Roo.log("calling toggole on editor");
31092 // tell the editor that it's been pressed..
31093 this.editor.toggleSourceEdit(sourceEditMode);
31107 * @class Roo.bootstrap.form.Markdown
31108 * @extends Roo.bootstrap.form.TextArea
31109 * Bootstrap Showdown editable area
31110 * @cfg {string} content
31113 * Create a new Showdown
31116 Roo.bootstrap.form.Markdown = function(config){
31117 Roo.bootstrap.form.Markdown.superclass.constructor.call(this, config);
31121 Roo.extend(Roo.bootstrap.form.Markdown, Roo.bootstrap.form.TextArea, {
31125 initEvents : function()
31128 Roo.bootstrap.form.TextArea.prototype.initEvents.call(this);
31129 this.markdownEl = this.el.createChild({
31130 cls : 'roo-markdown-area'
31132 this.inputEl().addClass('d-none');
31133 if (this.getValue() == '') {
31134 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31137 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31139 this.markdownEl.on('click', this.toggleTextEdit, this);
31140 this.on('blur', this.toggleTextEdit, this);
31141 this.on('specialkey', this.resizeTextArea, this);
31144 toggleTextEdit : function()
31146 var sh = this.markdownEl.getHeight();
31147 this.inputEl().addClass('d-none');
31148 this.markdownEl.addClass('d-none');
31149 if (!this.editing) {
31151 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
31152 this.inputEl().removeClass('d-none');
31153 this.inputEl().focus();
31154 this.editing = true;
31157 // show showdown...
31158 this.updateMarkdown();
31159 this.markdownEl.removeClass('d-none');
31160 this.editing = false;
31163 updateMarkdown : function()
31165 if (this.getValue() == '') {
31166 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
31170 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
31173 resizeTextArea: function () {
31176 Roo.log([sh, this.getValue().split("\n").length * 30]);
31177 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
31179 setValue : function(val)
31181 Roo.bootstrap.form.TextArea.prototype.setValue.call(this,val);
31182 if (!this.editing) {
31183 this.updateMarkdown();
31189 if (!this.editing) {
31190 this.toggleTextEdit();
31198 * Ext JS Library 1.1.1
31199 * Copyright(c) 2006-2007, Ext JS, LLC.
31201 * Originally Released Under LGPL - original licence link has changed is not relivant.
31204 * <script type="text/javascript">
31208 * @class Roo.bootstrap.PagingToolbar
31209 * @extends Roo.bootstrap.nav.Simplebar
31210 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31212 * Create a new PagingToolbar
31213 * @param {Object} config The config object
31214 * @param {Roo.data.Store} store
31216 Roo.bootstrap.PagingToolbar = function(config)
31218 // old args format still supported... - xtype is prefered..
31219 // created from xtype...
31221 this.ds = config.dataSource;
31223 if (config.store && !this.ds) {
31224 this.store= Roo.factory(config.store, Roo.data);
31225 this.ds = this.store;
31226 this.ds.xmodule = this.xmodule || false;
31229 this.toolbarItems = [];
31230 if (config.items) {
31231 this.toolbarItems = config.items;
31234 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
31239 this.bind(this.ds);
31242 if (Roo.bootstrap.version == 4) {
31243 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
31245 this.navgroup = new Roo.bootstrap.nav.Group({ cls: 'pagination' });
31250 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.nav.Simplebar, {
31252 * @cfg {Roo.bootstrap.Button} buttons[]
31253 * Buttons for the toolbar
31256 * @cfg {Roo.data.Store} store
31257 * The underlying data store providing the paged data
31260 * @cfg {String/HTMLElement/Element} container
31261 * container The id or element that will contain the toolbar
31264 * @cfg {Boolean} displayInfo
31265 * True to display the displayMsg (defaults to false)
31268 * @cfg {Number} pageSize
31269 * The number of records to display per page (defaults to 20)
31273 * @cfg {String} displayMsg
31274 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31276 displayMsg : 'Displaying {0} - {1} of {2}',
31278 * @cfg {String} emptyMsg
31279 * The message to display when no records are found (defaults to "No data to display")
31281 emptyMsg : 'No data to display',
31283 * Customizable piece of the default paging text (defaults to "Page")
31286 beforePageText : "Page",
31288 * Customizable piece of the default paging text (defaults to "of %0")
31291 afterPageText : "of {0}",
31293 * Customizable piece of the default paging text (defaults to "First Page")
31296 firstText : "First Page",
31298 * Customizable piece of the default paging text (defaults to "Previous Page")
31301 prevText : "Previous Page",
31303 * Customizable piece of the default paging text (defaults to "Next Page")
31306 nextText : "Next Page",
31308 * Customizable piece of the default paging text (defaults to "Last Page")
31311 lastText : "Last Page",
31313 * Customizable piece of the default paging text (defaults to "Refresh")
31316 refreshText : "Refresh",
31320 onRender : function(ct, position)
31322 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
31323 this.navgroup.parentId = this.id;
31324 this.navgroup.onRender(this.el, null);
31325 // add the buttons to the navgroup
31327 if(this.displayInfo){
31328 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
31329 this.displayEl = this.el.select('.x-paging-info', true).first();
31330 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
31331 // this.displayEl = navel.el.select('span',true).first();
31337 Roo.each(_this.buttons, function(e){ // this might need to use render????
31338 Roo.factory(e).render(_this.el);
31342 Roo.each(_this.toolbarItems, function(e) {
31343 _this.navgroup.addItem(e);
31347 this.first = this.navgroup.addItem({
31348 tooltip: this.firstText,
31349 cls: "prev btn-outline-secondary",
31350 html : ' <i class="fa fa-step-backward"></i>',
31352 preventDefault: true,
31353 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
31356 this.prev = this.navgroup.addItem({
31357 tooltip: this.prevText,
31358 cls: "prev btn-outline-secondary",
31359 html : ' <i class="fa fa-backward"></i>',
31361 preventDefault: true,
31362 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
31364 //this.addSeparator();
31367 var field = this.navgroup.addItem( {
31369 cls : 'x-paging-position btn-outline-secondary',
31371 html : this.beforePageText +
31372 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
31373 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
31376 this.field = field.el.select('input', true).first();
31377 this.field.on("keydown", this.onPagingKeydown, this);
31378 this.field.on("focus", function(){this.dom.select();});
31381 this.afterTextEl = field.el.select('.x-paging-after',true).first();
31382 //this.field.setHeight(18);
31383 //this.addSeparator();
31384 this.next = this.navgroup.addItem({
31385 tooltip: this.nextText,
31386 cls: "next btn-outline-secondary",
31387 html : ' <i class="fa fa-forward"></i>',
31389 preventDefault: true,
31390 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
31392 this.last = this.navgroup.addItem({
31393 tooltip: this.lastText,
31394 html : ' <i class="fa fa-step-forward"></i>',
31395 cls: "next btn-outline-secondary",
31397 preventDefault: true,
31398 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
31400 //this.addSeparator();
31401 this.loading = this.navgroup.addItem({
31402 tooltip: this.refreshText,
31403 cls: "btn-outline-secondary",
31404 html : ' <i class="fa fa-refresh"></i>',
31405 preventDefault: true,
31406 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
31412 updateInfo : function(){
31413 if(this.displayEl){
31414 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
31415 var msg = count == 0 ?
31419 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
31421 this.displayEl.update(msg);
31426 onLoad : function(ds, r, o)
31428 this.cursor = o.params && o.params.start ? o.params.start : 0;
31430 var d = this.getPageData(),
31435 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
31436 this.field.dom.value = ap;
31437 this.first.setDisabled(ap == 1);
31438 this.prev.setDisabled(ap == 1);
31439 this.next.setDisabled(ap == ps);
31440 this.last.setDisabled(ap == ps);
31441 this.loading.enable();
31446 getPageData : function(){
31447 var total = this.ds.getTotalCount();
31450 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31451 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31456 onLoadError : function(proxy, o){
31457 this.loading.enable();
31458 if (this.ds.events.loadexception.listeners.length < 2) {
31459 // nothing has been assigned to loadexception except this...
31461 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
31467 onPagingKeydown : function(e){
31468 var k = e.getKey();
31469 var d = this.getPageData();
31471 var v = this.field.dom.value, pageNum;
31472 if(!v || isNaN(pageNum = parseInt(v, 10))){
31473 this.field.dom.value = d.activePage;
31476 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31477 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31480 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))
31482 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31483 this.field.dom.value = pageNum;
31484 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31487 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31489 var v = this.field.dom.value, pageNum;
31490 var increment = (e.shiftKey) ? 10 : 1;
31491 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31494 if(!v || isNaN(pageNum = parseInt(v, 10))) {
31495 this.field.dom.value = d.activePage;
31498 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31500 this.field.dom.value = parseInt(v, 10) + increment;
31501 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31502 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31509 beforeLoad : function(){
31511 this.loading.disable();
31516 onClick : function(which){
31525 ds.load({params:{start: 0, limit: this.pageSize}});
31528 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31531 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31534 var total = ds.getTotalCount();
31535 var extra = total % this.pageSize;
31536 var lastStart = extra ? (total - extra) : total-this.pageSize;
31537 ds.load({params:{start: lastStart, limit: this.pageSize}});
31540 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31546 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31547 * @param {Roo.data.Store} store The data store to unbind
31549 unbind : function(ds){
31550 ds.un("beforeload", this.beforeLoad, this);
31551 ds.un("load", this.onLoad, this);
31552 ds.un("loadexception", this.onLoadError, this);
31553 ds.un("remove", this.updateInfo, this);
31554 ds.un("add", this.updateInfo, this);
31555 this.ds = undefined;
31559 * Binds the paging toolbar to the specified {@link Roo.data.Store}
31560 * @param {Roo.data.Store} store The data store to bind
31562 bind : function(ds){
31563 ds.on("beforeload", this.beforeLoad, this);
31564 ds.on("load", this.onLoad, this);
31565 ds.on("loadexception", this.onLoadError, this);
31566 ds.on("remove", this.updateInfo, this);
31567 ds.on("add", this.updateInfo, this);
31578 * @class Roo.bootstrap.MessageBar
31579 * @extends Roo.bootstrap.Component
31580 * Bootstrap MessageBar class
31581 * @cfg {String} html contents of the MessageBar
31582 * @cfg {String} weight (info | success | warning | danger) default info
31583 * @cfg {String} beforeClass insert the bar before the given class
31584 * @cfg {Boolean} closable (true | false) default false
31585 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
31588 * Create a new Element
31589 * @param {Object} config The config object
31592 Roo.bootstrap.MessageBar = function(config){
31593 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
31596 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
31602 beforeClass: 'bootstrap-sticky-wrap',
31604 getAutoCreate : function(){
31608 cls: 'alert alert-dismissable alert-' + this.weight,
31613 html: this.html || ''
31619 cfg.cls += ' alert-messages-fixed';
31633 onRender : function(ct, position)
31635 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
31638 var cfg = Roo.apply({}, this.getAutoCreate());
31642 cfg.cls += ' ' + this.cls;
31645 cfg.style = this.style;
31647 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
31649 this.el.setVisibilityMode(Roo.Element.DISPLAY);
31652 this.el.select('>button.close').on('click', this.hide, this);
31658 if (!this.rendered) {
31664 this.fireEvent('show', this);
31670 if (!this.rendered) {
31676 this.fireEvent('hide', this);
31679 update : function()
31681 // var e = this.el.dom.firstChild;
31683 // if(this.closable){
31684 // e = e.nextSibling;
31687 // e.data = this.html || '';
31689 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
31705 * @class Roo.bootstrap.Graph
31706 * @extends Roo.bootstrap.Component
31707 * Bootstrap Graph class
31711 @cfg {String} graphtype bar | vbar | pie
31712 @cfg {number} g_x coodinator | centre x (pie)
31713 @cfg {number} g_y coodinator | centre y (pie)
31714 @cfg {number} g_r radius (pie)
31715 @cfg {number} g_height height of the chart (respected by all elements in the set)
31716 @cfg {number} g_width width of the chart (respected by all elements in the set)
31717 @cfg {Object} title The title of the chart
31720 -opts (object) options for the chart
31722 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
31723 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
31725 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.
31726 o stacked (boolean) whether or not to tread values as in a stacked bar chart
31728 o stretch (boolean)
31730 -opts (object) options for the pie
31733 o startAngle (number)
31734 o endAngle (number)
31738 * Create a new Input
31739 * @param {Object} config The config object
31742 Roo.bootstrap.Graph = function(config){
31743 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
31749 * The img click event for the img.
31750 * @param {Roo.EventObject} e
31756 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
31767 //g_colors: this.colors,
31774 getAutoCreate : function(){
31785 onRender : function(ct,position){
31788 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
31790 if (typeof(Raphael) == 'undefined') {
31791 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
31795 this.raphael = Raphael(this.el.dom);
31797 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31798 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31799 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
31800 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
31802 r.text(160, 10, "Single Series Chart").attr(txtattr);
31803 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
31804 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
31805 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
31807 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
31808 r.barchart(330, 10, 300, 220, data1);
31809 r.barchart(10, 250, 300, 220, data2, {stacked: true});
31810 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
31813 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31814 // r.barchart(30, 30, 560, 250, xdata, {
31815 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
31816 // axis : "0 0 1 1",
31817 // axisxlabels : xdata
31818 // //yvalues : cols,
31821 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
31823 // this.load(null,xdata,{
31824 // axis : "0 0 1 1",
31825 // axisxlabels : xdata
31830 load : function(graphtype,xdata,opts)
31832 this.raphael.clear();
31834 graphtype = this.graphtype;
31839 var r = this.raphael,
31840 fin = function () {
31841 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
31843 fout = function () {
31844 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
31846 pfin = function() {
31847 this.sector.stop();
31848 this.sector.scale(1.1, 1.1, this.cx, this.cy);
31851 this.label[0].stop();
31852 this.label[0].attr({ r: 7.5 });
31853 this.label[1].attr({ "font-weight": 800 });
31856 pfout = function() {
31857 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
31860 this.label[0].animate({ r: 5 }, 500, "bounce");
31861 this.label[1].attr({ "font-weight": 400 });
31867 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31870 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
31873 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
31874 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
31876 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
31883 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
31888 setTitle: function(o)
31893 initEvents: function() {
31896 this.el.on('click', this.onClick, this);
31900 onClick : function(e)
31902 Roo.log('img onclick');
31903 this.fireEvent('click', this, e);
31909 Roo.bootstrap.dash = {};/*
31915 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
31918 * @class Roo.bootstrap.dash.NumberBox
31919 * @extends Roo.bootstrap.Component
31920 * Bootstrap NumberBox class
31921 * @cfg {String} headline Box headline
31922 * @cfg {String} content Box content
31923 * @cfg {String} icon Box icon
31924 * @cfg {String} footer Footer text
31925 * @cfg {String} fhref Footer href
31928 * Create a new NumberBox
31929 * @param {Object} config The config object
31933 Roo.bootstrap.dash.NumberBox = function(config){
31934 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
31938 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
31947 getAutoCreate : function(){
31951 cls : 'small-box ',
31959 cls : 'roo-headline',
31960 html : this.headline
31964 cls : 'roo-content',
31965 html : this.content
31979 cls : 'ion ' + this.icon
31988 cls : 'small-box-footer',
31989 href : this.fhref || '#',
31993 cfg.cn.push(footer);
32000 onRender : function(ct,position){
32001 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
32008 setHeadline: function (value)
32010 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
32013 setFooter: function (value, href)
32015 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
32018 this.el.select('a.small-box-footer',true).first().attr('href', href);
32023 setContent: function (value)
32025 this.el.select('.roo-content',true).first().dom.innerHTML = value;
32028 initEvents: function()
32042 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32045 * @class Roo.bootstrap.dash.TabBox
32046 * @extends Roo.bootstrap.Component
32047 * @children Roo.bootstrap.dash.TabPane
32048 * Bootstrap TabBox class
32049 * @cfg {String} title Title of the TabBox
32050 * @cfg {String} icon Icon of the TabBox
32051 * @cfg {Boolean} showtabs (true|false) show the tabs default true
32052 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
32055 * Create a new TabBox
32056 * @param {Object} config The config object
32060 Roo.bootstrap.dash.TabBox = function(config){
32061 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
32066 * When a pane is added
32067 * @param {Roo.bootstrap.dash.TabPane} pane
32071 * @event activatepane
32072 * When a pane is activated
32073 * @param {Roo.bootstrap.dash.TabPane} pane
32075 "activatepane" : true
32083 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
32088 tabScrollable : false,
32090 getChildContainer : function()
32092 return this.el.select('.tab-content', true).first();
32095 getAutoCreate : function(){
32099 cls: 'pull-left header',
32107 cls: 'fa ' + this.icon
32113 cls: 'nav nav-tabs pull-right',
32119 if(this.tabScrollable){
32126 cls: 'nav nav-tabs pull-right',
32137 cls: 'nav-tabs-custom',
32142 cls: 'tab-content no-padding',
32150 initEvents : function()
32152 //Roo.log('add add pane handler');
32153 this.on('addpane', this.onAddPane, this);
32156 * Updates the box title
32157 * @param {String} html to set the title to.
32159 setTitle : function(value)
32161 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
32163 onAddPane : function(pane)
32165 this.panes.push(pane);
32166 //Roo.log('addpane');
32168 // tabs are rendere left to right..
32169 if(!this.showtabs){
32173 var ctr = this.el.select('.nav-tabs', true).first();
32176 var existing = ctr.select('.nav-tab',true);
32177 var qty = existing.getCount();;
32180 var tab = ctr.createChild({
32182 cls : 'nav-tab' + (qty ? '' : ' active'),
32190 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
32193 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
32195 pane.el.addClass('active');
32200 onTabClick : function(ev,un,ob,pane)
32202 //Roo.log('tab - prev default');
32203 ev.preventDefault();
32206 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
32207 pane.tab.addClass('active');
32208 //Roo.log(pane.title);
32209 this.getChildContainer().select('.tab-pane',true).removeClass('active');
32210 // technically we should have a deactivate event.. but maybe add later.
32211 // and it should not de-activate the selected tab...
32212 this.fireEvent('activatepane', pane);
32213 pane.el.addClass('active');
32214 pane.fireEvent('activate');
32219 getActivePane : function()
32222 Roo.each(this.panes, function(p) {
32223 if(p.el.hasClass('active')){
32244 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
32246 * @class Roo.bootstrap.TabPane
32247 * @extends Roo.bootstrap.Component
32248 * @children Roo.bootstrap.Graph Roo.bootstrap.Column
32249 * Bootstrap TabPane class
32250 * @cfg {Boolean} active (false | true) Default false
32251 * @cfg {String} title title of panel
32255 * Create a new TabPane
32256 * @param {Object} config The config object
32259 Roo.bootstrap.dash.TabPane = function(config){
32260 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
32266 * When a pane is activated
32267 * @param {Roo.bootstrap.dash.TabPane} pane
32274 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
32279 // the tabBox that this is attached to.
32282 getAutoCreate : function()
32290 cfg.cls += ' active';
32295 initEvents : function()
32297 //Roo.log('trigger add pane handler');
32298 this.parent().fireEvent('addpane', this)
32302 * Updates the tab title
32303 * @param {String} html to set the title to.
32305 setTitle: function(str)
32311 this.tab.select('a', true).first().dom.innerHTML = str;
32330 * @class Roo.bootstrap.Tooltip
32331 * Bootstrap Tooltip class
32332 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
32333 * to determine which dom element triggers the tooltip.
32335 * It needs to add support for additional attributes like tooltip-position
32338 * Create a new Toolti
32339 * @param {Object} config The config object
32342 Roo.bootstrap.Tooltip = function(config){
32343 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
32345 this.alignment = Roo.bootstrap.Tooltip.alignment;
32347 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
32348 this.alignment = config.alignment;
32353 Roo.apply(Roo.bootstrap.Tooltip, {
32355 * @function init initialize tooltip monitoring.
32359 currentTip : false,
32360 currentRegion : false,
32366 Roo.get(document).on('mouseover', this.enter ,this);
32367 Roo.get(document).on('mouseout', this.leave, this);
32370 this.currentTip = new Roo.bootstrap.Tooltip();
32373 enter : function(ev)
32375 var dom = ev.getTarget();
32377 //Roo.log(['enter',dom]);
32378 var el = Roo.fly(dom);
32379 if (this.currentEl) {
32381 //Roo.log(this.currentEl);
32382 //Roo.log(this.currentEl.contains(dom));
32383 if (this.currentEl == el) {
32386 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
32392 if (this.currentTip.el) {
32393 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
32397 if(!el || el.dom == document){
32403 if (!el.attr('tooltip')) {
32404 pel = el.findParent("[tooltip]");
32406 bindEl = Roo.get(pel);
32412 // you can not look for children, as if el is the body.. then everythign is the child..
32413 if (!pel && !el.attr('tooltip')) { //
32414 if (!el.select("[tooltip]").elements.length) {
32417 // is the mouse over this child...?
32418 bindEl = el.select("[tooltip]").first();
32419 var xy = ev.getXY();
32420 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
32421 //Roo.log("not in region.");
32424 //Roo.log("child element over..");
32427 this.currentEl = el;
32428 this.currentTip.bind(bindEl);
32429 this.currentRegion = Roo.lib.Region.getRegion(dom);
32430 this.currentTip.enter();
32433 leave : function(ev)
32435 var dom = ev.getTarget();
32436 //Roo.log(['leave',dom]);
32437 if (!this.currentEl) {
32442 if (dom != this.currentEl.dom) {
32445 var xy = ev.getXY();
32446 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
32449 // only activate leave if mouse cursor is outside... bounding box..
32454 if (this.currentTip) {
32455 this.currentTip.leave();
32457 //Roo.log('clear currentEl');
32458 this.currentEl = false;
32463 'left' : ['r-l', [-2,0], 'right'],
32464 'right' : ['l-r', [2,0], 'left'],
32465 'bottom' : ['t-b', [0,2], 'top'],
32466 'top' : [ 'b-t', [0,-2], 'bottom']
32472 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
32477 delay : null, // can be { show : 300 , hide: 500}
32481 hoverState : null, //???
32483 placement : 'bottom',
32487 getAutoCreate : function(){
32494 cls : 'tooltip-arrow arrow'
32497 cls : 'tooltip-inner'
32504 bind : function(el)
32509 initEvents : function()
32511 this.arrowEl = this.el.select('.arrow', true).first();
32512 this.innerEl = this.el.select('.tooltip-inner', true).first();
32515 enter : function () {
32517 if (this.timeout != null) {
32518 clearTimeout(this.timeout);
32521 this.hoverState = 'in';
32522 //Roo.log("enter - show");
32523 if (!this.delay || !this.delay.show) {
32528 this.timeout = setTimeout(function () {
32529 if (_t.hoverState == 'in') {
32532 }, this.delay.show);
32536 clearTimeout(this.timeout);
32538 this.hoverState = 'out';
32539 if (!this.delay || !this.delay.hide) {
32545 this.timeout = setTimeout(function () {
32546 //Roo.log("leave - timeout");
32548 if (_t.hoverState == 'out') {
32550 Roo.bootstrap.Tooltip.currentEl = false;
32555 show : function (msg)
32558 this.render(document.body);
32561 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
32563 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
32565 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
32567 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
32568 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
32570 var placement = typeof this.placement == 'function' ?
32571 this.placement.call(this, this.el, on_el) :
32574 var autoToken = /\s?auto?\s?/i;
32575 var autoPlace = autoToken.test(placement);
32577 placement = placement.replace(autoToken, '') || 'top';
32581 //this.el.setXY([0,0]);
32583 //this.el.dom.style.display='block';
32585 //this.el.appendTo(on_el);
32587 var p = this.getPosition();
32588 var box = this.el.getBox();
32594 var align = this.alignment[placement];
32596 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
32598 if(placement == 'top' || placement == 'bottom'){
32600 placement = 'right';
32603 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
32604 placement = 'left';
32607 var scroll = Roo.select('body', true).first().getScroll();
32609 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
32613 align = this.alignment[placement];
32615 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
32619 var elems = document.getElementsByTagName('div');
32620 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
32621 for (var i = 0; i < elems.length; i++) {
32622 var zindex = Number.parseInt(
32623 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
32626 if (zindex > highest) {
32633 this.el.dom.style.zIndex = highest;
32635 this.el.alignTo(this.bindEl, align[0],align[1]);
32636 //var arrow = this.el.select('.arrow',true).first();
32637 //arrow.set(align[2],
32639 this.el.addClass(placement);
32640 this.el.addClass("bs-tooltip-"+ placement);
32642 this.el.addClass('in fade show');
32644 this.hoverState = null;
32646 if (this.el.hasClass('fade')) {
32661 //this.el.setXY([0,0]);
32662 this.el.removeClass(['show', 'in']);
32678 * @class Roo.bootstrap.LocationPicker
32679 * @extends Roo.bootstrap.Component
32680 * Bootstrap LocationPicker class
32681 * @cfg {Number} latitude Position when init default 0
32682 * @cfg {Number} longitude Position when init default 0
32683 * @cfg {Number} zoom default 15
32684 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
32685 * @cfg {Boolean} mapTypeControl default false
32686 * @cfg {Boolean} disableDoubleClickZoom default false
32687 * @cfg {Boolean} scrollwheel default true
32688 * @cfg {Boolean} streetViewControl default false
32689 * @cfg {Number} radius default 0
32690 * @cfg {String} locationName
32691 * @cfg {Boolean} draggable default true
32692 * @cfg {Boolean} enableAutocomplete default false
32693 * @cfg {Boolean} enableReverseGeocode default true
32694 * @cfg {String} markerTitle
32697 * Create a new LocationPicker
32698 * @param {Object} config The config object
32702 Roo.bootstrap.LocationPicker = function(config){
32704 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
32709 * Fires when the picker initialized.
32710 * @param {Roo.bootstrap.LocationPicker} this
32711 * @param {Google Location} location
32715 * @event positionchanged
32716 * Fires when the picker position changed.
32717 * @param {Roo.bootstrap.LocationPicker} this
32718 * @param {Google Location} location
32720 positionchanged : true,
32723 * Fires when the map resize.
32724 * @param {Roo.bootstrap.LocationPicker} this
32729 * Fires when the map show.
32730 * @param {Roo.bootstrap.LocationPicker} this
32735 * Fires when the map hide.
32736 * @param {Roo.bootstrap.LocationPicker} this
32741 * Fires when click the map.
32742 * @param {Roo.bootstrap.LocationPicker} this
32743 * @param {Map event} e
32747 * @event mapRightClick
32748 * Fires when right click the map.
32749 * @param {Roo.bootstrap.LocationPicker} this
32750 * @param {Map event} e
32752 mapRightClick : true,
32754 * @event markerClick
32755 * Fires when click the marker.
32756 * @param {Roo.bootstrap.LocationPicker} this
32757 * @param {Map event} e
32759 markerClick : true,
32761 * @event markerRightClick
32762 * Fires when right click the marker.
32763 * @param {Roo.bootstrap.LocationPicker} this
32764 * @param {Map event} e
32766 markerRightClick : true,
32768 * @event OverlayViewDraw
32769 * Fires when OverlayView Draw
32770 * @param {Roo.bootstrap.LocationPicker} this
32772 OverlayViewDraw : true,
32774 * @event OverlayViewOnAdd
32775 * Fires when OverlayView Draw
32776 * @param {Roo.bootstrap.LocationPicker} this
32778 OverlayViewOnAdd : true,
32780 * @event OverlayViewOnRemove
32781 * Fires when OverlayView Draw
32782 * @param {Roo.bootstrap.LocationPicker} this
32784 OverlayViewOnRemove : true,
32786 * @event OverlayViewShow
32787 * Fires when OverlayView Draw
32788 * @param {Roo.bootstrap.LocationPicker} this
32789 * @param {Pixel} cpx
32791 OverlayViewShow : true,
32793 * @event OverlayViewHide
32794 * Fires when OverlayView Draw
32795 * @param {Roo.bootstrap.LocationPicker} this
32797 OverlayViewHide : true,
32799 * @event loadexception
32800 * Fires when load google lib failed.
32801 * @param {Roo.bootstrap.LocationPicker} this
32803 loadexception : true
32808 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
32810 gMapContext: false,
32816 mapTypeControl: false,
32817 disableDoubleClickZoom: false,
32819 streetViewControl: false,
32823 enableAutocomplete: false,
32824 enableReverseGeocode: true,
32827 getAutoCreate: function()
32832 cls: 'roo-location-picker'
32838 initEvents: function(ct, position)
32840 if(!this.el.getWidth() || this.isApplied()){
32844 this.el.setVisibilityMode(Roo.Element.DISPLAY);
32849 initial: function()
32851 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
32852 this.fireEvent('loadexception', this);
32856 if(!this.mapTypeId){
32857 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
32860 this.gMapContext = this.GMapContext();
32862 this.initOverlayView();
32864 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
32868 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
32869 _this.setPosition(_this.gMapContext.marker.position);
32872 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
32873 _this.fireEvent('mapClick', this, event);
32877 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
32878 _this.fireEvent('mapRightClick', this, event);
32882 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
32883 _this.fireEvent('markerClick', this, event);
32887 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
32888 _this.fireEvent('markerRightClick', this, event);
32892 this.setPosition(this.gMapContext.location);
32894 this.fireEvent('initial', this, this.gMapContext.location);
32897 initOverlayView: function()
32901 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
32905 _this.fireEvent('OverlayViewDraw', _this);
32910 _this.fireEvent('OverlayViewOnAdd', _this);
32913 onRemove: function()
32915 _this.fireEvent('OverlayViewOnRemove', _this);
32918 show: function(cpx)
32920 _this.fireEvent('OverlayViewShow', _this, cpx);
32925 _this.fireEvent('OverlayViewHide', _this);
32931 fromLatLngToContainerPixel: function(event)
32933 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
32936 isApplied: function()
32938 return this.getGmapContext() == false ? false : true;
32941 getGmapContext: function()
32943 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
32946 GMapContext: function()
32948 var position = new google.maps.LatLng(this.latitude, this.longitude);
32950 var _map = new google.maps.Map(this.el.dom, {
32953 mapTypeId: this.mapTypeId,
32954 mapTypeControl: this.mapTypeControl,
32955 disableDoubleClickZoom: this.disableDoubleClickZoom,
32956 scrollwheel: this.scrollwheel,
32957 streetViewControl: this.streetViewControl,
32958 locationName: this.locationName,
32959 draggable: this.draggable,
32960 enableAutocomplete: this.enableAutocomplete,
32961 enableReverseGeocode: this.enableReverseGeocode
32964 var _marker = new google.maps.Marker({
32965 position: position,
32967 title: this.markerTitle,
32968 draggable: this.draggable
32975 location: position,
32976 radius: this.radius,
32977 locationName: this.locationName,
32978 addressComponents: {
32979 formatted_address: null,
32980 addressLine1: null,
32981 addressLine2: null,
32983 streetNumber: null,
32987 stateOrProvince: null
32990 domContainer: this.el.dom,
32991 geodecoder: new google.maps.Geocoder()
32995 drawCircle: function(center, radius, options)
32997 if (this.gMapContext.circle != null) {
32998 this.gMapContext.circle.setMap(null);
33002 options = Roo.apply({}, options, {
33003 strokeColor: "#0000FF",
33004 strokeOpacity: .35,
33006 fillColor: "#0000FF",
33010 options.map = this.gMapContext.map;
33011 options.radius = radius;
33012 options.center = center;
33013 this.gMapContext.circle = new google.maps.Circle(options);
33014 return this.gMapContext.circle;
33020 setPosition: function(location)
33022 this.gMapContext.location = location;
33023 this.gMapContext.marker.setPosition(location);
33024 this.gMapContext.map.panTo(location);
33025 this.drawCircle(location, this.gMapContext.radius, {});
33029 if (this.gMapContext.settings.enableReverseGeocode) {
33030 this.gMapContext.geodecoder.geocode({
33031 latLng: this.gMapContext.location
33032 }, function(results, status) {
33034 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
33035 _this.gMapContext.locationName = results[0].formatted_address;
33036 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
33038 _this.fireEvent('positionchanged', this, location);
33045 this.fireEvent('positionchanged', this, location);
33050 google.maps.event.trigger(this.gMapContext.map, "resize");
33052 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
33054 this.fireEvent('resize', this);
33057 setPositionByLatLng: function(latitude, longitude)
33059 this.setPosition(new google.maps.LatLng(latitude, longitude));
33062 getCurrentPosition: function()
33065 latitude: this.gMapContext.location.lat(),
33066 longitude: this.gMapContext.location.lng()
33070 getAddressName: function()
33072 return this.gMapContext.locationName;
33075 getAddressComponents: function()
33077 return this.gMapContext.addressComponents;
33080 address_component_from_google_geocode: function(address_components)
33084 for (var i = 0; i < address_components.length; i++) {
33085 var component = address_components[i];
33086 if (component.types.indexOf("postal_code") >= 0) {
33087 result.postalCode = component.short_name;
33088 } else if (component.types.indexOf("street_number") >= 0) {
33089 result.streetNumber = component.short_name;
33090 } else if (component.types.indexOf("route") >= 0) {
33091 result.streetName = component.short_name;
33092 } else if (component.types.indexOf("neighborhood") >= 0) {
33093 result.city = component.short_name;
33094 } else if (component.types.indexOf("locality") >= 0) {
33095 result.city = component.short_name;
33096 } else if (component.types.indexOf("sublocality") >= 0) {
33097 result.district = component.short_name;
33098 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
33099 result.stateOrProvince = component.short_name;
33100 } else if (component.types.indexOf("country") >= 0) {
33101 result.country = component.short_name;
33105 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
33106 result.addressLine2 = "";
33110 setZoomLevel: function(zoom)
33112 this.gMapContext.map.setZoom(zoom);
33125 this.fireEvent('show', this);
33136 this.fireEvent('hide', this);
33141 Roo.apply(Roo.bootstrap.LocationPicker, {
33143 OverlayView : function(map, options)
33145 options = options || {};
33152 * @class Roo.bootstrap.Alert
33153 * @extends Roo.bootstrap.Component
33154 * Bootstrap Alert class - shows an alert area box
33156 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
33157 Enter a valid email address
33160 * @cfg {String} title The title of alert
33161 * @cfg {String} html The content of alert
33162 * @cfg {String} weight (success|info|warning|danger) Weight of the message
33163 * @cfg {String} fa font-awesomeicon
33164 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
33165 * @cfg {Boolean} close true to show a x closer
33169 * Create a new alert
33170 * @param {Object} config The config object
33174 Roo.bootstrap.Alert = function(config){
33175 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
33179 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
33185 faicon: false, // BC
33189 getAutoCreate : function()
33201 style : this.close ? '' : 'display:none'
33205 cls : 'roo-alert-icon'
33210 cls : 'roo-alert-title',
33215 cls : 'roo-alert-text',
33222 cfg.cn[0].cls += ' fa ' + this.faicon;
33225 cfg.cn[0].cls += ' fa ' + this.fa;
33229 cfg.cls += ' alert-' + this.weight;
33235 initEvents: function()
33237 this.el.setVisibilityMode(Roo.Element.DISPLAY);
33238 this.titleEl = this.el.select('.roo-alert-title',true).first();
33239 this.iconEl = this.el.select('.roo-alert-icon',true).first();
33240 this.htmlEl = this.el.select('.roo-alert-text',true).first();
33241 if (this.seconds > 0) {
33242 this.hide.defer(this.seconds, this);
33246 * Set the Title Message HTML
33247 * @param {String} html
33249 setTitle : function(str)
33251 this.titleEl.dom.innerHTML = str;
33255 * Set the Body Message HTML
33256 * @param {String} html
33258 setHtml : function(str)
33260 this.htmlEl.dom.innerHTML = str;
33263 * Set the Weight of the alert
33264 * @param {String} (success|info|warning|danger) weight
33267 setWeight : function(weight)
33270 this.el.removeClass('alert-' + this.weight);
33273 this.weight = weight;
33275 this.el.addClass('alert-' + this.weight);
33278 * Set the Icon of the alert
33279 * @param {String} see fontawsome names (name without the 'fa-' bit)
33281 setIcon : function(icon)
33284 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
33287 this.faicon = icon;
33289 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
33314 * @class Roo.bootstrap.UploadCropbox
33315 * @extends Roo.bootstrap.Component
33316 * Bootstrap UploadCropbox class
33317 * @cfg {String} emptyText show when image has been loaded
33318 * @cfg {String} rotateNotify show when image too small to rotate
33319 * @cfg {Number} errorTimeout default 3000
33320 * @cfg {Number} minWidth default 300
33321 * @cfg {Number} minHeight default 300
33322 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
33323 * @cfg {Boolean} isDocument (true|false) default false
33324 * @cfg {String} url action url
33325 * @cfg {String} paramName default 'imageUpload'
33326 * @cfg {String} method default POST
33327 * @cfg {Boolean} loadMask (true|false) default true
33328 * @cfg {Boolean} loadingText default 'Loading...'
33331 * Create a new UploadCropbox
33332 * @param {Object} config The config object
33335 Roo.bootstrap.UploadCropbox = function(config){
33336 console.log("BOOTSTRAP UPLOAD CROPBOX CONSTRUCTOR");
33337 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
33341 * @event beforeselectfile
33342 * Fire before select file
33343 * @param {Roo.bootstrap.UploadCropbox} this
33345 "beforeselectfile" : true,
33348 * Fire after initEvent
33349 * @param {Roo.bootstrap.UploadCropbox} this
33354 * Fire after initEvent
33355 * @param {Roo.bootstrap.UploadCropbox} this
33356 * @param {String} data
33361 * Fire when preparing the file data
33362 * @param {Roo.bootstrap.UploadCropbox} this
33363 * @param {Object} file
33368 * Fire when get exception
33369 * @param {Roo.bootstrap.UploadCropbox} this
33370 * @param {XMLHttpRequest} xhr
33372 "exception" : true,
33374 * @event beforeloadcanvas
33375 * Fire before load the canvas
33376 * @param {Roo.bootstrap.UploadCropbox} this
33377 * @param {String} src
33379 "beforeloadcanvas" : true,
33382 * Fire when trash image
33383 * @param {Roo.bootstrap.UploadCropbox} this
33388 * Fire when download the image
33389 * @param {Roo.bootstrap.UploadCropbox} this
33393 * @event footerbuttonclick
33394 * Fire when footerbuttonclick
33395 * @param {Roo.bootstrap.UploadCropbox} this
33396 * @param {String} type
33398 "footerbuttonclick" : true,
33402 * @param {Roo.bootstrap.UploadCropbox} this
33407 * Fire when rotate the image
33408 * @param {Roo.bootstrap.UploadCropbox} this
33409 * @param {String} pos
33414 * Fire when inspect the file
33415 * @param {Roo.bootstrap.UploadCropbox} this
33416 * @param {Object} file
33421 * Fire when xhr upload the file
33422 * @param {Roo.bootstrap.UploadCropbox} this
33423 * @param {Object} data
33428 * Fire when arrange the file data
33429 * @param {Roo.bootstrap.UploadCropbox} this
33430 * @param {Object} formData
33435 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
33438 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
33440 emptyText : 'Click to upload image',
33441 rotateNotify : 'Image is too small to rotate',
33442 errorTimeout : 3000,
33456 cropType : 'image/jpeg',
33458 canvasLoaded : false,
33459 isDocument : false,
33461 paramName : 'imageUpload',
33463 loadingText : 'Loading...',
33466 getAutoCreate : function()
33470 cls : 'roo-upload-cropbox',
33474 cls : 'roo-upload-cropbox-selector',
33479 cls : 'roo-upload-cropbox-body',
33480 style : 'cursor:pointer',
33484 cls : 'roo-upload-cropbox-preview'
33488 cls : 'roo-upload-cropbox-thumb'
33492 cls : 'roo-upload-cropbox-empty-notify',
33493 html : this.emptyText
33497 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
33498 html : this.rotateNotify
33504 cls : 'roo-upload-cropbox-footer',
33507 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
33517 onRender : function(ct, position)
33519 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
33521 if (this.buttons.length) {
33523 Roo.each(this.buttons, function(bb) {
33525 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
33527 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
33533 this.maskEl = this.el;
33537 initEvents : function()
33539 this.urlAPI = (window.createObjectURL && window) ||
33540 (window.URL && URL.revokeObjectURL && URL) ||
33541 (window.webkitURL && webkitURL);
33543 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
33544 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33546 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
33547 this.selectorEl.hide();
33549 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
33550 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33552 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
33553 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33554 this.thumbEl.hide();
33556 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
33557 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33559 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
33560 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33561 this.errorEl.hide();
33563 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
33564 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
33565 this.footerEl.hide();
33567 this.setThumbBoxSize();
33573 this.fireEvent('initial', this);
33580 window.addEventListener("resize", function() { _this.resize(); } );
33582 this.bodyEl.on('click', this.beforeSelectFile, this);
33585 this.bodyEl.on('touchstart', this.onTouchStart, this);
33586 this.bodyEl.on('touchmove', this.onTouchMove, this);
33587 this.bodyEl.on('touchend', this.onTouchEnd, this);
33591 this.bodyEl.on('mousedown', this.onMouseDown, this);
33592 this.bodyEl.on('mousemove', this.onMouseMove, this);
33593 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
33594 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
33595 Roo.get(document).on('mouseup', this.onMouseUp, this);
33598 this.selectorEl.on('change', this.onFileSelected, this);
33604 this.baseScale = 1;
33606 this.baseRotate = 1;
33607 this.dragable = false;
33608 this.pinching = false;
33611 this.cropData = false;
33612 this.notifyEl.dom.innerHTML = this.emptyText;
33614 this.selectorEl.dom.value = '';
33618 resize : function()
33620 if(this.fireEvent('resize', this) != false){
33621 this.setThumbBoxPosition();
33622 this.setCanvasPosition();
33626 onFooterButtonClick : function(e, el, o, type)
33629 case 'rotate-left' :
33630 this.onRotateLeft(e);
33632 case 'rotate-right' :
33633 this.onRotateRight(e);
33636 this.beforeSelectFile(e);
33651 this.fireEvent('footerbuttonclick', this, type);
33654 beforeSelectFile : function(e)
33656 e.preventDefault();
33658 if(this.fireEvent('beforeselectfile', this) != false){
33659 this.selectorEl.dom.click();
33663 onFileSelected : function(e)
33665 e.preventDefault();
33667 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
33671 var file = this.selectorEl.dom.files[0];
33673 if(this.fireEvent('inspect', this, file) != false){
33674 this.prepare(file);
33679 trash : function(e)
33681 this.fireEvent('trash', this);
33684 download : function(e)
33686 this.fireEvent('download', this);
33689 loadCanvas : function(src)
33691 if(this.fireEvent('beforeloadcanvas', this, src) != false){
33695 this.imageEl = document.createElement('img');
33699 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
33701 this.imageEl.src = src;
33705 onLoadCanvas : function()
33707 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
33708 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
33710 this.bodyEl.un('click', this.beforeSelectFile, this);
33712 this.notifyEl.hide();
33713 this.thumbEl.show();
33714 this.footerEl.show();
33716 this.baseRotateLevel();
33718 if(this.isDocument){
33719 this.setThumbBoxSize();
33722 this.setThumbBoxPosition();
33724 this.baseScaleLevel();
33730 this.canvasLoaded = true;
33733 this.maskEl.unmask();
33738 setCanvasPosition : function()
33740 if(!this.canvasEl){
33744 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
33745 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
33747 this.previewEl.setLeft(pw);
33748 this.previewEl.setTop(ph);
33752 onMouseDown : function(e)
33756 this.dragable = true;
33757 this.pinching = false;
33759 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
33760 this.dragable = false;
33764 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33765 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33769 onMouseMove : function(e)
33773 if(!this.canvasLoaded){
33777 if (!this.dragable){
33781 var minX = Math.ceil(this.thumbEl.getLeft(true));
33782 var minY = Math.ceil(this.thumbEl.getTop(true));
33784 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
33785 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
33787 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33788 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33790 x = x - this.mouseX;
33791 y = y - this.mouseY;
33793 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
33794 var bgY = Math.ceil(y + this.previewEl.getTop(true));
33796 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
33797 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
33799 this.previewEl.setLeft(bgX);
33800 this.previewEl.setTop(bgY);
33802 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
33803 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
33806 onMouseUp : function(e)
33810 this.dragable = false;
33813 onMouseWheel : function(e)
33817 this.startScale = this.scale;
33819 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
33821 if(!this.zoomable()){
33822 this.scale = this.startScale;
33831 zoomable : function()
33833 var minScale = this.thumbEl.getWidth() / this.minWidth;
33835 if(this.minWidth < this.minHeight){
33836 minScale = this.thumbEl.getHeight() / this.minHeight;
33839 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
33840 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
33844 (this.rotate == 0 || this.rotate == 180) &&
33846 width > this.imageEl.OriginWidth ||
33847 height > this.imageEl.OriginHeight ||
33848 (width < this.minWidth && height < this.minHeight)
33856 (this.rotate == 90 || this.rotate == 270) &&
33858 width > this.imageEl.OriginWidth ||
33859 height > this.imageEl.OriginHeight ||
33860 (width < this.minHeight && height < this.minWidth)
33867 !this.isDocument &&
33868 (this.rotate == 0 || this.rotate == 180) &&
33870 width < this.minWidth ||
33871 width > this.imageEl.OriginWidth ||
33872 height < this.minHeight ||
33873 height > this.imageEl.OriginHeight
33880 !this.isDocument &&
33881 (this.rotate == 90 || this.rotate == 270) &&
33883 width < this.minHeight ||
33884 width > this.imageEl.OriginWidth ||
33885 height < this.minWidth ||
33886 height > this.imageEl.OriginHeight
33896 onRotateLeft : function(e)
33898 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33900 var minScale = this.thumbEl.getWidth() / this.minWidth;
33902 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33903 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33905 this.startScale = this.scale;
33907 while (this.getScaleLevel() < minScale){
33909 this.scale = this.scale + 1;
33911 if(!this.zoomable()){
33916 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33917 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33922 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33929 this.scale = this.startScale;
33931 this.onRotateFail();
33936 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
33938 if(this.isDocument){
33939 this.setThumbBoxSize();
33940 this.setThumbBoxPosition();
33941 this.setCanvasPosition();
33946 this.fireEvent('rotate', this, 'left');
33950 onRotateRight : function(e)
33952 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
33954 var minScale = this.thumbEl.getWidth() / this.minWidth;
33956 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
33957 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
33959 this.startScale = this.scale;
33961 while (this.getScaleLevel() < minScale){
33963 this.scale = this.scale + 1;
33965 if(!this.zoomable()){
33970 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
33971 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
33976 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33983 this.scale = this.startScale;
33985 this.onRotateFail();
33990 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
33992 if(this.isDocument){
33993 this.setThumbBoxSize();
33994 this.setThumbBoxPosition();
33995 this.setCanvasPosition();
34000 this.fireEvent('rotate', this, 'right');
34003 onRotateFail : function()
34005 this.errorEl.show(true);
34009 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
34014 this.previewEl.dom.innerHTML = '';
34016 var canvasEl = document.createElement("canvas");
34018 var contextEl = canvasEl.getContext("2d");
34020 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34021 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34022 var center = this.imageEl.OriginWidth / 2;
34024 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
34025 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34026 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34027 center = this.imageEl.OriginHeight / 2;
34030 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
34032 contextEl.translate(center, center);
34033 contextEl.rotate(this.rotate * Math.PI / 180);
34035 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34037 this.canvasEl = document.createElement("canvas");
34039 this.contextEl = this.canvasEl.getContext("2d");
34041 switch (this.rotate) {
34044 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34045 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34047 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34052 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34053 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34055 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34056 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);
34060 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34065 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
34066 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
34068 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34069 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);
34073 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);
34078 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
34079 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
34081 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34082 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
34086 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);
34093 this.previewEl.appendChild(this.canvasEl);
34095 this.setCanvasPosition();
34100 if(!this.canvasLoaded){
34104 var imageCanvas = document.createElement("canvas");
34106 var imageContext = imageCanvas.getContext("2d");
34108 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34109 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
34111 var center = imageCanvas.width / 2;
34113 imageContext.translate(center, center);
34115 imageContext.rotate(this.rotate * Math.PI / 180);
34117 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
34119 var canvas = document.createElement("canvas");
34121 var context = canvas.getContext("2d");
34123 canvas.width = this.minWidth;
34124 canvas.height = this.minHeight;
34126 switch (this.rotate) {
34129 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34130 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34132 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34133 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34135 var targetWidth = this.minWidth - 2 * x;
34136 var targetHeight = this.minHeight - 2 * y;
34140 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34141 scale = targetWidth / width;
34144 if(x > 0 && y == 0){
34145 scale = targetHeight / height;
34148 if(x > 0 && y > 0){
34149 scale = targetWidth / width;
34151 if(width < height){
34152 scale = targetHeight / height;
34156 context.scale(scale, scale);
34158 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34159 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34161 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34162 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34164 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34169 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34170 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34172 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34173 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34175 var targetWidth = this.minWidth - 2 * x;
34176 var targetHeight = this.minHeight - 2 * y;
34180 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34181 scale = targetWidth / width;
34184 if(x > 0 && y == 0){
34185 scale = targetHeight / height;
34188 if(x > 0 && y > 0){
34189 scale = targetWidth / width;
34191 if(width < height){
34192 scale = targetHeight / height;
34196 context.scale(scale, scale);
34198 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34199 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34201 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34202 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34204 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34206 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34211 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
34212 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
34214 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34215 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34217 var targetWidth = this.minWidth - 2 * x;
34218 var targetHeight = this.minHeight - 2 * y;
34222 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34223 scale = targetWidth / width;
34226 if(x > 0 && y == 0){
34227 scale = targetHeight / height;
34230 if(x > 0 && y > 0){
34231 scale = targetWidth / width;
34233 if(width < height){
34234 scale = targetHeight / height;
34238 context.scale(scale, scale);
34240 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34241 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34243 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34244 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34246 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34247 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
34249 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34254 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
34255 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
34257 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
34258 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
34260 var targetWidth = this.minWidth - 2 * x;
34261 var targetHeight = this.minHeight - 2 * y;
34265 if((x == 0 && y == 0) || (x == 0 && y > 0)){
34266 scale = targetWidth / width;
34269 if(x > 0 && y == 0){
34270 scale = targetHeight / height;
34273 if(x > 0 && y > 0){
34274 scale = targetWidth / width;
34276 if(width < height){
34277 scale = targetHeight / height;
34281 context.scale(scale, scale);
34283 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
34284 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
34286 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
34287 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
34289 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
34291 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
34298 this.cropData = canvas.toDataURL(this.cropType);
34300 if(this.fireEvent('crop', this, this.cropData) !== false){
34301 this.process(this.file, this.cropData);
34308 setThumbBoxSize : function()
34312 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
34313 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
34314 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
34316 this.minWidth = width;
34317 this.minHeight = height;
34319 if(this.rotate == 90 || this.rotate == 270){
34320 this.minWidth = height;
34321 this.minHeight = width;
34326 width = Math.ceil(this.minWidth * height / this.minHeight);
34328 if(this.minWidth > this.minHeight){
34330 height = Math.ceil(this.minHeight * width / this.minWidth);
34333 this.thumbEl.setStyle({
34334 width : width + 'px',
34335 height : height + 'px'
34342 setThumbBoxPosition : function()
34344 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
34345 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
34347 this.thumbEl.setLeft(x);
34348 this.thumbEl.setTop(y);
34352 baseRotateLevel : function()
34354 this.baseRotate = 1;
34357 typeof(this.exif) != 'undefined' &&
34358 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
34359 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
34361 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
34364 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
34368 baseScaleLevel : function()
34372 if(this.isDocument){
34374 if(this.baseRotate == 6 || this.baseRotate == 8){
34376 height = this.thumbEl.getHeight();
34377 this.baseScale = height / this.imageEl.OriginWidth;
34379 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
34380 width = this.thumbEl.getWidth();
34381 this.baseScale = width / this.imageEl.OriginHeight;
34387 height = this.thumbEl.getHeight();
34388 this.baseScale = height / this.imageEl.OriginHeight;
34390 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
34391 width = this.thumbEl.getWidth();
34392 this.baseScale = width / this.imageEl.OriginWidth;
34398 if(this.baseRotate == 6 || this.baseRotate == 8){
34400 width = this.thumbEl.getHeight();
34401 this.baseScale = width / this.imageEl.OriginHeight;
34403 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
34404 height = this.thumbEl.getWidth();
34405 this.baseScale = height / this.imageEl.OriginHeight;
34408 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34409 height = this.thumbEl.getWidth();
34410 this.baseScale = height / this.imageEl.OriginHeight;
34412 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
34413 width = this.thumbEl.getHeight();
34414 this.baseScale = width / this.imageEl.OriginWidth;
34421 width = this.thumbEl.getWidth();
34422 this.baseScale = width / this.imageEl.OriginWidth;
34424 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
34425 height = this.thumbEl.getHeight();
34426 this.baseScale = height / this.imageEl.OriginHeight;
34429 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
34431 height = this.thumbEl.getHeight();
34432 this.baseScale = height / this.imageEl.OriginHeight;
34434 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
34435 width = this.thumbEl.getWidth();
34436 this.baseScale = width / this.imageEl.OriginWidth;
34444 getScaleLevel : function()
34446 return this.baseScale * Math.pow(1.1, this.scale);
34449 onTouchStart : function(e)
34451 if(!this.canvasLoaded){
34452 this.beforeSelectFile(e);
34456 var touches = e.browserEvent.touches;
34462 if(touches.length == 1){
34463 this.onMouseDown(e);
34467 if(touches.length != 2){
34473 for(var i = 0, finger; finger = touches[i]; i++){
34474 coords.push(finger.pageX, finger.pageY);
34477 var x = Math.pow(coords[0] - coords[2], 2);
34478 var y = Math.pow(coords[1] - coords[3], 2);
34480 this.startDistance = Math.sqrt(x + y);
34482 this.startScale = this.scale;
34484 this.pinching = true;
34485 this.dragable = false;
34489 onTouchMove : function(e)
34491 if(!this.pinching && !this.dragable){
34495 var touches = e.browserEvent.touches;
34502 this.onMouseMove(e);
34508 for(var i = 0, finger; finger = touches[i]; i++){
34509 coords.push(finger.pageX, finger.pageY);
34512 var x = Math.pow(coords[0] - coords[2], 2);
34513 var y = Math.pow(coords[1] - coords[3], 2);
34515 this.endDistance = Math.sqrt(x + y);
34517 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
34519 if(!this.zoomable()){
34520 this.scale = this.startScale;
34528 onTouchEnd : function(e)
34530 this.pinching = false;
34531 this.dragable = false;
34535 process : function(file, crop)
34538 this.maskEl.mask(this.loadingText);
34541 this.xhr = new XMLHttpRequest();
34543 file.xhr = this.xhr;
34545 this.xhr.open(this.method, this.url, true);
34548 "Accept": "application/json",
34549 "Cache-Control": "no-cache",
34550 "X-Requested-With": "XMLHttpRequest"
34553 for (var headerName in headers) {
34554 var headerValue = headers[headerName];
34556 this.xhr.setRequestHeader(headerName, headerValue);
34562 this.xhr.onload = function()
34564 _this.xhrOnLoad(_this.xhr);
34567 this.xhr.onerror = function()
34569 _this.xhrOnError(_this.xhr);
34572 var formData = new FormData();
34574 formData.append('returnHTML', 'NO');
34577 formData.append('crop', crop);
34580 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
34581 formData.append(this.paramName, file, file.name);
34584 if(typeof(file.filename) != 'undefined'){
34585 formData.append('filename', file.filename);
34588 if(typeof(file.mimetype) != 'undefined'){
34589 formData.append('mimetype', file.mimetype);
34592 if(this.fireEvent('arrange', this, formData) != false){
34593 this.xhr.send(formData);
34597 xhrOnLoad : function(xhr)
34600 this.maskEl.unmask();
34603 if (xhr.readyState !== 4) {
34604 this.fireEvent('exception', this, xhr);
34608 var response = Roo.decode(xhr.responseText);
34610 if(!response.success){
34611 this.fireEvent('exception', this, xhr);
34615 var response = Roo.decode(xhr.responseText);
34617 this.fireEvent('upload', this, response);
34621 xhrOnError : function()
34624 this.maskEl.unmask();
34627 Roo.log('xhr on error');
34629 var response = Roo.decode(xhr.responseText);
34635 prepare : function(file)
34638 this.maskEl.mask(this.loadingText);
34644 if(typeof(file) === 'string'){
34645 this.loadCanvas(file);
34649 if(!file || !this.urlAPI){
34654 this.cropType = file.type;
34658 if(this.fireEvent('prepare', this, this.file) != false){
34660 var reader = new FileReader();
34662 reader.onload = function (e) {
34663 if (e.target.error) {
34664 Roo.log(e.target.error);
34668 var buffer = e.target.result,
34669 dataView = new DataView(buffer),
34671 maxOffset = dataView.byteLength - 4,
34675 if (dataView.getUint16(0) === 0xffd8) {
34676 while (offset < maxOffset) {
34677 markerBytes = dataView.getUint16(offset);
34679 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
34680 markerLength = dataView.getUint16(offset + 2) + 2;
34681 if (offset + markerLength > dataView.byteLength) {
34682 Roo.log('Invalid meta data: Invalid segment size.');
34686 if(markerBytes == 0xffe1){
34687 _this.parseExifData(
34694 offset += markerLength;
34704 var url = _this.urlAPI.createObjectURL(_this.file);
34706 _this.loadCanvas(url);
34711 reader.readAsArrayBuffer(this.file);
34717 parseExifData : function(dataView, offset, length)
34719 var tiffOffset = offset + 10,
34723 if (dataView.getUint32(offset + 4) !== 0x45786966) {
34724 // No Exif data, might be XMP data instead
34728 // Check for the ASCII code for "Exif" (0x45786966):
34729 if (dataView.getUint32(offset + 4) !== 0x45786966) {
34730 // No Exif data, might be XMP data instead
34733 if (tiffOffset + 8 > dataView.byteLength) {
34734 Roo.log('Invalid Exif data: Invalid segment size.');
34737 // Check for the two null bytes:
34738 if (dataView.getUint16(offset + 8) !== 0x0000) {
34739 Roo.log('Invalid Exif data: Missing byte alignment offset.');
34742 // Check the byte alignment:
34743 switch (dataView.getUint16(tiffOffset)) {
34745 littleEndian = true;
34748 littleEndian = false;
34751 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
34754 // Check for the TIFF tag marker (0x002A):
34755 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
34756 Roo.log('Invalid Exif data: Missing TIFF marker.');
34759 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
34760 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
34762 this.parseExifTags(
34765 tiffOffset + dirOffset,
34770 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
34775 if (dirOffset + 6 > dataView.byteLength) {
34776 Roo.log('Invalid Exif data: Invalid directory offset.');
34779 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
34780 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
34781 if (dirEndOffset + 4 > dataView.byteLength) {
34782 Roo.log('Invalid Exif data: Invalid directory size.');
34785 for (i = 0; i < tagsNumber; i += 1) {
34789 dirOffset + 2 + 12 * i, // tag offset
34793 // Return the offset to the next directory:
34794 return dataView.getUint32(dirEndOffset, littleEndian);
34797 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
34799 var tag = dataView.getUint16(offset, littleEndian);
34801 this.exif[tag] = this.getExifValue(
34805 dataView.getUint16(offset + 2, littleEndian), // tag type
34806 dataView.getUint32(offset + 4, littleEndian), // tag length
34811 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
34813 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
34822 Roo.log('Invalid Exif data: Invalid tag type.');
34826 tagSize = tagType.size * length;
34827 // Determine if the value is contained in the dataOffset bytes,
34828 // or if the value at the dataOffset is a pointer to the actual data:
34829 dataOffset = tagSize > 4 ?
34830 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
34831 if (dataOffset + tagSize > dataView.byteLength) {
34832 Roo.log('Invalid Exif data: Invalid data offset.');
34835 if (length === 1) {
34836 return tagType.getValue(dataView, dataOffset, littleEndian);
34839 for (i = 0; i < length; i += 1) {
34840 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
34843 if (tagType.ascii) {
34845 // Concatenate the chars:
34846 for (i = 0; i < values.length; i += 1) {
34848 // Ignore the terminating NULL byte(s):
34849 if (c === '\u0000') {
34861 Roo.apply(Roo.bootstrap.UploadCropbox, {
34863 'Orientation': 0x0112
34867 1: 0, //'top-left',
34869 3: 180, //'bottom-right',
34870 // 4: 'bottom-left',
34872 6: 90, //'right-top',
34873 // 7: 'right-bottom',
34874 8: 270 //'left-bottom'
34878 // byte, 8-bit unsigned int:
34880 getValue: function (dataView, dataOffset) {
34881 return dataView.getUint8(dataOffset);
34885 // ascii, 8-bit byte:
34887 getValue: function (dataView, dataOffset) {
34888 return String.fromCharCode(dataView.getUint8(dataOffset));
34893 // short, 16 bit int:
34895 getValue: function (dataView, dataOffset, littleEndian) {
34896 return dataView.getUint16(dataOffset, littleEndian);
34900 // long, 32 bit int:
34902 getValue: function (dataView, dataOffset, littleEndian) {
34903 return dataView.getUint32(dataOffset, littleEndian);
34907 // rational = two long values, first is numerator, second is denominator:
34909 getValue: function (dataView, dataOffset, littleEndian) {
34910 return dataView.getUint32(dataOffset, littleEndian) /
34911 dataView.getUint32(dataOffset + 4, littleEndian);
34915 // slong, 32 bit signed int:
34917 getValue: function (dataView, dataOffset, littleEndian) {
34918 return dataView.getInt32(dataOffset, littleEndian);
34922 // srational, two slongs, first is numerator, second is denominator:
34924 getValue: function (dataView, dataOffset, littleEndian) {
34925 return dataView.getInt32(dataOffset, littleEndian) /
34926 dataView.getInt32(dataOffset + 4, littleEndian);
34936 cls : 'btn-group roo-upload-cropbox-rotate-left',
34937 action : 'rotate-left',
34941 cls : 'btn btn-default',
34942 html : '<i class="fa fa-undo"></i>'
34948 cls : 'btn-group roo-upload-cropbox-picture',
34949 action : 'picture',
34953 cls : 'btn btn-default',
34954 html : '<i class="fa fa-picture-o"></i>'
34960 cls : 'btn-group roo-upload-cropbox-rotate-right',
34961 action : 'rotate-right',
34965 cls : 'btn btn-default',
34966 html : '<i class="fa fa-repeat"></i>'
34974 cls : 'btn-group roo-upload-cropbox-rotate-left',
34975 action : 'rotate-left',
34979 cls : 'btn btn-default',
34980 html : '<i class="fa fa-undo"></i>'
34986 cls : 'btn-group roo-upload-cropbox-download',
34987 action : 'download',
34991 cls : 'btn btn-default',
34992 html : '<i class="fa fa-download"></i>'
34998 cls : 'btn-group roo-upload-cropbox-crop',
35003 cls : 'btn btn-default',
35004 html : '<i class="fa fa-crop"></i>'
35010 cls : 'btn-group roo-upload-cropbox-trash',
35015 cls : 'btn btn-default',
35016 html : '<i class="fa fa-trash"></i>'
35022 cls : 'btn-group roo-upload-cropbox-rotate-right',
35023 action : 'rotate-right',
35027 cls : 'btn btn-default',
35028 html : '<i class="fa fa-repeat"></i>'
35036 cls : 'btn-group roo-upload-cropbox-rotate-left',
35037 action : 'rotate-left',
35041 cls : 'btn btn-default',
35042 html : '<i class="fa fa-undo"></i>'
35048 cls : 'btn-group roo-upload-cropbox-rotate-right',
35049 action : 'rotate-right',
35053 cls : 'btn btn-default',
35054 html : '<i class="fa fa-repeat"></i>'
35067 * @class Roo.bootstrap.DocumentManager
35068 * @extends Roo.bootstrap.Component
35069 * Bootstrap DocumentManager class
35070 * @cfg {String} paramName default 'imageUpload'
35071 * @cfg {String} toolTipName default 'filename'
35072 * @cfg {String} method default POST
35073 * @cfg {String} url action url
35074 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
35075 * @cfg {Boolean} multiple multiple upload default true
35076 * @cfg {Number} thumbSize default 300
35077 * @cfg {String} fieldLabel
35078 * @cfg {Number} labelWidth default 4
35079 * @cfg {String} labelAlign (left|top) default left
35080 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
35081 * @cfg {Number} labellg set the width of label (1-12)
35082 * @cfg {Number} labelmd set the width of label (1-12)
35083 * @cfg {Number} labelsm set the width of label (1-12)
35084 * @cfg {Number} labelxs set the width of label (1-12)
35087 * Create a new DocumentManager
35088 * @param {Object} config The config object
35091 Roo.bootstrap.DocumentManager = function(config){
35092 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
35095 this.delegates = [];
35100 * Fire when initial the DocumentManager
35101 * @param {Roo.bootstrap.DocumentManager} this
35106 * inspect selected file
35107 * @param {Roo.bootstrap.DocumentManager} this
35108 * @param {File} file
35113 * Fire when xhr load exception
35114 * @param {Roo.bootstrap.DocumentManager} this
35115 * @param {XMLHttpRequest} xhr
35117 "exception" : true,
35119 * @event afterupload
35120 * Fire when xhr load exception
35121 * @param {Roo.bootstrap.DocumentManager} this
35122 * @param {XMLHttpRequest} xhr
35124 "afterupload" : true,
35127 * prepare the form data
35128 * @param {Roo.bootstrap.DocumentManager} this
35129 * @param {Object} formData
35134 * Fire when remove the file
35135 * @param {Roo.bootstrap.DocumentManager} this
35136 * @param {Object} file
35141 * Fire after refresh the file
35142 * @param {Roo.bootstrap.DocumentManager} this
35147 * Fire after click the image
35148 * @param {Roo.bootstrap.DocumentManager} this
35149 * @param {Object} file
35154 * Fire when upload a image and editable set to true
35155 * @param {Roo.bootstrap.DocumentManager} this
35156 * @param {Object} file
35160 * @event beforeselectfile
35161 * Fire before select file
35162 * @param {Roo.bootstrap.DocumentManager} this
35164 "beforeselectfile" : true,
35167 * Fire before process file
35168 * @param {Roo.bootstrap.DocumentManager} this
35169 * @param {Object} file
35173 * @event previewrendered
35174 * Fire when preview rendered
35175 * @param {Roo.bootstrap.DocumentManager} this
35176 * @param {Object} file
35178 "previewrendered" : true,
35181 "previewResize" : true
35186 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
35195 paramName : 'imageUpload',
35196 toolTipName : 'filename',
35199 labelAlign : 'left',
35209 getAutoCreate : function()
35211 var managerWidget = {
35213 cls : 'roo-document-manager',
35217 cls : 'roo-document-manager-selector',
35222 cls : 'roo-document-manager-uploader',
35226 cls : 'roo-document-manager-upload-btn',
35227 html : '<i class="fa fa-plus"></i>'
35238 cls : 'column col-md-12',
35243 if(this.fieldLabel.length){
35248 cls : 'column col-md-12',
35249 html : this.fieldLabel
35253 cls : 'column col-md-12',
35258 if(this.labelAlign == 'left'){
35263 html : this.fieldLabel
35272 if(this.labelWidth > 12){
35273 content[0].style = "width: " + this.labelWidth + 'px';
35276 if(this.labelWidth < 13 && this.labelmd == 0){
35277 this.labelmd = this.labelWidth;
35280 if(this.labellg > 0){
35281 content[0].cls += ' col-lg-' + this.labellg;
35282 content[1].cls += ' col-lg-' + (12 - this.labellg);
35285 if(this.labelmd > 0){
35286 content[0].cls += ' col-md-' + this.labelmd;
35287 content[1].cls += ' col-md-' + (12 - this.labelmd);
35290 if(this.labelsm > 0){
35291 content[0].cls += ' col-sm-' + this.labelsm;
35292 content[1].cls += ' col-sm-' + (12 - this.labelsm);
35295 if(this.labelxs > 0){
35296 content[0].cls += ' col-xs-' + this.labelxs;
35297 content[1].cls += ' col-xs-' + (12 - this.labelxs);
35305 cls : 'row clearfix',
35313 initEvents : function()
35315 this.managerEl = this.el.select('.roo-document-manager', true).first();
35316 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35318 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
35319 this.selectorEl.hide();
35322 this.selectorEl.attr('multiple', 'multiple');
35325 this.selectorEl.on('change', this.onFileSelected, this);
35327 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
35328 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35330 this.uploader.on('click', this.onUploaderClick, this);
35332 this.renderProgressDialog();
35336 window.addEventListener("resize", function() { _this.refresh(); } );
35338 this.fireEvent('initial', this);
35341 renderProgressDialog : function()
35345 this.progressDialog = new Roo.bootstrap.Modal({
35346 cls : 'roo-document-manager-progress-dialog',
35347 allow_close : false,
35358 btnclick : function() {
35359 _this.uploadCancel();
35365 this.progressDialog.render(Roo.get(document.body));
35367 this.progress = new Roo.bootstrap.Progress({
35368 cls : 'roo-document-manager-progress',
35373 this.progress.render(this.progressDialog.getChildContainer());
35375 this.progressBar = new Roo.bootstrap.ProgressBar({
35376 cls : 'roo-document-manager-progress-bar',
35379 aria_valuemax : 12,
35383 this.progressBar.render(this.progress.getChildContainer());
35386 onUploaderClick : function(e)
35388 e.preventDefault();
35390 if(this.fireEvent('beforeselectfile', this) != false){
35391 this.selectorEl.dom.click();
35396 onFileSelected : function(e)
35398 e.preventDefault();
35400 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
35404 Roo.each(this.selectorEl.dom.files, function(file){
35405 if(this.fireEvent('inspect', this, file) != false){
35406 this.files.push(file);
35416 this.selectorEl.dom.value = '';
35418 if(!this.files || !this.files.length){
35422 if(this.boxes > 0 && this.files.length > this.boxes){
35423 this.files = this.files.slice(0, this.boxes);
35426 this.uploader.show();
35428 if(this.boxes > 0 && this.files.length > this.boxes - 1){
35429 this.uploader.hide();
35438 Roo.each(this.files, function(file){
35440 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35441 var f = this.renderPreview(file);
35446 if(file.type.indexOf('image') != -1){
35447 this.delegates.push(
35449 _this.process(file);
35450 }).createDelegate(this)
35458 _this.process(file);
35459 }).createDelegate(this)
35464 this.files = files;
35466 this.delegates = this.delegates.concat(docs);
35468 if(!this.delegates.length){
35473 this.progressBar.aria_valuemax = this.delegates.length;
35480 arrange : function()
35482 if(!this.delegates.length){
35483 this.progressDialog.hide();
35488 var delegate = this.delegates.shift();
35490 this.progressDialog.show();
35492 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
35494 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
35499 refresh : function()
35501 this.uploader.show();
35503 if(this.boxes > 0 && this.files.length > this.boxes - 1){
35504 this.uploader.hide();
35507 Roo.isTouch ? this.closable(false) : this.closable(true);
35509 this.fireEvent('refresh', this);
35512 onRemove : function(e, el, o)
35514 e.preventDefault();
35516 this.fireEvent('remove', this, o);
35520 remove : function(o)
35524 Roo.each(this.files, function(file){
35525 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
35534 this.files = files;
35541 Roo.each(this.files, function(file){
35546 file.target.remove();
35555 onClick : function(e, el, o)
35557 e.preventDefault();
35559 this.fireEvent('click', this, o);
35563 closable : function(closable)
35565 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
35567 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
35579 xhrOnLoad : function(xhr)
35581 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35585 if (xhr.readyState !== 4) {
35587 this.fireEvent('exception', this, xhr);
35591 var response = Roo.decode(xhr.responseText);
35593 if(!response.success){
35595 this.fireEvent('exception', this, xhr);
35599 var file = this.renderPreview(response.data);
35601 this.files.push(file);
35605 this.fireEvent('afterupload', this, xhr);
35609 xhrOnError : function(xhr)
35611 Roo.log('xhr on error');
35613 var response = Roo.decode(xhr.responseText);
35620 process : function(file)
35622 if(this.fireEvent('process', this, file) !== false){
35623 if(this.editable && file.type.indexOf('image') != -1){
35624 this.fireEvent('edit', this, file);
35628 this.uploadStart(file, false);
35635 uploadStart : function(file, crop)
35637 this.xhr = new XMLHttpRequest();
35639 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
35644 file.xhr = this.xhr;
35646 this.managerEl.createChild({
35648 cls : 'roo-document-manager-loading',
35652 tooltip : file.name,
35653 cls : 'roo-document-manager-thumb',
35654 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35660 this.xhr.open(this.method, this.url, true);
35663 "Accept": "application/json",
35664 "Cache-Control": "no-cache",
35665 "X-Requested-With": "XMLHttpRequest"
35668 for (var headerName in headers) {
35669 var headerValue = headers[headerName];
35671 this.xhr.setRequestHeader(headerName, headerValue);
35677 this.xhr.onload = function()
35679 _this.xhrOnLoad(_this.xhr);
35682 this.xhr.onerror = function()
35684 _this.xhrOnError(_this.xhr);
35687 var formData = new FormData();
35689 formData.append('returnHTML', 'NO');
35692 formData.append('crop', crop);
35695 formData.append(this.paramName, file, file.name);
35702 if(this.fireEvent('prepare', this, formData, options) != false){
35704 if(options.manually){
35708 this.xhr.send(formData);
35712 this.uploadCancel();
35715 uploadCancel : function()
35721 this.delegates = [];
35723 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
35730 renderPreview : function(file)
35732 if(typeof(file.target) != 'undefined' && file.target){
35736 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
35738 var previewEl = this.managerEl.createChild({
35740 cls : 'roo-document-manager-preview',
35744 tooltip : file[this.toolTipName],
35745 cls : 'roo-document-manager-thumb',
35746 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
35751 html : '<i class="fa fa-times-circle"></i>'
35756 var close = previewEl.select('button.close', true).first();
35758 close.on('click', this.onRemove, this, file);
35760 file.target = previewEl;
35762 var image = previewEl.select('img', true).first();
35766 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
35768 image.on('click', this.onClick, this, file);
35770 this.fireEvent('previewrendered', this, file);
35776 onPreviewLoad : function(file, image)
35778 if(typeof(file.target) == 'undefined' || !file.target){
35782 var width = image.dom.naturalWidth || image.dom.width;
35783 var height = image.dom.naturalHeight || image.dom.height;
35785 if(!this.previewResize) {
35789 if(width > height){
35790 file.target.addClass('wide');
35794 file.target.addClass('tall');
35799 uploadFromSource : function(file, crop)
35801 this.xhr = new XMLHttpRequest();
35803 this.managerEl.createChild({
35805 cls : 'roo-document-manager-loading',
35809 tooltip : file.name,
35810 cls : 'roo-document-manager-thumb',
35811 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
35817 this.xhr.open(this.method, this.url, true);
35820 "Accept": "application/json",
35821 "Cache-Control": "no-cache",
35822 "X-Requested-With": "XMLHttpRequest"
35825 for (var headerName in headers) {
35826 var headerValue = headers[headerName];
35828 this.xhr.setRequestHeader(headerName, headerValue);
35834 this.xhr.onload = function()
35836 _this.xhrOnLoad(_this.xhr);
35839 this.xhr.onerror = function()
35841 _this.xhrOnError(_this.xhr);
35844 var formData = new FormData();
35846 formData.append('returnHTML', 'NO');
35848 formData.append('crop', crop);
35850 if(typeof(file.filename) != 'undefined'){
35851 formData.append('filename', file.filename);
35854 if(typeof(file.mimetype) != 'undefined'){
35855 formData.append('mimetype', file.mimetype);
35860 if(this.fireEvent('prepare', this, formData) != false){
35861 this.xhr.send(formData);
35871 * @class Roo.bootstrap.DocumentViewer
35872 * @extends Roo.bootstrap.Component
35873 * Bootstrap DocumentViewer class
35874 * @cfg {Boolean} showDownload (true|false) show download button (default true)
35875 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
35878 * Create a new DocumentViewer
35879 * @param {Object} config The config object
35882 Roo.bootstrap.DocumentViewer = function(config){
35883 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
35888 * Fire after initEvent
35889 * @param {Roo.bootstrap.DocumentViewer} this
35895 * @param {Roo.bootstrap.DocumentViewer} this
35900 * Fire after download button
35901 * @param {Roo.bootstrap.DocumentViewer} this
35906 * Fire after trash button
35907 * @param {Roo.bootstrap.DocumentViewer} this
35914 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
35916 showDownload : true,
35920 getAutoCreate : function()
35924 cls : 'roo-document-viewer',
35928 cls : 'roo-document-viewer-body',
35932 cls : 'roo-document-viewer-thumb',
35936 cls : 'roo-document-viewer-image'
35944 cls : 'roo-document-viewer-footer',
35947 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
35951 cls : 'btn-group roo-document-viewer-download',
35955 cls : 'btn btn-default',
35956 html : '<i class="fa fa-download"></i>'
35962 cls : 'btn-group roo-document-viewer-trash',
35966 cls : 'btn btn-default',
35967 html : '<i class="fa fa-trash"></i>'
35980 initEvents : function()
35982 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
35983 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
35985 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
35986 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
35988 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
35989 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
35991 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
35992 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
35994 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
35995 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
35997 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
35998 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
36000 this.bodyEl.on('click', this.onClick, this);
36001 this.downloadBtn.on('click', this.onDownload, this);
36002 this.trashBtn.on('click', this.onTrash, this);
36004 this.downloadBtn.hide();
36005 this.trashBtn.hide();
36007 if(this.showDownload){
36008 this.downloadBtn.show();
36011 if(this.showTrash){
36012 this.trashBtn.show();
36015 if(!this.showDownload && !this.showTrash) {
36016 this.footerEl.hide();
36021 initial : function()
36023 this.fireEvent('initial', this);
36027 onClick : function(e)
36029 e.preventDefault();
36031 this.fireEvent('click', this);
36034 onDownload : function(e)
36036 e.preventDefault();
36038 this.fireEvent('download', this);
36041 onTrash : function(e)
36043 e.preventDefault();
36045 this.fireEvent('trash', this);
36057 * @class Roo.bootstrap.form.FieldLabel
36058 * @extends Roo.bootstrap.Component
36059 * Bootstrap FieldLabel class
36060 * @cfg {String} html contents of the element
36061 * @cfg {String} tag tag of the element default label
36062 * @cfg {String} cls class of the element
36063 * @cfg {String} target label target
36064 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
36065 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
36066 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
36067 * @cfg {String} iconTooltip default "This field is required"
36068 * @cfg {String} indicatorpos (left|right) default left
36071 * Create a new FieldLabel
36072 * @param {Object} config The config object
36075 Roo.bootstrap.form.FieldLabel = function(config){
36076 Roo.bootstrap.Element.superclass.constructor.call(this, config);
36081 * Fires after the field has been marked as invalid.
36082 * @param {Roo.form.FieldLabel} this
36083 * @param {String} msg The validation message
36088 * Fires after the field has been validated with no errors.
36089 * @param {Roo.form.FieldLabel} this
36095 Roo.extend(Roo.bootstrap.form.FieldLabel, Roo.bootstrap.Component, {
36102 invalidClass : 'has-warning',
36103 validClass : 'has-success',
36104 iconTooltip : 'This field is required',
36105 indicatorpos : 'left',
36107 getAutoCreate : function(){
36110 if (!this.allowBlank) {
36116 cls : 'roo-bootstrap-field-label ' + this.cls,
36121 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
36122 tooltip : this.iconTooltip
36131 if(this.indicatorpos == 'right'){
36134 cls : 'roo-bootstrap-field-label ' + this.cls,
36143 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
36144 tooltip : this.iconTooltip
36153 initEvents: function()
36155 Roo.bootstrap.Element.superclass.initEvents.call(this);
36157 this.indicator = this.indicatorEl();
36159 if(this.indicator){
36160 this.indicator.removeClass('visible');
36161 this.indicator.addClass('invisible');
36164 Roo.bootstrap.form.FieldLabel.register(this);
36167 indicatorEl : function()
36169 var indicator = this.el.select('i.roo-required-indicator',true).first();
36180 * Mark this field as valid
36182 markValid : function()
36184 if(this.indicator){
36185 this.indicator.removeClass('visible');
36186 this.indicator.addClass('invisible');
36188 if (Roo.bootstrap.version == 3) {
36189 this.el.removeClass(this.invalidClass);
36190 this.el.addClass(this.validClass);
36192 this.el.removeClass('is-invalid');
36193 this.el.addClass('is-valid');
36197 this.fireEvent('valid', this);
36201 * Mark this field as invalid
36202 * @param {String} msg The validation message
36204 markInvalid : function(msg)
36206 if(this.indicator){
36207 this.indicator.removeClass('invisible');
36208 this.indicator.addClass('visible');
36210 if (Roo.bootstrap.version == 3) {
36211 this.el.removeClass(this.validClass);
36212 this.el.addClass(this.invalidClass);
36214 this.el.removeClass('is-valid');
36215 this.el.addClass('is-invalid');
36219 this.fireEvent('invalid', this, msg);
36225 Roo.apply(Roo.bootstrap.form.FieldLabel, {
36230 * register a FieldLabel Group
36231 * @param {Roo.bootstrap.form.FieldLabel} the FieldLabel to add
36233 register : function(label)
36235 if(this.groups.hasOwnProperty(label.target)){
36239 this.groups[label.target] = label;
36243 * fetch a FieldLabel Group based on the target
36244 * @param {string} target
36245 * @returns {Roo.bootstrap.form.FieldLabel} the CheckBox group
36247 get: function(target) {
36248 if (typeof(this.groups[target]) == 'undefined') {
36252 return this.groups[target] ;
36261 * page DateSplitField.
36267 * @class Roo.bootstrap.form.DateSplitField
36268 * @extends Roo.bootstrap.Component
36269 * Bootstrap DateSplitField class
36270 * @cfg {string} fieldLabel - the label associated
36271 * @cfg {Number} labelWidth set the width of label (0-12)
36272 * @cfg {String} labelAlign (top|left)
36273 * @cfg {Boolean} dayAllowBlank (true|false) default false
36274 * @cfg {Boolean} monthAllowBlank (true|false) default false
36275 * @cfg {Boolean} yearAllowBlank (true|false) default false
36276 * @cfg {string} dayPlaceholder
36277 * @cfg {string} monthPlaceholder
36278 * @cfg {string} yearPlaceholder
36279 * @cfg {string} dayFormat default 'd'
36280 * @cfg {string} monthFormat default 'm'
36281 * @cfg {string} yearFormat default 'Y'
36282 * @cfg {Number} labellg set the width of label (1-12)
36283 * @cfg {Number} labelmd set the width of label (1-12)
36284 * @cfg {Number} labelsm set the width of label (1-12)
36285 * @cfg {Number} labelxs set the width of label (1-12)
36289 * Create a new DateSplitField
36290 * @param {Object} config The config object
36293 Roo.bootstrap.form.DateSplitField = function(config){
36294 Roo.bootstrap.form.DateSplitField.superclass.constructor.call(this, config);
36300 * getting the data of years
36301 * @param {Roo.bootstrap.form.DateSplitField} this
36302 * @param {Object} years
36307 * getting the data of days
36308 * @param {Roo.bootstrap.form.DateSplitField} this
36309 * @param {Object} days
36314 * Fires after the field has been marked as invalid.
36315 * @param {Roo.form.Field} this
36316 * @param {String} msg The validation message
36321 * Fires after the field has been validated with no errors.
36322 * @param {Roo.form.Field} this
36328 Roo.extend(Roo.bootstrap.form.DateSplitField, Roo.bootstrap.Component, {
36331 labelAlign : 'top',
36333 dayAllowBlank : false,
36334 monthAllowBlank : false,
36335 yearAllowBlank : false,
36336 dayPlaceholder : '',
36337 monthPlaceholder : '',
36338 yearPlaceholder : '',
36342 isFormField : true,
36348 getAutoCreate : function()
36352 cls : 'row roo-date-split-field-group',
36357 cls : 'form-hidden-field roo-date-split-field-group-value',
36363 var labelCls = 'col-md-12';
36364 var contentCls = 'col-md-4';
36366 if(this.fieldLabel){
36370 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
36374 html : this.fieldLabel
36379 if(this.labelAlign == 'left'){
36381 if(this.labelWidth > 12){
36382 label.style = "width: " + this.labelWidth + 'px';
36385 if(this.labelWidth < 13 && this.labelmd == 0){
36386 this.labelmd = this.labelWidth;
36389 if(this.labellg > 0){
36390 labelCls = ' col-lg-' + this.labellg;
36391 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
36394 if(this.labelmd > 0){
36395 labelCls = ' col-md-' + this.labelmd;
36396 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
36399 if(this.labelsm > 0){
36400 labelCls = ' col-sm-' + this.labelsm;
36401 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
36404 if(this.labelxs > 0){
36405 labelCls = ' col-xs-' + this.labelxs;
36406 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
36410 label.cls += ' ' + labelCls;
36412 cfg.cn.push(label);
36415 Roo.each(['day', 'month', 'year'], function(t){
36418 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
36425 inputEl: function ()
36427 return this.el.select('.roo-date-split-field-group-value', true).first();
36430 onRender : function(ct, position)
36434 Roo.bootstrap.DateSplitFiel.superclass.onRender.call(this, ct, position);
36436 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
36438 this.dayField = new Roo.bootstrap.form.ComboBox({
36439 allowBlank : this.dayAllowBlank,
36440 alwaysQuery : true,
36441 displayField : 'value',
36444 forceSelection : true,
36446 placeholder : this.dayPlaceholder,
36447 selectOnFocus : true,
36448 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36449 triggerAction : 'all',
36451 valueField : 'value',
36452 store : new Roo.data.SimpleStore({
36453 data : (function() {
36455 _this.fireEvent('days', _this, days);
36458 fields : [ 'value' ]
36461 select : function (_self, record, index)
36463 _this.setValue(_this.getValue());
36468 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
36470 this.monthField = new Roo.bootstrap.form.MonthField({
36471 after : '<i class=\"fa fa-calendar\"></i>',
36472 allowBlank : this.monthAllowBlank,
36473 placeholder : this.monthPlaceholder,
36476 render : function (_self)
36478 this.el.select('span.input-group-addon', true).first().on('click', function(e){
36479 e.preventDefault();
36483 select : function (_self, oldvalue, newvalue)
36485 _this.setValue(_this.getValue());
36490 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
36492 this.yearField = new Roo.bootstrap.form.ComboBox({
36493 allowBlank : this.yearAllowBlank,
36494 alwaysQuery : true,
36495 displayField : 'value',
36498 forceSelection : true,
36500 placeholder : this.yearPlaceholder,
36501 selectOnFocus : true,
36502 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
36503 triggerAction : 'all',
36505 valueField : 'value',
36506 store : new Roo.data.SimpleStore({
36507 data : (function() {
36509 _this.fireEvent('years', _this, years);
36512 fields : [ 'value' ]
36515 select : function (_self, record, index)
36517 _this.setValue(_this.getValue());
36522 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
36525 setValue : function(v, format)
36527 this.inputEl.dom.value = v;
36529 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
36531 var d = Date.parseDate(v, f);
36538 this.setDay(d.format(this.dayFormat));
36539 this.setMonth(d.format(this.monthFormat));
36540 this.setYear(d.format(this.yearFormat));
36547 setDay : function(v)
36549 this.dayField.setValue(v);
36550 this.inputEl.dom.value = this.getValue();
36555 setMonth : function(v)
36557 this.monthField.setValue(v, true);
36558 this.inputEl.dom.value = this.getValue();
36563 setYear : function(v)
36565 this.yearField.setValue(v);
36566 this.inputEl.dom.value = this.getValue();
36571 getDay : function()
36573 return this.dayField.getValue();
36576 getMonth : function()
36578 return this.monthField.getValue();
36581 getYear : function()
36583 return this.yearField.getValue();
36586 getValue : function()
36588 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
36590 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
36600 this.inputEl.dom.value = '';
36605 validate : function()
36607 var d = this.dayField.validate();
36608 var m = this.monthField.validate();
36609 var y = this.yearField.validate();
36614 (!this.dayAllowBlank && !d) ||
36615 (!this.monthAllowBlank && !m) ||
36616 (!this.yearAllowBlank && !y)
36621 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
36630 this.markInvalid();
36635 markValid : function()
36638 var label = this.el.select('label', true).first();
36639 var icon = this.el.select('i.fa-star', true).first();
36645 this.fireEvent('valid', this);
36649 * Mark this field as invalid
36650 * @param {String} msg The validation message
36652 markInvalid : function(msg)
36655 var label = this.el.select('label', true).first();
36656 var icon = this.el.select('i.fa-star', true).first();
36658 if(label && !icon){
36659 this.el.select('.roo-date-split-field-label', true).createChild({
36661 cls : 'text-danger fa fa-lg fa-star',
36662 tooltip : 'This field is required',
36663 style : 'margin-right:5px;'
36667 this.fireEvent('invalid', this, msg);
36670 clearInvalid : function()
36672 var label = this.el.select('label', true).first();
36673 var icon = this.el.select('i.fa-star', true).first();
36679 this.fireEvent('valid', this);
36682 getName: function()
36692 * @class Roo.bootstrap.LayoutMasonry
36693 * @extends Roo.bootstrap.Component
36694 * @children Roo.bootstrap.Element Roo.bootstrap.Img Roo.bootstrap.MasonryBrick
36695 * Bootstrap Layout Masonry class
36698 * http://masonry.desandro.com
36700 * The idea is to render all the bricks based on vertical width...
36702 * The original code extends 'outlayer' - we might need to use that....
36705 * Create a new Element
36706 * @param {Object} config The config object
36709 Roo.bootstrap.LayoutMasonry = function(config){
36711 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
36715 Roo.bootstrap.LayoutMasonry.register(this);
36721 * Fire after layout the items
36722 * @param {Roo.bootstrap.LayoutMasonry} this
36723 * @param {Roo.EventObject} e
36730 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
36733 * @cfg {Boolean} isLayoutInstant = no animation?
36735 isLayoutInstant : false, // needed?
36738 * @cfg {Number} boxWidth width of the columns
36743 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
36748 * @cfg {Number} padWidth padding below box..
36753 * @cfg {Number} gutter gutter width..
36758 * @cfg {Number} maxCols maximum number of columns
36764 * @cfg {Boolean} isAutoInitial defalut true
36766 isAutoInitial : true,
36771 * @cfg {Boolean} isHorizontal defalut false
36773 isHorizontal : false,
36775 currentSize : null,
36781 bricks: null, //CompositeElement
36785 _isLayoutInited : false,
36787 // isAlternative : false, // only use for vertical layout...
36790 * @cfg {Number} alternativePadWidth padding below box..
36792 alternativePadWidth : 50,
36794 selectedBrick : [],
36796 getAutoCreate : function(){
36798 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
36802 cls: 'blog-masonary-wrapper ' + this.cls,
36804 cls : 'mas-boxes masonary'
36811 getChildContainer: function( )
36813 if (this.boxesEl) {
36814 return this.boxesEl;
36817 this.boxesEl = this.el.select('.mas-boxes').first();
36819 return this.boxesEl;
36823 initEvents : function()
36827 if(this.isAutoInitial){
36828 Roo.log('hook children rendered');
36829 this.on('childrenrendered', function() {
36830 Roo.log('children rendered');
36836 initial : function()
36838 this.selectedBrick = [];
36840 this.currentSize = this.el.getBox(true);
36842 Roo.EventManager.onWindowResize(this.resize, this);
36844 if(!this.isAutoInitial){
36852 //this.layout.defer(500,this);
36856 resize : function()
36858 var cs = this.el.getBox(true);
36861 this.currentSize.width == cs.width &&
36862 this.currentSize.x == cs.x &&
36863 this.currentSize.height == cs.height &&
36864 this.currentSize.y == cs.y
36866 Roo.log("no change in with or X or Y");
36870 this.currentSize = cs;
36876 layout : function()
36878 this._resetLayout();
36880 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
36882 this.layoutItems( isInstant );
36884 this._isLayoutInited = true;
36886 this.fireEvent('layout', this);
36890 _resetLayout : function()
36892 if(this.isHorizontal){
36893 this.horizontalMeasureColumns();
36897 this.verticalMeasureColumns();
36901 verticalMeasureColumns : function()
36903 this.getContainerWidth();
36905 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36906 // this.colWidth = Math.floor(this.containerWidth * 0.8);
36910 var boxWidth = this.boxWidth + this.padWidth;
36912 if(this.containerWidth < this.boxWidth){
36913 boxWidth = this.containerWidth
36916 var containerWidth = this.containerWidth;
36918 var cols = Math.floor(containerWidth / boxWidth);
36920 this.cols = Math.max( cols, 1 );
36922 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
36924 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
36926 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
36928 this.colWidth = boxWidth + avail - this.padWidth;
36930 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
36931 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
36934 horizontalMeasureColumns : function()
36936 this.getContainerWidth();
36938 var boxWidth = this.boxWidth;
36940 if(this.containerWidth < boxWidth){
36941 boxWidth = this.containerWidth;
36944 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
36946 this.el.setHeight(boxWidth);
36950 getContainerWidth : function()
36952 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
36955 layoutItems : function( isInstant )
36957 Roo.log(this.bricks);
36959 var items = Roo.apply([], this.bricks);
36961 if(this.isHorizontal){
36962 this._horizontalLayoutItems( items , isInstant );
36966 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
36967 // this._verticalAlternativeLayoutItems( items , isInstant );
36971 this._verticalLayoutItems( items , isInstant );
36975 _verticalLayoutItems : function ( items , isInstant)
36977 if ( !items || !items.length ) {
36982 ['xs', 'xs', 'xs', 'tall'],
36983 ['xs', 'xs', 'tall'],
36984 ['xs', 'xs', 'sm'],
36985 ['xs', 'xs', 'xs'],
36991 ['sm', 'xs', 'xs'],
36995 ['tall', 'xs', 'xs', 'xs'],
36996 ['tall', 'xs', 'xs'],
37008 Roo.each(items, function(item, k){
37010 switch (item.size) {
37011 // these layouts take up a full box,
37022 boxes.push([item]);
37045 var filterPattern = function(box, length)
37053 var pattern = box.slice(0, length);
37057 Roo.each(pattern, function(i){
37058 format.push(i.size);
37061 Roo.each(standard, function(s){
37063 if(String(s) != String(format)){
37072 if(!match && length == 1){
37077 filterPattern(box, length - 1);
37081 queue.push(pattern);
37083 box = box.slice(length, box.length);
37085 filterPattern(box, 4);
37091 Roo.each(boxes, function(box, k){
37097 if(box.length == 1){
37102 filterPattern(box, 4);
37106 this._processVerticalLayoutQueue( queue, isInstant );
37110 // _verticalAlternativeLayoutItems : function( items , isInstant )
37112 // if ( !items || !items.length ) {
37116 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
37120 _horizontalLayoutItems : function ( items , isInstant)
37122 if ( !items || !items.length || items.length < 3) {
37128 var eItems = items.slice(0, 3);
37130 items = items.slice(3, items.length);
37133 ['xs', 'xs', 'xs', 'wide'],
37134 ['xs', 'xs', 'wide'],
37135 ['xs', 'xs', 'sm'],
37136 ['xs', 'xs', 'xs'],
37142 ['sm', 'xs', 'xs'],
37146 ['wide', 'xs', 'xs', 'xs'],
37147 ['wide', 'xs', 'xs'],
37160 Roo.each(items, function(item, k){
37162 switch (item.size) {
37173 boxes.push([item]);
37197 var filterPattern = function(box, length)
37205 var pattern = box.slice(0, length);
37209 Roo.each(pattern, function(i){
37210 format.push(i.size);
37213 Roo.each(standard, function(s){
37215 if(String(s) != String(format)){
37224 if(!match && length == 1){
37229 filterPattern(box, length - 1);
37233 queue.push(pattern);
37235 box = box.slice(length, box.length);
37237 filterPattern(box, 4);
37243 Roo.each(boxes, function(box, k){
37249 if(box.length == 1){
37254 filterPattern(box, 4);
37261 var pos = this.el.getBox(true);
37265 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37267 var hit_end = false;
37269 Roo.each(queue, function(box){
37273 Roo.each(box, function(b){
37275 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37285 Roo.each(box, function(b){
37287 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37290 mx = Math.max(mx, b.x);
37294 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
37298 Roo.each(box, function(b){
37300 b.el.setVisibilityMode(Roo.Element.DISPLAY);
37314 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
37317 /** Sets position of item in DOM
37318 * @param {Element} item
37319 * @param {Number} x - horizontal position
37320 * @param {Number} y - vertical position
37321 * @param {Boolean} isInstant - disables transitions
37323 _processVerticalLayoutQueue : function( queue, isInstant )
37325 var pos = this.el.getBox(true);
37330 for (var i = 0; i < this.cols; i++){
37334 Roo.each(queue, function(box, k){
37336 var col = k % this.cols;
37338 Roo.each(box, function(b,kk){
37340 b.el.position('absolute');
37342 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37343 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37345 if(b.size == 'md-left' || b.size == 'md-right'){
37346 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37347 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37350 b.el.setWidth(width);
37351 b.el.setHeight(height);
37353 b.el.select('iframe',true).setSize(width,height);
37357 for (var i = 0; i < this.cols; i++){
37359 if(maxY[i] < maxY[col]){
37364 col = Math.min(col, i);
37368 x = pos.x + col * (this.colWidth + this.padWidth);
37372 var positions = [];
37374 switch (box.length){
37376 positions = this.getVerticalOneBoxColPositions(x, y, box);
37379 positions = this.getVerticalTwoBoxColPositions(x, y, box);
37382 positions = this.getVerticalThreeBoxColPositions(x, y, box);
37385 positions = this.getVerticalFourBoxColPositions(x, y, box);
37391 Roo.each(box, function(b,kk){
37393 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37395 var sz = b.el.getSize();
37397 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
37405 for (var i = 0; i < this.cols; i++){
37406 mY = Math.max(mY, maxY[i]);
37409 this.el.setHeight(mY - pos.y);
37413 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
37415 // var pos = this.el.getBox(true);
37418 // var maxX = pos.right;
37420 // var maxHeight = 0;
37422 // Roo.each(items, function(item, k){
37426 // item.el.position('absolute');
37428 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
37430 // item.el.setWidth(width);
37432 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
37434 // item.el.setHeight(height);
37437 // item.el.setXY([x, y], isInstant ? false : true);
37439 // item.el.setXY([maxX - width, y], isInstant ? false : true);
37442 // y = y + height + this.alternativePadWidth;
37444 // maxHeight = maxHeight + height + this.alternativePadWidth;
37448 // this.el.setHeight(maxHeight);
37452 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
37454 var pos = this.el.getBox(true);
37459 var maxX = pos.right;
37461 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
37463 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
37465 Roo.each(queue, function(box, k){
37467 Roo.each(box, function(b, kk){
37469 b.el.position('absolute');
37471 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37472 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37474 if(b.size == 'md-left' || b.size == 'md-right'){
37475 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
37476 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
37479 b.el.setWidth(width);
37480 b.el.setHeight(height);
37488 var positions = [];
37490 switch (box.length){
37492 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
37495 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
37498 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
37501 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
37507 Roo.each(box, function(b,kk){
37509 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
37511 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
37519 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
37521 Roo.each(eItems, function(b,k){
37523 b.size = (k == 0) ? 'sm' : 'xs';
37524 b.x = (k == 0) ? 2 : 1;
37525 b.y = (k == 0) ? 2 : 1;
37527 b.el.position('absolute');
37529 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
37531 b.el.setWidth(width);
37533 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
37535 b.el.setHeight(height);
37539 var positions = [];
37542 x : maxX - this.unitWidth * 2 - this.gutter,
37547 x : maxX - this.unitWidth,
37548 y : minY + (this.unitWidth + this.gutter) * 2
37552 x : maxX - this.unitWidth * 3 - this.gutter * 2,
37556 Roo.each(eItems, function(b,k){
37558 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
37564 getVerticalOneBoxColPositions : function(x, y, box)
37568 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
37570 if(box[0].size == 'md-left'){
37574 if(box[0].size == 'md-right'){
37579 x : x + (this.unitWidth + this.gutter) * rand,
37586 getVerticalTwoBoxColPositions : function(x, y, box)
37590 if(box[0].size == 'xs'){
37594 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
37598 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
37612 x : x + (this.unitWidth + this.gutter) * 2,
37613 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
37620 getVerticalThreeBoxColPositions : function(x, y, box)
37624 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37632 x : x + (this.unitWidth + this.gutter) * 1,
37637 x : x + (this.unitWidth + this.gutter) * 2,
37645 if(box[0].size == 'xs' && box[1].size == 'xs'){
37654 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
37658 x : x + (this.unitWidth + this.gutter) * 1,
37672 x : x + (this.unitWidth + this.gutter) * 2,
37677 x : x + (this.unitWidth + this.gutter) * 2,
37678 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
37685 getVerticalFourBoxColPositions : function(x, y, box)
37689 if(box[0].size == 'xs'){
37698 y : y + (this.unitHeight + this.gutter) * 1
37703 y : y + (this.unitHeight + this.gutter) * 2
37707 x : x + (this.unitWidth + this.gutter) * 1,
37721 x : x + (this.unitWidth + this.gutter) * 2,
37726 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
37727 y : y + (this.unitHeight + this.gutter) * 1
37731 x : x + (this.unitWidth + this.gutter) * 2,
37732 y : y + (this.unitWidth + this.gutter) * 2
37739 getHorizontalOneBoxColPositions : function(maxX, minY, box)
37743 if(box[0].size == 'md-left'){
37745 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37752 if(box[0].size == 'md-right'){
37754 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
37755 y : minY + (this.unitWidth + this.gutter) * 1
37761 var rand = Math.floor(Math.random() * (4 - box[0].y));
37764 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37765 y : minY + (this.unitWidth + this.gutter) * rand
37772 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
37776 if(box[0].size == 'xs'){
37779 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37784 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37785 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
37793 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37798 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37799 y : minY + (this.unitWidth + this.gutter) * 2
37806 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
37810 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
37813 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37818 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37819 y : minY + (this.unitWidth + this.gutter) * 1
37823 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37824 y : minY + (this.unitWidth + this.gutter) * 2
37831 if(box[0].size == 'xs' && box[1].size == 'xs'){
37834 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37839 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37844 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37845 y : minY + (this.unitWidth + this.gutter) * 1
37853 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37858 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37859 y : minY + (this.unitWidth + this.gutter) * 2
37863 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37864 y : minY + (this.unitWidth + this.gutter) * 2
37871 getHorizontalFourBoxColPositions : function(maxX, minY, box)
37875 if(box[0].size == 'xs'){
37878 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37883 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37888 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),
37893 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
37894 y : minY + (this.unitWidth + this.gutter) * 1
37902 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
37907 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
37908 y : minY + (this.unitWidth + this.gutter) * 2
37912 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
37913 y : minY + (this.unitWidth + this.gutter) * 2
37917 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),
37918 y : minY + (this.unitWidth + this.gutter) * 2
37926 * remove a Masonry Brick
37927 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
37929 removeBrick : function(brick_id)
37935 for (var i = 0; i<this.bricks.length; i++) {
37936 if (this.bricks[i].id == brick_id) {
37937 this.bricks.splice(i,1);
37938 this.el.dom.removeChild(Roo.get(brick_id).dom);
37945 * adds a Masonry Brick
37946 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37948 addBrick : function(cfg)
37950 var cn = new Roo.bootstrap.MasonryBrick(cfg);
37951 //this.register(cn);
37952 cn.parentId = this.id;
37953 cn.render(this.el);
37958 * register a Masonry Brick
37959 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
37962 register : function(brick)
37964 this.bricks.push(brick);
37965 brick.masonryId = this.id;
37969 * clear all the Masonry Brick
37971 clearAll : function()
37974 //this.getChildContainer().dom.innerHTML = "";
37975 this.el.dom.innerHTML = '';
37978 getSelected : function()
37980 if (!this.selectedBrick) {
37984 return this.selectedBrick;
37988 Roo.apply(Roo.bootstrap.LayoutMasonry, {
37992 * register a Masonry Layout
37993 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
37996 register : function(layout)
37998 this.groups[layout.id] = layout;
38001 * fetch a Masonry Layout based on the masonry layout ID
38002 * @param {string} the masonry layout to add
38003 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
38006 get: function(layout_id) {
38007 if (typeof(this.groups[layout_id]) == 'undefined') {
38010 return this.groups[layout_id] ;
38022 * http://masonry.desandro.com
38024 * The idea is to render all the bricks based on vertical width...
38026 * The original code extends 'outlayer' - we might need to use that....
38032 * @class Roo.bootstrap.LayoutMasonryAuto
38033 * @extends Roo.bootstrap.Component
38034 * Bootstrap Layout Masonry class
38037 * Create a new Element
38038 * @param {Object} config The config object
38041 Roo.bootstrap.LayoutMasonryAuto = function(config){
38042 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
38045 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
38048 * @cfg {Boolean} isFitWidth - resize the width..
38050 isFitWidth : false, // options..
38052 * @cfg {Boolean} isOriginLeft = left align?
38054 isOriginLeft : true,
38056 * @cfg {Boolean} isOriginTop = top align?
38058 isOriginTop : false,
38060 * @cfg {Boolean} isLayoutInstant = no animation?
38062 isLayoutInstant : false, // needed?
38064 * @cfg {Boolean} isResizingContainer = not sure if this is used..
38066 isResizingContainer : true,
38068 * @cfg {Number} columnWidth width of the columns
38074 * @cfg {Number} maxCols maximum number of columns
38079 * @cfg {Number} padHeight padding below box..
38085 * @cfg {Boolean} isAutoInitial defalut true
38088 isAutoInitial : true,
38094 initialColumnWidth : 0,
38095 currentSize : null,
38097 colYs : null, // array.
38104 bricks: null, //CompositeElement
38105 cols : 0, // array?
38106 // element : null, // wrapped now this.el
38107 _isLayoutInited : null,
38110 getAutoCreate : function(){
38114 cls: 'blog-masonary-wrapper ' + this.cls,
38116 cls : 'mas-boxes masonary'
38123 getChildContainer: function( )
38125 if (this.boxesEl) {
38126 return this.boxesEl;
38129 this.boxesEl = this.el.select('.mas-boxes').first();
38131 return this.boxesEl;
38135 initEvents : function()
38139 if(this.isAutoInitial){
38140 Roo.log('hook children rendered');
38141 this.on('childrenrendered', function() {
38142 Roo.log('children rendered');
38149 initial : function()
38151 this.reloadItems();
38153 this.currentSize = this.el.getBox(true);
38155 /// was window resize... - let's see if this works..
38156 Roo.EventManager.onWindowResize(this.resize, this);
38158 if(!this.isAutoInitial){
38163 this.layout.defer(500,this);
38166 reloadItems: function()
38168 this.bricks = this.el.select('.masonry-brick', true);
38170 this.bricks.each(function(b) {
38171 //Roo.log(b.getSize());
38172 if (!b.attr('originalwidth')) {
38173 b.attr('originalwidth', b.getSize().width);
38178 Roo.log(this.bricks.elements.length);
38181 resize : function()
38184 var cs = this.el.getBox(true);
38186 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
38187 Roo.log("no change in with or X");
38190 this.currentSize = cs;
38194 layout : function()
38197 this._resetLayout();
38198 //this._manageStamps();
38200 // don't animate first layout
38201 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
38202 this.layoutItems( isInstant );
38204 // flag for initalized
38205 this._isLayoutInited = true;
38208 layoutItems : function( isInstant )
38210 //var items = this._getItemsForLayout( this.items );
38211 // original code supports filtering layout items.. we just ignore it..
38213 this._layoutItems( this.bricks , isInstant );
38215 this._postLayout();
38217 _layoutItems : function ( items , isInstant)
38219 //this.fireEvent( 'layout', this, items );
38222 if ( !items || !items.elements.length ) {
38223 // no items, emit event with empty array
38228 items.each(function(item) {
38229 Roo.log("layout item");
38231 // get x/y object from method
38232 var position = this._getItemLayoutPosition( item );
38234 position.item = item;
38235 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
38236 queue.push( position );
38239 this._processLayoutQueue( queue );
38241 /** Sets position of item in DOM
38242 * @param {Element} item
38243 * @param {Number} x - horizontal position
38244 * @param {Number} y - vertical position
38245 * @param {Boolean} isInstant - disables transitions
38247 _processLayoutQueue : function( queue )
38249 for ( var i=0, len = queue.length; i < len; i++ ) {
38250 var obj = queue[i];
38251 obj.item.position('absolute');
38252 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
38258 * Any logic you want to do after each layout,
38259 * i.e. size the container
38261 _postLayout : function()
38263 this.resizeContainer();
38266 resizeContainer : function()
38268 if ( !this.isResizingContainer ) {
38271 var size = this._getContainerSize();
38273 this.el.setSize(size.width,size.height);
38274 this.boxesEl.setSize(size.width,size.height);
38280 _resetLayout : function()
38282 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
38283 this.colWidth = this.el.getWidth();
38284 //this.gutter = this.el.getWidth();
38286 this.measureColumns();
38292 this.colYs.push( 0 );
38298 measureColumns : function()
38300 this.getContainerWidth();
38301 // if columnWidth is 0, default to outerWidth of first item
38302 if ( !this.columnWidth ) {
38303 var firstItem = this.bricks.first();
38304 Roo.log(firstItem);
38305 this.columnWidth = this.containerWidth;
38306 if (firstItem && firstItem.attr('originalwidth') ) {
38307 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
38309 // columnWidth fall back to item of first element
38310 Roo.log("set column width?");
38311 this.initialColumnWidth = this.columnWidth ;
38313 // if first elem has no width, default to size of container
38318 if (this.initialColumnWidth) {
38319 this.columnWidth = this.initialColumnWidth;
38324 // column width is fixed at the top - however if container width get's smaller we should
38327 // this bit calcs how man columns..
38329 var columnWidth = this.columnWidth += this.gutter;
38331 // calculate columns
38332 var containerWidth = this.containerWidth + this.gutter;
38334 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
38335 // fix rounding errors, typically with gutters
38336 var excess = columnWidth - containerWidth % columnWidth;
38339 // if overshoot is less than a pixel, round up, otherwise floor it
38340 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
38341 cols = Math[ mathMethod ]( cols );
38342 this.cols = Math.max( cols, 1 );
38343 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
38345 // padding positioning..
38346 var totalColWidth = this.cols * this.columnWidth;
38347 var padavail = this.containerWidth - totalColWidth;
38348 // so for 2 columns - we need 3 'pads'
38350 var padNeeded = (1+this.cols) * this.padWidth;
38352 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
38354 this.columnWidth += padExtra
38355 //this.padWidth = Math.floor(padavail / ( this.cols));
38357 // adjust colum width so that padding is fixed??
38359 // we have 3 columns ... total = width * 3
38360 // we have X left over... that should be used by
38362 //if (this.expandC) {
38370 getContainerWidth : function()
38372 /* // container is parent if fit width
38373 var container = this.isFitWidth ? this.element.parentNode : this.element;
38374 // check that this.size and size are there
38375 // IE8 triggers resize on body size change, so they might not be
38377 var size = getSize( container ); //FIXME
38378 this.containerWidth = size && size.innerWidth; //FIXME
38381 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
38385 _getItemLayoutPosition : function( item ) // what is item?
38387 // we resize the item to our columnWidth..
38389 item.setWidth(this.columnWidth);
38390 item.autoBoxAdjust = false;
38392 var sz = item.getSize();
38394 // how many columns does this brick span
38395 var remainder = this.containerWidth % this.columnWidth;
38397 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
38398 // round if off by 1 pixel, otherwise use ceil
38399 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
38400 colSpan = Math.min( colSpan, this.cols );
38402 // normally this should be '1' as we dont' currently allow multi width columns..
38404 var colGroup = this._getColGroup( colSpan );
38405 // get the minimum Y value from the columns
38406 var minimumY = Math.min.apply( Math, colGroup );
38407 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
38409 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
38411 // position the brick
38413 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
38414 y: this.currentSize.y + minimumY + this.padHeight
38418 // apply setHeight to necessary columns
38419 var setHeight = minimumY + sz.height + this.padHeight;
38420 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
38422 var setSpan = this.cols + 1 - colGroup.length;
38423 for ( var i = 0; i < setSpan; i++ ) {
38424 this.colYs[ shortColIndex + i ] = setHeight ;
38431 * @param {Number} colSpan - number of columns the element spans
38432 * @returns {Array} colGroup
38434 _getColGroup : function( colSpan )
38436 if ( colSpan < 2 ) {
38437 // if brick spans only one column, use all the column Ys
38442 // how many different places could this brick fit horizontally
38443 var groupCount = this.cols + 1 - colSpan;
38444 // for each group potential horizontal position
38445 for ( var i = 0; i < groupCount; i++ ) {
38446 // make an array of colY values for that one group
38447 var groupColYs = this.colYs.slice( i, i + colSpan );
38448 // and get the max value of the array
38449 colGroup[i] = Math.max.apply( Math, groupColYs );
38454 _manageStamp : function( stamp )
38456 var stampSize = stamp.getSize();
38457 var offset = stamp.getBox();
38458 // get the columns that this stamp affects
38459 var firstX = this.isOriginLeft ? offset.x : offset.right;
38460 var lastX = firstX + stampSize.width;
38461 var firstCol = Math.floor( firstX / this.columnWidth );
38462 firstCol = Math.max( 0, firstCol );
38464 var lastCol = Math.floor( lastX / this.columnWidth );
38465 // lastCol should not go over if multiple of columnWidth #425
38466 lastCol -= lastX % this.columnWidth ? 0 : 1;
38467 lastCol = Math.min( this.cols - 1, lastCol );
38469 // set colYs to bottom of the stamp
38470 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
38473 for ( var i = firstCol; i <= lastCol; i++ ) {
38474 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
38479 _getContainerSize : function()
38481 this.maxY = Math.max.apply( Math, this.colYs );
38486 if ( this.isFitWidth ) {
38487 size.width = this._getContainerFitWidth();
38493 _getContainerFitWidth : function()
38495 var unusedCols = 0;
38496 // count unused columns
38499 if ( this.colYs[i] !== 0 ) {
38504 // fit container to columns that have been used
38505 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
38508 needsResizeLayout : function()
38510 var previousWidth = this.containerWidth;
38511 this.getContainerWidth();
38512 return previousWidth !== this.containerWidth;
38527 * @class Roo.bootstrap.MasonryBrick
38528 * @extends Roo.bootstrap.Component
38529 * Bootstrap MasonryBrick class
38532 * Create a new MasonryBrick
38533 * @param {Object} config The config object
38536 Roo.bootstrap.MasonryBrick = function(config){
38538 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
38540 Roo.bootstrap.MasonryBrick.register(this);
38546 * When a MasonryBrick is clcik
38547 * @param {Roo.bootstrap.MasonryBrick} this
38548 * @param {Roo.EventObject} e
38554 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
38557 * @cfg {String} title
38561 * @cfg {String} html
38565 * @cfg {String} bgimage
38569 * @cfg {String} videourl
38573 * @cfg {String} cls
38577 * @cfg {String} href
38581 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
38586 * @cfg {String} placetitle (center|bottom)
38591 * @cfg {Boolean} isFitContainer defalut true
38593 isFitContainer : true,
38596 * @cfg {Boolean} preventDefault defalut false
38598 preventDefault : false,
38601 * @cfg {Boolean} inverse defalut false
38603 maskInverse : false,
38605 getAutoCreate : function()
38607 if(!this.isFitContainer){
38608 return this.getSplitAutoCreate();
38611 var cls = 'masonry-brick masonry-brick-full';
38613 if(this.href.length){
38614 cls += ' masonry-brick-link';
38617 if(this.bgimage.length){
38618 cls += ' masonry-brick-image';
38621 if(this.maskInverse){
38622 cls += ' mask-inverse';
38625 if(!this.html.length && !this.maskInverse && !this.videourl.length){
38626 cls += ' enable-mask';
38630 cls += ' masonry-' + this.size + '-brick';
38633 if(this.placetitle.length){
38635 switch (this.placetitle) {
38637 cls += ' masonry-center-title';
38640 cls += ' masonry-bottom-title';
38647 if(!this.html.length && !this.bgimage.length){
38648 cls += ' masonry-center-title';
38651 if(!this.html.length && this.bgimage.length){
38652 cls += ' masonry-bottom-title';
38657 cls += ' ' + this.cls;
38661 tag: (this.href.length) ? 'a' : 'div',
38666 cls: 'masonry-brick-mask'
38670 cls: 'masonry-brick-paragraph',
38676 if(this.href.length){
38677 cfg.href = this.href;
38680 var cn = cfg.cn[1].cn;
38682 if(this.title.length){
38685 cls: 'masonry-brick-title',
38690 if(this.html.length){
38693 cls: 'masonry-brick-text',
38698 if (!this.title.length && !this.html.length) {
38699 cfg.cn[1].cls += ' hide';
38702 if(this.bgimage.length){
38705 cls: 'masonry-brick-image-view',
38710 if(this.videourl.length){
38711 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38712 // youtube support only?
38715 cls: 'masonry-brick-image-view',
38718 allowfullscreen : true
38726 getSplitAutoCreate : function()
38728 var cls = 'masonry-brick masonry-brick-split';
38730 if(this.href.length){
38731 cls += ' masonry-brick-link';
38734 if(this.bgimage.length){
38735 cls += ' masonry-brick-image';
38739 cls += ' masonry-' + this.size + '-brick';
38742 switch (this.placetitle) {
38744 cls += ' masonry-center-title';
38747 cls += ' masonry-bottom-title';
38750 if(!this.bgimage.length){
38751 cls += ' masonry-center-title';
38754 if(this.bgimage.length){
38755 cls += ' masonry-bottom-title';
38761 cls += ' ' + this.cls;
38765 tag: (this.href.length) ? 'a' : 'div',
38770 cls: 'masonry-brick-split-head',
38774 cls: 'masonry-brick-paragraph',
38781 cls: 'masonry-brick-split-body',
38787 if(this.href.length){
38788 cfg.href = this.href;
38791 if(this.title.length){
38792 cfg.cn[0].cn[0].cn.push({
38794 cls: 'masonry-brick-title',
38799 if(this.html.length){
38800 cfg.cn[1].cn.push({
38802 cls: 'masonry-brick-text',
38807 if(this.bgimage.length){
38808 cfg.cn[0].cn.push({
38810 cls: 'masonry-brick-image-view',
38815 if(this.videourl.length){
38816 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
38817 // youtube support only?
38818 cfg.cn[0].cn.cn.push({
38820 cls: 'masonry-brick-image-view',
38823 allowfullscreen : true
38830 initEvents: function()
38832 switch (this.size) {
38865 this.el.on('touchstart', this.onTouchStart, this);
38866 this.el.on('touchmove', this.onTouchMove, this);
38867 this.el.on('touchend', this.onTouchEnd, this);
38868 this.el.on('contextmenu', this.onContextMenu, this);
38870 this.el.on('mouseenter' ,this.enter, this);
38871 this.el.on('mouseleave', this.leave, this);
38872 this.el.on('click', this.onClick, this);
38875 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
38876 this.parent().bricks.push(this);
38881 onClick: function(e, el)
38883 var time = this.endTimer - this.startTimer;
38884 // Roo.log(e.preventDefault());
38887 e.preventDefault();
38892 if(!this.preventDefault){
38896 e.preventDefault();
38898 if (this.activeClass != '') {
38899 this.selectBrick();
38902 this.fireEvent('click', this, e);
38905 enter: function(e, el)
38907 e.preventDefault();
38909 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38913 if(this.bgimage.length && this.html.length){
38914 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38918 leave: function(e, el)
38920 e.preventDefault();
38922 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
38926 if(this.bgimage.length && this.html.length){
38927 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38931 onTouchStart: function(e, el)
38933 // e.preventDefault();
38935 this.touchmoved = false;
38937 if(!this.isFitContainer){
38941 if(!this.bgimage.length || !this.html.length){
38945 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
38947 this.timer = new Date().getTime();
38951 onTouchMove: function(e, el)
38953 this.touchmoved = true;
38956 onContextMenu : function(e,el)
38958 e.preventDefault();
38959 e.stopPropagation();
38963 onTouchEnd: function(e, el)
38965 // e.preventDefault();
38967 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
38974 if(!this.bgimage.length || !this.html.length){
38976 if(this.href.length){
38977 window.location.href = this.href;
38983 if(!this.isFitContainer){
38987 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
38989 window.location.href = this.href;
38992 //selection on single brick only
38993 selectBrick : function() {
38995 if (!this.parentId) {
38999 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
39000 var index = m.selectedBrick.indexOf(this.id);
39003 m.selectedBrick.splice(index,1);
39004 this.el.removeClass(this.activeClass);
39008 for(var i = 0; i < m.selectedBrick.length; i++) {
39009 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
39010 b.el.removeClass(b.activeClass);
39013 m.selectedBrick = [];
39015 m.selectedBrick.push(this.id);
39016 this.el.addClass(this.activeClass);
39020 isSelected : function(){
39021 return this.el.hasClass(this.activeClass);
39026 Roo.apply(Roo.bootstrap.MasonryBrick, {
39029 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
39031 * register a Masonry Brick
39032 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
39035 register : function(brick)
39037 //this.groups[brick.id] = brick;
39038 this.groups.add(brick.id, brick);
39041 * fetch a masonry brick based on the masonry brick ID
39042 * @param {string} the masonry brick to add
39043 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
39046 get: function(brick_id)
39048 // if (typeof(this.groups[brick_id]) == 'undefined') {
39051 // return this.groups[brick_id] ;
39053 if(this.groups.key(brick_id)) {
39054 return this.groups.key(brick_id);
39072 * @class Roo.bootstrap.Brick
39073 * @extends Roo.bootstrap.Component
39074 * Bootstrap Brick class
39077 * Create a new Brick
39078 * @param {Object} config The config object
39081 Roo.bootstrap.Brick = function(config){
39082 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
39088 * When a Brick is click
39089 * @param {Roo.bootstrap.Brick} this
39090 * @param {Roo.EventObject} e
39096 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
39099 * @cfg {String} title
39103 * @cfg {String} html
39107 * @cfg {String} bgimage
39111 * @cfg {String} cls
39115 * @cfg {String} href
39119 * @cfg {String} video
39123 * @cfg {Boolean} square
39127 getAutoCreate : function()
39129 var cls = 'roo-brick';
39131 if(this.href.length){
39132 cls += ' roo-brick-link';
39135 if(this.bgimage.length){
39136 cls += ' roo-brick-image';
39139 if(!this.html.length && !this.bgimage.length){
39140 cls += ' roo-brick-center-title';
39143 if(!this.html.length && this.bgimage.length){
39144 cls += ' roo-brick-bottom-title';
39148 cls += ' ' + this.cls;
39152 tag: (this.href.length) ? 'a' : 'div',
39157 cls: 'roo-brick-paragraph',
39163 if(this.href.length){
39164 cfg.href = this.href;
39167 var cn = cfg.cn[0].cn;
39169 if(this.title.length){
39172 cls: 'roo-brick-title',
39177 if(this.html.length){
39180 cls: 'roo-brick-text',
39187 if(this.bgimage.length){
39190 cls: 'roo-brick-image-view',
39198 initEvents: function()
39200 if(this.title.length || this.html.length){
39201 this.el.on('mouseenter' ,this.enter, this);
39202 this.el.on('mouseleave', this.leave, this);
39205 Roo.EventManager.onWindowResize(this.resize, this);
39207 if(this.bgimage.length){
39208 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
39209 this.imageEl.on('load', this.onImageLoad, this);
39216 onImageLoad : function()
39221 resize : function()
39223 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
39225 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
39227 if(this.bgimage.length){
39228 var image = this.el.select('.roo-brick-image-view', true).first();
39230 image.setWidth(paragraph.getWidth());
39233 image.setHeight(paragraph.getWidth());
39236 this.el.setHeight(image.getHeight());
39237 paragraph.setHeight(image.getHeight());
39243 enter: function(e, el)
39245 e.preventDefault();
39247 if(this.bgimage.length){
39248 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
39249 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
39253 leave: function(e, el)
39255 e.preventDefault();
39257 if(this.bgimage.length){
39258 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
39259 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
39274 * @class Roo.bootstrap.form.NumberField
39275 * @extends Roo.bootstrap.form.Input
39276 * Bootstrap NumberField class
39282 * Create a new NumberField
39283 * @param {Object} config The config object
39286 Roo.bootstrap.form.NumberField = function(config){
39287 Roo.bootstrap.form.NumberField.superclass.constructor.call(this, config);
39290 Roo.extend(Roo.bootstrap.form.NumberField, Roo.bootstrap.form.Input, {
39293 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39295 allowDecimals : true,
39297 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39299 decimalSeparator : ".",
39301 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39303 decimalPrecision : 2,
39305 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39307 allowNegative : true,
39310 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
39314 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39316 minValue : Number.NEGATIVE_INFINITY,
39318 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39320 maxValue : Number.MAX_VALUE,
39322 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39324 minText : "The minimum value for this field is {0}",
39326 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39328 maxText : "The maximum value for this field is {0}",
39330 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
39331 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39333 nanText : "{0} is not a valid number",
39335 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
39337 thousandsDelimiter : false,
39339 * @cfg {String} valueAlign alignment of value
39341 valueAlign : "left",
39343 getAutoCreate : function()
39345 var hiddenInput = {
39349 cls: 'hidden-number-input'
39353 hiddenInput.name = this.name;
39358 var cfg = Roo.bootstrap.form.NumberField.superclass.getAutoCreate.call(this);
39360 this.name = hiddenInput.name;
39362 if(cfg.cn.length > 0) {
39363 cfg.cn.push(hiddenInput);
39370 initEvents : function()
39372 Roo.bootstrap.form.NumberField.superclass.initEvents.call(this);
39374 var allowed = "0123456789";
39376 if(this.allowDecimals){
39377 allowed += this.decimalSeparator;
39380 if(this.allowNegative){
39384 if(this.thousandsDelimiter) {
39388 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
39390 var keyPress = function(e){
39392 var k = e.getKey();
39394 var c = e.getCharCode();
39397 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39398 allowed.indexOf(String.fromCharCode(c)) === -1
39404 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39408 if(allowed.indexOf(String.fromCharCode(c)) === -1){
39413 this.el.on("keypress", keyPress, this);
39416 validateValue : function(value)
39419 if(!Roo.bootstrap.form.NumberField.superclass.validateValue.call(this, value)){
39423 var num = this.parseValue(value);
39426 this.markInvalid(String.format(this.nanText, value));
39430 if(num < this.minValue){
39431 this.markInvalid(String.format(this.minText, this.minValue));
39435 if(num > this.maxValue){
39436 this.markInvalid(String.format(this.maxText, this.maxValue));
39443 getValue : function()
39445 var v = this.hiddenEl().getValue();
39447 return this.fixPrecision(this.parseValue(v));
39450 parseValue : function(value)
39452 if(this.thousandsDelimiter) {
39454 r = new RegExp(",", "g");
39455 value = value.replace(r, "");
39458 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
39459 return isNaN(value) ? '' : value;
39462 fixPrecision : function(value)
39464 if(this.thousandsDelimiter) {
39466 r = new RegExp(",", "g");
39467 value = value.replace(r, "");
39470 var nan = isNaN(value);
39472 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
39473 return nan ? '' : value;
39475 return parseFloat(value).toFixed(this.decimalPrecision);
39478 setValue : function(v)
39480 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
39486 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39488 this.inputEl().dom.value = (v == '') ? '' :
39489 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
39491 if(!this.allowZero && v === '0') {
39492 this.hiddenEl().dom.value = '';
39493 this.inputEl().dom.value = '';
39500 decimalPrecisionFcn : function(v)
39502 return Math.floor(v);
39505 beforeBlur : function()
39507 var v = this.parseValue(this.getRawValue());
39509 if(v || v === 0 || v === ''){
39514 hiddenEl : function()
39516 return this.el.select('input.hidden-number-input',true).first();
39528 * @class Roo.bootstrap.DocumentSlider
39529 * @extends Roo.bootstrap.Component
39530 * Bootstrap DocumentSlider class
39533 * Create a new DocumentViewer
39534 * @param {Object} config The config object
39537 Roo.bootstrap.DocumentSlider = function(config){
39538 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
39545 * Fire after initEvent
39546 * @param {Roo.bootstrap.DocumentSlider} this
39551 * Fire after update
39552 * @param {Roo.bootstrap.DocumentSlider} this
39558 * @param {Roo.bootstrap.DocumentSlider} this
39564 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
39570 getAutoCreate : function()
39574 cls : 'roo-document-slider',
39578 cls : 'roo-document-slider-header',
39582 cls : 'roo-document-slider-header-title'
39588 cls : 'roo-document-slider-body',
39592 cls : 'roo-document-slider-prev',
39596 cls : 'fa fa-chevron-left'
39602 cls : 'roo-document-slider-thumb',
39606 cls : 'roo-document-slider-image'
39612 cls : 'roo-document-slider-next',
39616 cls : 'fa fa-chevron-right'
39628 initEvents : function()
39630 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
39631 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
39633 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
39634 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
39636 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
39637 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
39639 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
39640 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
39642 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
39643 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
39645 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
39646 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39648 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
39649 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
39651 this.thumbEl.on('click', this.onClick, this);
39653 this.prevIndicator.on('click', this.prev, this);
39655 this.nextIndicator.on('click', this.next, this);
39659 initial : function()
39661 if(this.files.length){
39662 this.indicator = 1;
39666 this.fireEvent('initial', this);
39669 update : function()
39671 this.imageEl.attr('src', this.files[this.indicator - 1]);
39673 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
39675 this.prevIndicator.show();
39677 if(this.indicator == 1){
39678 this.prevIndicator.hide();
39681 this.nextIndicator.show();
39683 if(this.indicator == this.files.length){
39684 this.nextIndicator.hide();
39687 this.thumbEl.scrollTo('top');
39689 this.fireEvent('update', this);
39692 onClick : function(e)
39694 e.preventDefault();
39696 this.fireEvent('click', this);
39701 e.preventDefault();
39703 this.indicator = Math.max(1, this.indicator - 1);
39710 e.preventDefault();
39712 this.indicator = Math.min(this.files.length, this.indicator + 1);
39726 * @class Roo.bootstrap.form.RadioSet
39727 * @extends Roo.bootstrap.form.Input
39728 * @children Roo.bootstrap.form.Radio
39729 * Bootstrap RadioSet class
39730 * @cfg {String} indicatorpos (left|right) default left
39731 * @cfg {Boolean} inline (true|false) inline the element (default true)
39732 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
39734 * Create a new RadioSet
39735 * @param {Object} config The config object
39738 Roo.bootstrap.form.RadioSet = function(config){
39740 Roo.bootstrap.form.RadioSet.superclass.constructor.call(this, config);
39744 Roo.bootstrap.form.RadioSet.register(this);
39749 * Fires when the element is checked or unchecked.
39750 * @param {Roo.bootstrap.form.RadioSet} this This radio
39751 * @param {Roo.bootstrap.form.Radio} item The checked item
39756 * Fires when the element is click.
39757 * @param {Roo.bootstrap.form.RadioSet} this This radio set
39758 * @param {Roo.bootstrap.form.Radio} item The checked item
39759 * @param {Roo.EventObject} e The event object
39766 Roo.extend(Roo.bootstrap.form.RadioSet, Roo.bootstrap.form.Input, {
39774 indicatorpos : 'left',
39776 getAutoCreate : function()
39780 cls : 'roo-radio-set-label',
39784 html : this.fieldLabel
39788 if (Roo.bootstrap.version == 3) {
39791 if(this.indicatorpos == 'left'){
39794 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39795 tooltip : 'This field is required'
39800 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39801 tooltip : 'This field is required'
39807 cls : 'roo-radio-set-items'
39810 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
39812 if (align === 'left' && this.fieldLabel.length) {
39815 cls : "roo-radio-set-right",
39821 if(this.labelWidth > 12){
39822 label.style = "width: " + this.labelWidth + 'px';
39825 if(this.labelWidth < 13 && this.labelmd == 0){
39826 this.labelmd = this.labelWidth;
39829 if(this.labellg > 0){
39830 label.cls += ' col-lg-' + this.labellg;
39831 items.cls += ' col-lg-' + (12 - this.labellg);
39834 if(this.labelmd > 0){
39835 label.cls += ' col-md-' + this.labelmd;
39836 items.cls += ' col-md-' + (12 - this.labelmd);
39839 if(this.labelsm > 0){
39840 label.cls += ' col-sm-' + this.labelsm;
39841 items.cls += ' col-sm-' + (12 - this.labelsm);
39844 if(this.labelxs > 0){
39845 label.cls += ' col-xs-' + this.labelxs;
39846 items.cls += ' col-xs-' + (12 - this.labelxs);
39852 cls : 'roo-radio-set',
39856 cls : 'roo-radio-set-input',
39859 value : this.value ? this.value : ''
39866 if(this.weight.length){
39867 cfg.cls += ' roo-radio-' + this.weight;
39871 cfg.cls += ' roo-radio-set-inline';
39875 ['xs','sm','md','lg'].map(function(size){
39876 if (settings[size]) {
39877 cfg.cls += ' col-' + size + '-' + settings[size];
39885 initEvents : function()
39887 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
39888 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
39890 if(!this.fieldLabel.length){
39891 this.labelEl.hide();
39894 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
39895 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
39897 this.indicator = this.indicatorEl();
39899 if(this.indicator){
39900 this.indicator.addClass('invisible');
39903 this.originalValue = this.getValue();
39907 inputEl: function ()
39909 return this.el.select('.roo-radio-set-input', true).first();
39912 getChildContainer : function()
39914 return this.itemsEl;
39917 register : function(item)
39919 this.radioes.push(item);
39923 validate : function()
39925 if(this.getVisibilityEl().hasClass('hidden')){
39931 Roo.each(this.radioes, function(i){
39940 if(this.allowBlank) {
39944 if(this.disabled || valid){
39949 this.markInvalid();
39954 markValid : function()
39956 if(this.labelEl.isVisible(true) && this.indicatorEl()){
39957 this.indicatorEl().removeClass('visible');
39958 this.indicatorEl().addClass('invisible');
39962 if (Roo.bootstrap.version == 3) {
39963 this.el.removeClass([this.invalidClass, this.validClass]);
39964 this.el.addClass(this.validClass);
39966 this.el.removeClass(['is-invalid','is-valid']);
39967 this.el.addClass(['is-valid']);
39969 this.fireEvent('valid', this);
39972 markInvalid : function(msg)
39974 if(this.allowBlank || this.disabled){
39978 if(this.labelEl.isVisible(true) && this.indicatorEl()){
39979 this.indicatorEl().removeClass('invisible');
39980 this.indicatorEl().addClass('visible');
39982 if (Roo.bootstrap.version == 3) {
39983 this.el.removeClass([this.invalidClass, this.validClass]);
39984 this.el.addClass(this.invalidClass);
39986 this.el.removeClass(['is-invalid','is-valid']);
39987 this.el.addClass(['is-invalid']);
39990 this.fireEvent('invalid', this, msg);
39994 setValue : function(v, suppressEvent)
39996 if(this.value === v){
40003 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40006 Roo.each(this.radioes, function(i){
40008 i.el.removeClass('checked');
40011 Roo.each(this.radioes, function(i){
40013 if(i.value === v || i.value.toString() === v.toString()){
40015 i.el.addClass('checked');
40017 if(suppressEvent !== true){
40018 this.fireEvent('check', this, i);
40029 clearInvalid : function(){
40031 if(!this.el || this.preventMark){
40035 this.el.removeClass([this.invalidClass]);
40037 this.fireEvent('valid', this);
40042 Roo.apply(Roo.bootstrap.form.RadioSet, {
40046 register : function(set)
40048 this.groups[set.name] = set;
40051 get: function(name)
40053 if (typeof(this.groups[name]) == 'undefined') {
40057 return this.groups[name] ;
40063 * Ext JS Library 1.1.1
40064 * Copyright(c) 2006-2007, Ext JS, LLC.
40066 * Originally Released Under LGPL - original licence link has changed is not relivant.
40069 * <script type="text/javascript">
40074 * @class Roo.bootstrap.SplitBar
40075 * @extends Roo.util.Observable
40076 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
40080 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
40081 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
40082 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
40083 split.minSize = 100;
40084 split.maxSize = 600;
40085 split.animate = true;
40086 split.on('moved', splitterMoved);
40089 * Create a new SplitBar
40090 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
40091 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
40092 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40093 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
40094 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
40095 position of the SplitBar).
40097 Roo.bootstrap.SplitBar = function(cfg){
40102 // dragElement : elm
40103 // resizingElement: el,
40105 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
40106 // placement : Roo.bootstrap.SplitBar.LEFT ,
40107 // existingProxy ???
40110 this.el = Roo.get(cfg.dragElement, true);
40111 this.el.dom.unselectable = "on";
40113 this.resizingEl = Roo.get(cfg.resizingElement, true);
40117 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
40118 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
40121 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
40124 * The minimum size of the resizing element. (Defaults to 0)
40130 * The maximum size of the resizing element. (Defaults to 2000)
40133 this.maxSize = 2000;
40136 * Whether to animate the transition to the new size
40139 this.animate = false;
40142 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
40145 this.useShim = false;
40150 if(!cfg.existingProxy){
40152 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
40154 this.proxy = Roo.get(cfg.existingProxy).dom;
40157 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
40160 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
40163 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
40166 this.dragSpecs = {};
40169 * @private The adapter to use to positon and resize elements
40171 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40172 this.adapter.init(this);
40174 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40176 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
40177 this.el.addClass("roo-splitbar-h");
40180 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
40181 this.el.addClass("roo-splitbar-v");
40187 * Fires when the splitter is moved (alias for {@link #event-moved})
40188 * @param {Roo.bootstrap.SplitBar} this
40189 * @param {Number} newSize the new width or height
40194 * Fires when the splitter is moved
40195 * @param {Roo.bootstrap.SplitBar} this
40196 * @param {Number} newSize the new width or height
40200 * @event beforeresize
40201 * Fires before the splitter is dragged
40202 * @param {Roo.bootstrap.SplitBar} this
40204 "beforeresize" : true,
40206 "beforeapply" : true
40209 Roo.util.Observable.call(this);
40212 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
40213 onStartProxyDrag : function(x, y){
40214 this.fireEvent("beforeresize", this);
40216 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
40218 o.enableDisplayMode("block");
40219 // all splitbars share the same overlay
40220 Roo.bootstrap.SplitBar.prototype.overlay = o;
40222 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40223 this.overlay.show();
40224 Roo.get(this.proxy).setDisplayed("block");
40225 var size = this.adapter.getElementSize(this);
40226 this.activeMinSize = this.getMinimumSize();;
40227 this.activeMaxSize = this.getMaximumSize();;
40228 var c1 = size - this.activeMinSize;
40229 var c2 = Math.max(this.activeMaxSize - size, 0);
40230 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40231 this.dd.resetConstraints();
40232 this.dd.setXConstraint(
40233 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
40234 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
40236 this.dd.setYConstraint(0, 0);
40238 this.dd.resetConstraints();
40239 this.dd.setXConstraint(0, 0);
40240 this.dd.setYConstraint(
40241 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
40242 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
40245 this.dragSpecs.startSize = size;
40246 this.dragSpecs.startPoint = [x, y];
40247 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
40251 * @private Called after the drag operation by the DDProxy
40253 onEndProxyDrag : function(e){
40254 Roo.get(this.proxy).setDisplayed(false);
40255 var endPoint = Roo.lib.Event.getXY(e);
40257 this.overlay.hide();
40260 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40261 newSize = this.dragSpecs.startSize +
40262 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
40263 endPoint[0] - this.dragSpecs.startPoint[0] :
40264 this.dragSpecs.startPoint[0] - endPoint[0]
40267 newSize = this.dragSpecs.startSize +
40268 (this.placement == Roo.bootstrap.SplitBar.TOP ?
40269 endPoint[1] - this.dragSpecs.startPoint[1] :
40270 this.dragSpecs.startPoint[1] - endPoint[1]
40273 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
40274 if(newSize != this.dragSpecs.startSize){
40275 if(this.fireEvent('beforeapply', this, newSize) !== false){
40276 this.adapter.setElementSize(this, newSize);
40277 this.fireEvent("moved", this, newSize);
40278 this.fireEvent("resize", this, newSize);
40284 * Get the adapter this SplitBar uses
40285 * @return The adapter object
40287 getAdapter : function(){
40288 return this.adapter;
40292 * Set the adapter this SplitBar uses
40293 * @param {Object} adapter A SplitBar adapter object
40295 setAdapter : function(adapter){
40296 this.adapter = adapter;
40297 this.adapter.init(this);
40301 * Gets the minimum size for the resizing element
40302 * @return {Number} The minimum size
40304 getMinimumSize : function(){
40305 return this.minSize;
40309 * Sets the minimum size for the resizing element
40310 * @param {Number} minSize The minimum size
40312 setMinimumSize : function(minSize){
40313 this.minSize = minSize;
40317 * Gets the maximum size for the resizing element
40318 * @return {Number} The maximum size
40320 getMaximumSize : function(){
40321 return this.maxSize;
40325 * Sets the maximum size for the resizing element
40326 * @param {Number} maxSize The maximum size
40328 setMaximumSize : function(maxSize){
40329 this.maxSize = maxSize;
40333 * Sets the initialize size for the resizing element
40334 * @param {Number} size The initial size
40336 setCurrentSize : function(size){
40337 var oldAnimate = this.animate;
40338 this.animate = false;
40339 this.adapter.setElementSize(this, size);
40340 this.animate = oldAnimate;
40344 * Destroy this splitbar.
40345 * @param {Boolean} removeEl True to remove the element
40347 destroy : function(removeEl){
40349 this.shim.remove();
40352 this.proxy.parentNode.removeChild(this.proxy);
40360 * @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.
40362 Roo.bootstrap.SplitBar.createProxy = function(dir){
40363 var proxy = new Roo.Element(document.createElement("div"));
40364 proxy.unselectable();
40365 var cls = 'roo-splitbar-proxy';
40366 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
40367 document.body.appendChild(proxy.dom);
40372 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
40373 * Default Adapter. It assumes the splitter and resizing element are not positioned
40374 * elements and only gets/sets the width of the element. Generally used for table based layouts.
40376 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
40379 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
40380 // do nothing for now
40381 init : function(s){
40385 * Called before drag operations to get the current size of the resizing element.
40386 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40388 getElementSize : function(s){
40389 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40390 return s.resizingEl.getWidth();
40392 return s.resizingEl.getHeight();
40397 * Called after drag operations to set the size of the resizing element.
40398 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
40399 * @param {Number} newSize The new size to set
40400 * @param {Function} onComplete A function to be invoked when resizing is complete
40402 setElementSize : function(s, newSize, onComplete){
40403 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
40405 s.resizingEl.setWidth(newSize);
40407 onComplete(s, newSize);
40410 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
40415 s.resizingEl.setHeight(newSize);
40417 onComplete(s, newSize);
40420 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
40427 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
40428 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
40429 * Adapter that moves the splitter element to align with the resized sizing element.
40430 * Used with an absolute positioned SplitBar.
40431 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
40432 * document.body, make sure you assign an id to the body element.
40434 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
40435 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
40436 this.container = Roo.get(container);
40439 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
40440 init : function(s){
40441 this.basic.init(s);
40444 getElementSize : function(s){
40445 return this.basic.getElementSize(s);
40448 setElementSize : function(s, newSize, onComplete){
40449 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
40452 moveSplitter : function(s){
40453 var yes = Roo.bootstrap.SplitBar;
40454 switch(s.placement){
40456 s.el.setX(s.resizingEl.getRight());
40459 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
40462 s.el.setY(s.resizingEl.getBottom());
40465 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
40472 * Orientation constant - Create a vertical SplitBar
40476 Roo.bootstrap.SplitBar.VERTICAL = 1;
40479 * Orientation constant - Create a horizontal SplitBar
40483 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
40486 * Placement constant - The resizing element is to the left of the splitter element
40490 Roo.bootstrap.SplitBar.LEFT = 1;
40493 * Placement constant - The resizing element is to the right of the splitter element
40497 Roo.bootstrap.SplitBar.RIGHT = 2;
40500 * Placement constant - The resizing element is positioned above the splitter element
40504 Roo.bootstrap.SplitBar.TOP = 3;
40507 * Placement constant - The resizing element is positioned under splitter element
40511 Roo.bootstrap.SplitBar.BOTTOM = 4;
40514 * Ext JS Library 1.1.1
40515 * Copyright(c) 2006-2007, Ext JS, LLC.
40517 * Originally Released Under LGPL - original licence link has changed is not relivant.
40520 * <script type="text/javascript">
40524 * @class Roo.bootstrap.layout.Manager
40525 * @extends Roo.bootstrap.Component
40527 * Base class for layout managers.
40529 Roo.bootstrap.layout.Manager = function(config)
40531 this.monitorWindowResize = true; // do this before we apply configuration.
40533 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
40539 /** false to disable window resize monitoring @type Boolean */
40545 * Fires when a layout is performed.
40546 * @param {Roo.LayoutManager} this
40550 * @event regionresized
40551 * Fires when the user resizes a region.
40552 * @param {Roo.LayoutRegion} region The resized region
40553 * @param {Number} newSize The new size (width for east/west, height for north/south)
40555 "regionresized" : true,
40557 * @event regioncollapsed
40558 * Fires when a region is collapsed.
40559 * @param {Roo.LayoutRegion} region The collapsed region
40561 "regioncollapsed" : true,
40563 * @event regionexpanded
40564 * Fires when a region is expanded.
40565 * @param {Roo.LayoutRegion} region The expanded region
40567 "regionexpanded" : true
40569 this.updating = false;
40572 this.el = Roo.get(config.el);
40578 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
40583 monitorWindowResize : true,
40589 onRender : function(ct, position)
40592 this.el = Roo.get(ct);
40595 //this.fireEvent('render',this);
40599 initEvents: function()
40603 // ie scrollbar fix
40604 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
40605 document.body.scroll = "no";
40606 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
40607 this.el.position('relative');
40609 this.id = this.el.id;
40610 this.el.addClass("roo-layout-container");
40611 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
40612 if(this.el.dom != document.body ) {
40613 this.el.on('resize', this.layout,this);
40614 this.el.on('show', this.layout,this);
40620 * Returns true if this layout is currently being updated
40621 * @return {Boolean}
40623 isUpdating : function(){
40624 return this.updating;
40628 * Suspend the LayoutManager from doing auto-layouts while
40629 * making multiple add or remove calls
40631 beginUpdate : function(){
40632 this.updating = true;
40636 * Restore auto-layouts and optionally disable the manager from performing a layout
40637 * @param {Boolean} noLayout true to disable a layout update
40639 endUpdate : function(noLayout){
40640 this.updating = false;
40646 layout: function(){
40650 onRegionResized : function(region, newSize){
40651 this.fireEvent("regionresized", region, newSize);
40655 onRegionCollapsed : function(region){
40656 this.fireEvent("regioncollapsed", region);
40659 onRegionExpanded : function(region){
40660 this.fireEvent("regionexpanded", region);
40664 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
40665 * performs box-model adjustments.
40666 * @return {Object} The size as an object {width: (the width), height: (the height)}
40668 getViewSize : function()
40671 if(this.el.dom != document.body){
40672 size = this.el.getSize();
40674 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
40676 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
40677 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40682 * Returns the Element this layout is bound to.
40683 * @return {Roo.Element}
40685 getEl : function(){
40690 * Returns the specified region.
40691 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
40692 * @return {Roo.LayoutRegion}
40694 getRegion : function(target){
40695 return this.regions[target.toLowerCase()];
40698 onWindowResize : function(){
40699 if(this.monitorWindowResize){
40706 * Ext JS Library 1.1.1
40707 * Copyright(c) 2006-2007, Ext JS, LLC.
40709 * Originally Released Under LGPL - original licence link has changed is not relivant.
40712 * <script type="text/javascript">
40715 * @class Roo.bootstrap.layout.Border
40716 * @extends Roo.bootstrap.layout.Manager
40717 * @children Roo.bootstrap.panel.Content Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Grid
40718 * @parent builder Roo.bootstrap.panel.Nest Roo.bootstrap.panel.Nest Roo.bootstrap.Modal
40719 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
40720 * please see: examples/bootstrap/nested.html<br><br>
40722 <b>The container the layout is rendered into can be either the body element or any other element.
40723 If it is not the body element, the container needs to either be an absolute positioned element,
40724 or you will need to add "position:relative" to the css of the container. You will also need to specify
40725 the container size if it is not the body element.</b>
40728 * Create a new Border
40729 * @param {Object} config Configuration options
40731 Roo.bootstrap.layout.Border = function(config){
40732 config = config || {};
40733 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
40737 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40738 if(config[region]){
40739 config[region].region = region;
40740 this.addRegion(config[region]);
40746 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
40748 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
40751 * @cfg {Roo.bootstrap.layout.Region} center region to go in center
40754 * @cfg {Roo.bootstrap.layout.Region} west region to go in west
40757 * @cfg {Roo.bootstrap.layout.Region} east region to go in east
40760 * @cfg {Roo.bootstrap.layout.Region} south region to go in south
40763 * @cfg {Roo.bootstrap.layout.Region} north region to go in north
40769 parent : false, // this might point to a 'nest' or a ???
40772 * Creates and adds a new region if it doesn't already exist.
40773 * @param {String} target The target region key (north, south, east, west or center).
40774 * @param {Object} config The regions config object
40775 * @return {BorderLayoutRegion} The new region
40777 addRegion : function(config)
40779 if(!this.regions[config.region]){
40780 var r = this.factory(config);
40781 this.bindRegion(r);
40783 return this.regions[config.region];
40787 bindRegion : function(r){
40788 this.regions[r.config.region] = r;
40790 r.on("visibilitychange", this.layout, this);
40791 r.on("paneladded", this.layout, this);
40792 r.on("panelremoved", this.layout, this);
40793 r.on("invalidated", this.layout, this);
40794 r.on("resized", this.onRegionResized, this);
40795 r.on("collapsed", this.onRegionCollapsed, this);
40796 r.on("expanded", this.onRegionExpanded, this);
40800 * Performs a layout update.
40802 layout : function()
40804 if(this.updating) {
40808 // render all the rebions if they have not been done alreayd?
40809 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
40810 if(this.regions[region] && !this.regions[region].bodyEl){
40811 this.regions[region].onRender(this.el)
40815 var size = this.getViewSize();
40816 var w = size.width;
40817 var h = size.height;
40822 //var x = 0, y = 0;
40824 var rs = this.regions;
40825 var north = rs["north"];
40826 var south = rs["south"];
40827 var west = rs["west"];
40828 var east = rs["east"];
40829 var center = rs["center"];
40830 //if(this.hideOnLayout){ // not supported anymore
40831 //c.el.setStyle("display", "none");
40833 if(north && north.isVisible()){
40834 var b = north.getBox();
40835 var m = north.getMargins();
40836 b.width = w - (m.left+m.right);
40839 centerY = b.height + b.y + m.bottom;
40840 centerH -= centerY;
40841 north.updateBox(this.safeBox(b));
40843 if(south && south.isVisible()){
40844 var b = south.getBox();
40845 var m = south.getMargins();
40846 b.width = w - (m.left+m.right);
40848 var totalHeight = (b.height + m.top + m.bottom);
40849 b.y = h - totalHeight + m.top;
40850 centerH -= totalHeight;
40851 south.updateBox(this.safeBox(b));
40853 if(west && west.isVisible()){
40854 var b = west.getBox();
40855 var m = west.getMargins();
40856 b.height = centerH - (m.top+m.bottom);
40858 b.y = centerY + m.top;
40859 var totalWidth = (b.width + m.left + m.right);
40860 centerX += totalWidth;
40861 centerW -= totalWidth;
40862 west.updateBox(this.safeBox(b));
40864 if(east && east.isVisible()){
40865 var b = east.getBox();
40866 var m = east.getMargins();
40867 b.height = centerH - (m.top+m.bottom);
40868 var totalWidth = (b.width + m.left + m.right);
40869 b.x = w - totalWidth + m.left;
40870 b.y = centerY + m.top;
40871 centerW -= totalWidth;
40872 east.updateBox(this.safeBox(b));
40875 var m = center.getMargins();
40877 x: centerX + m.left,
40878 y: centerY + m.top,
40879 width: centerW - (m.left+m.right),
40880 height: centerH - (m.top+m.bottom)
40882 //if(this.hideOnLayout){
40883 //center.el.setStyle("display", "block");
40885 center.updateBox(this.safeBox(centerBox));
40888 this.fireEvent("layout", this);
40892 safeBox : function(box){
40893 box.width = Math.max(0, box.width);
40894 box.height = Math.max(0, box.height);
40899 * Adds a ContentPanel (or subclass) to this layout.
40900 * @param {String} target The target region key (north, south, east, west or center).
40901 * @param {Roo.ContentPanel} panel The panel to add
40902 * @return {Roo.ContentPanel} The added panel
40904 add : function(target, panel){
40906 target = target.toLowerCase();
40907 return this.regions[target].add(panel);
40911 * Remove a ContentPanel (or subclass) to this layout.
40912 * @param {String} target The target region key (north, south, east, west or center).
40913 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
40914 * @return {Roo.ContentPanel} The removed panel
40916 remove : function(target, panel){
40917 target = target.toLowerCase();
40918 return this.regions[target].remove(panel);
40922 * Searches all regions for a panel with the specified id
40923 * @param {String} panelId
40924 * @return {Roo.ContentPanel} The panel or null if it wasn't found
40926 findPanel : function(panelId){
40927 var rs = this.regions;
40928 for(var target in rs){
40929 if(typeof rs[target] != "function"){
40930 var p = rs[target].getPanel(panelId);
40940 * Searches all regions for a panel with the specified id and activates (shows) it.
40941 * @param {String/ContentPanel} panelId The panels id or the panel itself
40942 * @return {Roo.ContentPanel} The shown panel or null
40944 showPanel : function(panelId) {
40945 var rs = this.regions;
40946 for(var target in rs){
40947 var r = rs[target];
40948 if(typeof r != "function"){
40949 if(r.hasPanel(panelId)){
40950 return r.showPanel(panelId);
40958 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
40959 * @param {Roo.state.Provider} provider (optional) An alternate state provider
40962 restoreState : function(provider){
40964 provider = Roo.state.Manager;
40966 var sm = new Roo.LayoutStateManager();
40967 sm.init(this, provider);
40973 * Adds a xtype elements to the layout.
40977 xtype : 'ContentPanel',
40984 xtype : 'NestedLayoutPanel',
40990 items : [ ... list of content panels or nested layout panels.. ]
40994 * @param {Object} cfg Xtype definition of item to add.
40996 addxtype : function(cfg)
40998 // basically accepts a pannel...
40999 // can accept a layout region..!?!?
41000 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
41003 // theory? children can only be panels??
41005 //if (!cfg.xtype.match(/Panel$/)) {
41010 if (typeof(cfg.region) == 'undefined') {
41011 Roo.log("Failed to add Panel, region was not set");
41015 var region = cfg.region;
41021 xitems = cfg.items;
41026 if ( region == 'center') {
41027 Roo.log("Center: " + cfg.title);
41033 case 'Content': // ContentPanel (el, cfg)
41034 case 'Scroll': // ContentPanel (el, cfg)
41036 cfg.autoCreate = cfg.autoCreate || true;
41037 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41039 // var el = this.el.createChild();
41040 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
41043 this.add(region, ret);
41047 case 'TreePanel': // our new panel!
41048 cfg.el = this.el.createChild();
41049 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41050 this.add(region, ret);
41055 // create a new Layout (which is a Border Layout...
41057 var clayout = cfg.layout;
41058 clayout.el = this.el.createChild();
41059 clayout.items = clayout.items || [];
41063 // replace this exitems with the clayout ones..
41064 xitems = clayout.items;
41066 // force background off if it's in center...
41067 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
41068 cfg.background = false;
41070 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
41073 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41074 //console.log('adding nested layout panel ' + cfg.toSource());
41075 this.add(region, ret);
41076 nb = {}; /// find first...
41081 // needs grid and region
41083 //var el = this.getRegion(region).el.createChild();
41085 *var el = this.el.createChild();
41086 // create the grid first...
41087 cfg.grid.container = el;
41088 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
41091 if (region == 'center' && this.active ) {
41092 cfg.background = false;
41095 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
41097 this.add(region, ret);
41099 if (cfg.background) {
41100 // render grid on panel activation (if panel background)
41101 ret.on('activate', function(gp) {
41102 if (!gp.grid.rendered) {
41103 // gp.grid.render(el);
41107 // cfg.grid.render(el);
41113 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
41114 // it was the old xcomponent building that caused this before.
41115 // espeically if border is the top element in the tree.
41125 if (typeof(Roo[cfg.xtype]) != 'undefined') {
41127 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
41128 this.add(region, ret);
41132 throw "Can not add '" + cfg.xtype + "' to Border";
41138 this.beginUpdate();
41142 Roo.each(xitems, function(i) {
41143 region = nb && i.region ? i.region : false;
41145 var add = ret.addxtype(i);
41148 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
41149 if (!i.background) {
41150 abn[region] = nb[region] ;
41157 // make the last non-background panel active..
41158 //if (nb) { Roo.log(abn); }
41161 for(var r in abn) {
41162 region = this.getRegion(r);
41164 // tried using nb[r], but it does not work..
41166 region.showPanel(abn[r]);
41177 factory : function(cfg)
41180 var validRegions = Roo.bootstrap.layout.Border.regions;
41182 var target = cfg.region;
41185 var r = Roo.bootstrap.layout;
41189 return new r.North(cfg);
41191 return new r.South(cfg);
41193 return new r.East(cfg);
41195 return new r.West(cfg);
41197 return new r.Center(cfg);
41199 throw 'Layout region "'+target+'" not supported.';
41206 * Ext JS Library 1.1.1
41207 * Copyright(c) 2006-2007, Ext JS, LLC.
41209 * Originally Released Under LGPL - original licence link has changed is not relivant.
41212 * <script type="text/javascript">
41216 * @class Roo.bootstrap.layout.Basic
41217 * @extends Roo.util.Observable
41218 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
41219 * and does not have a titlebar, tabs or any other features. All it does is size and position
41220 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
41221 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
41222 * @cfg {string} region the region that it inhabits..
41223 * @cfg {bool} skipConfig skip config?
41227 Roo.bootstrap.layout.Basic = function(config){
41229 this.mgr = config.mgr;
41231 this.position = config.region;
41233 var skipConfig = config.skipConfig;
41237 * @scope Roo.BasicLayoutRegion
41241 * @event beforeremove
41242 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
41243 * @param {Roo.LayoutRegion} this
41244 * @param {Roo.ContentPanel} panel The panel
41245 * @param {Object} e The cancel event object
41247 "beforeremove" : true,
41249 * @event invalidated
41250 * Fires when the layout for this region is changed.
41251 * @param {Roo.LayoutRegion} this
41253 "invalidated" : true,
41255 * @event visibilitychange
41256 * Fires when this region is shown or hidden
41257 * @param {Roo.LayoutRegion} this
41258 * @param {Boolean} visibility true or false
41260 "visibilitychange" : true,
41262 * @event paneladded
41263 * Fires when a panel is added.
41264 * @param {Roo.LayoutRegion} this
41265 * @param {Roo.ContentPanel} panel The panel
41267 "paneladded" : true,
41269 * @event panelremoved
41270 * Fires when a panel is removed.
41271 * @param {Roo.LayoutRegion} this
41272 * @param {Roo.ContentPanel} panel The panel
41274 "panelremoved" : true,
41276 * @event beforecollapse
41277 * Fires when this region before collapse.
41278 * @param {Roo.LayoutRegion} this
41280 "beforecollapse" : true,
41283 * Fires when this region is collapsed.
41284 * @param {Roo.LayoutRegion} this
41286 "collapsed" : true,
41289 * Fires when this region is expanded.
41290 * @param {Roo.LayoutRegion} this
41295 * Fires when this region is slid into view.
41296 * @param {Roo.LayoutRegion} this
41298 "slideshow" : true,
41301 * Fires when this region slides out of view.
41302 * @param {Roo.LayoutRegion} this
41304 "slidehide" : true,
41306 * @event panelactivated
41307 * Fires when a panel is activated.
41308 * @param {Roo.LayoutRegion} this
41309 * @param {Roo.ContentPanel} panel The activated panel
41311 "panelactivated" : true,
41314 * Fires when the user resizes this region.
41315 * @param {Roo.LayoutRegion} this
41316 * @param {Number} newSize The new size (width for east/west, height for north/south)
41320 /** A collection of panels in this region. @type Roo.util.MixedCollection */
41321 this.panels = new Roo.util.MixedCollection();
41322 this.panels.getKey = this.getPanelId.createDelegate(this);
41324 this.activePanel = null;
41325 // ensure listeners are added...
41327 if (config.listeners || config.events) {
41328 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
41329 listeners : config.listeners || {},
41330 events : config.events || {}
41334 if(skipConfig !== true){
41335 this.applyConfig(config);
41339 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
41341 getPanelId : function(p){
41345 applyConfig : function(config){
41346 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41347 this.config = config;
41352 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
41353 * the width, for horizontal (north, south) the height.
41354 * @param {Number} newSize The new width or height
41356 resizeTo : function(newSize){
41357 var el = this.el ? this.el :
41358 (this.activePanel ? this.activePanel.getEl() : null);
41360 switch(this.position){
41363 el.setWidth(newSize);
41364 this.fireEvent("resized", this, newSize);
41368 el.setHeight(newSize);
41369 this.fireEvent("resized", this, newSize);
41375 getBox : function(){
41376 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
41379 getMargins : function(){
41380 return this.margins;
41383 updateBox : function(box){
41385 var el = this.activePanel.getEl();
41386 el.dom.style.left = box.x + "px";
41387 el.dom.style.top = box.y + "px";
41388 this.activePanel.setSize(box.width, box.height);
41392 * Returns the container element for this region.
41393 * @return {Roo.Element}
41395 getEl : function(){
41396 return this.activePanel;
41400 * Returns true if this region is currently visible.
41401 * @return {Boolean}
41403 isVisible : function(){
41404 return this.activePanel ? true : false;
41407 setActivePanel : function(panel){
41408 panel = this.getPanel(panel);
41409 if(this.activePanel && this.activePanel != panel){
41410 this.activePanel.setActiveState(false);
41411 this.activePanel.getEl().setLeftTop(-10000,-10000);
41413 this.activePanel = panel;
41414 panel.setActiveState(true);
41416 panel.setSize(this.box.width, this.box.height);
41418 this.fireEvent("panelactivated", this, panel);
41419 this.fireEvent("invalidated");
41423 * Show the specified panel.
41424 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
41425 * @return {Roo.ContentPanel} The shown panel or null
41427 showPanel : function(panel){
41428 panel = this.getPanel(panel);
41430 this.setActivePanel(panel);
41436 * Get the active panel for this region.
41437 * @return {Roo.ContentPanel} The active panel or null
41439 getActivePanel : function(){
41440 return this.activePanel;
41444 * Add the passed ContentPanel(s)
41445 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
41446 * @return {Roo.ContentPanel} The panel added (if only one was added)
41448 add : function(panel){
41449 if(arguments.length > 1){
41450 for(var i = 0, len = arguments.length; i < len; i++) {
41451 this.add(arguments[i]);
41455 if(this.hasPanel(panel)){
41456 this.showPanel(panel);
41459 var el = panel.getEl();
41460 if(el.dom.parentNode != this.mgr.el.dom){
41461 this.mgr.el.dom.appendChild(el.dom);
41463 if(panel.setRegion){
41464 panel.setRegion(this);
41466 this.panels.add(panel);
41467 el.setStyle("position", "absolute");
41468 if(!panel.background){
41469 this.setActivePanel(panel);
41470 if(this.config.initialSize && this.panels.getCount()==1){
41471 this.resizeTo(this.config.initialSize);
41474 this.fireEvent("paneladded", this, panel);
41479 * Returns true if the panel is in this region.
41480 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41481 * @return {Boolean}
41483 hasPanel : function(panel){
41484 if(typeof panel == "object"){ // must be panel obj
41485 panel = panel.getId();
41487 return this.getPanel(panel) ? true : false;
41491 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
41492 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41493 * @param {Boolean} preservePanel Overrides the config preservePanel option
41494 * @return {Roo.ContentPanel} The panel that was removed
41496 remove : function(panel, preservePanel){
41497 panel = this.getPanel(panel);
41502 this.fireEvent("beforeremove", this, panel, e);
41503 if(e.cancel === true){
41506 var panelId = panel.getId();
41507 this.panels.removeKey(panelId);
41512 * Returns the panel specified or null if it's not in this region.
41513 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
41514 * @return {Roo.ContentPanel}
41516 getPanel : function(id){
41517 if(typeof id == "object"){ // must be panel obj
41520 return this.panels.get(id);
41524 * Returns this regions position (north/south/east/west/center).
41527 getPosition: function(){
41528 return this.position;
41532 * Ext JS Library 1.1.1
41533 * Copyright(c) 2006-2007, Ext JS, LLC.
41535 * Originally Released Under LGPL - original licence link has changed is not relivant.
41538 * <script type="text/javascript">
41542 * @class Roo.bootstrap.layout.Region
41543 * @extends Roo.bootstrap.layout.Basic
41544 * This class represents a region in a layout manager.
41546 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
41547 * @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})
41548 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
41549 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
41550 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
41551 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
41552 * @cfg {String} title The title for the region (overrides panel titles)
41553 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
41554 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
41555 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
41556 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
41557 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
41558 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
41559 * the space available, similar to FireFox 1.5 tabs (defaults to false)
41560 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
41561 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
41562 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
41564 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
41565 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
41566 * @cfg {Boolean} disableTabTips True to disable tab tooltips
41567 * @cfg {Number} width For East/West panels
41568 * @cfg {Number} height For North/South panels
41569 * @cfg {Boolean} split To show the splitter
41570 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
41572 * @cfg {string} cls Extra CSS classes to add to region
41574 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
41575 * @cfg {string} region the region that it inhabits..
41578 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
41579 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
41581 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
41582 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
41583 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
41585 Roo.bootstrap.layout.Region = function(config)
41587 this.applyConfig(config);
41589 var mgr = config.mgr;
41590 var pos = config.region;
41591 config.skipConfig = true;
41592 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
41595 this.onRender(mgr.el);
41598 this.visible = true;
41599 this.collapsed = false;
41600 this.unrendered_panels = [];
41603 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
41605 position: '', // set by wrapper (eg. north/south etc..)
41606 unrendered_panels : null, // unrendered panels.
41608 tabPosition : false,
41610 mgr: false, // points to 'Border'
41613 createBody : function(){
41614 /** This region's body element
41615 * @type Roo.Element */
41616 this.bodyEl = this.el.createChild({
41618 cls: "roo-layout-panel-body tab-content" // bootstrap added...
41622 onRender: function(ctr, pos)
41624 var dh = Roo.DomHelper;
41625 /** This region's container element
41626 * @type Roo.Element */
41627 this.el = dh.append(ctr.dom, {
41629 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
41631 /** This region's title element
41632 * @type Roo.Element */
41634 this.titleEl = dh.append(this.el.dom, {
41636 unselectable: "on",
41637 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
41639 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
41640 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
41644 this.titleEl.enableDisplayMode();
41645 /** This region's title text element
41646 * @type HTMLElement */
41647 this.titleTextEl = this.titleEl.dom.firstChild;
41648 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
41650 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
41651 this.closeBtn.enableDisplayMode();
41652 this.closeBtn.on("click", this.closeClicked, this);
41653 this.closeBtn.hide();
41655 this.createBody(this.config);
41656 if(this.config.hideWhenEmpty){
41658 this.on("paneladded", this.validateVisibility, this);
41659 this.on("panelremoved", this.validateVisibility, this);
41661 if(this.autoScroll){
41662 this.bodyEl.setStyle("overflow", "auto");
41664 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
41666 //if(c.titlebar !== false){
41667 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
41668 this.titleEl.hide();
41670 this.titleEl.show();
41671 if(this.config.title){
41672 this.titleTextEl.innerHTML = this.config.title;
41676 if(this.config.collapsed){
41677 this.collapse(true);
41679 if(this.config.hidden){
41683 if (this.unrendered_panels && this.unrendered_panels.length) {
41684 for (var i =0;i< this.unrendered_panels.length; i++) {
41685 this.add(this.unrendered_panels[i]);
41687 this.unrendered_panels = null;
41693 applyConfig : function(c)
41696 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
41697 var dh = Roo.DomHelper;
41698 if(c.titlebar !== false){
41699 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
41700 this.collapseBtn.on("click", this.collapse, this);
41701 this.collapseBtn.enableDisplayMode();
41703 if(c.showPin === true || this.showPin){
41704 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
41705 this.stickBtn.enableDisplayMode();
41706 this.stickBtn.on("click", this.expand, this);
41707 this.stickBtn.hide();
41712 /** This region's collapsed element
41713 * @type Roo.Element */
41716 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
41717 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
41720 if(c.floatable !== false){
41721 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
41722 this.collapsedEl.on("click", this.collapseClick, this);
41725 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
41726 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
41727 id: "message", unselectable: "on", style:{"float":"left"}});
41728 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
41730 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
41731 this.expandBtn.on("click", this.expand, this);
41735 if(this.collapseBtn){
41736 this.collapseBtn.setVisible(c.collapsible == true);
41739 this.cmargins = c.cmargins || this.cmargins ||
41740 (this.position == "west" || this.position == "east" ?
41741 {top: 0, left: 2, right:2, bottom: 0} :
41742 {top: 2, left: 0, right:0, bottom: 2});
41744 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
41747 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
41749 this.autoScroll = c.autoScroll || false;
41754 this.duration = c.duration || .30;
41755 this.slideDuration = c.slideDuration || .45;
41760 * Returns true if this region is currently visible.
41761 * @return {Boolean}
41763 isVisible : function(){
41764 return this.visible;
41768 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
41769 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
41771 //setCollapsedTitle : function(title){
41772 // title = title || " ";
41773 // if(this.collapsedTitleTextEl){
41774 // this.collapsedTitleTextEl.innerHTML = title;
41778 getBox : function(){
41780 // if(!this.collapsed){
41781 b = this.el.getBox(false, true);
41783 // b = this.collapsedEl.getBox(false, true);
41788 getMargins : function(){
41789 return this.margins;
41790 //return this.collapsed ? this.cmargins : this.margins;
41793 highlight : function(){
41794 this.el.addClass("x-layout-panel-dragover");
41797 unhighlight : function(){
41798 this.el.removeClass("x-layout-panel-dragover");
41801 updateBox : function(box)
41803 if (!this.bodyEl) {
41804 return; // not rendered yet..
41808 if(!this.collapsed){
41809 this.el.dom.style.left = box.x + "px";
41810 this.el.dom.style.top = box.y + "px";
41811 this.updateBody(box.width, box.height);
41813 this.collapsedEl.dom.style.left = box.x + "px";
41814 this.collapsedEl.dom.style.top = box.y + "px";
41815 this.collapsedEl.setSize(box.width, box.height);
41818 this.tabs.autoSizeTabs();
41822 updateBody : function(w, h)
41825 this.el.setWidth(w);
41826 w -= this.el.getBorderWidth("rl");
41827 if(this.config.adjustments){
41828 w += this.config.adjustments[0];
41831 if(h !== null && h > 0){
41832 this.el.setHeight(h);
41833 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
41834 h -= this.el.getBorderWidth("tb");
41835 if(this.config.adjustments){
41836 h += this.config.adjustments[1];
41838 this.bodyEl.setHeight(h);
41840 h = this.tabs.syncHeight(h);
41843 if(this.panelSize){
41844 w = w !== null ? w : this.panelSize.width;
41845 h = h !== null ? h : this.panelSize.height;
41847 if(this.activePanel){
41848 var el = this.activePanel.getEl();
41849 w = w !== null ? w : el.getWidth();
41850 h = h !== null ? h : el.getHeight();
41851 this.panelSize = {width: w, height: h};
41852 this.activePanel.setSize(w, h);
41854 if(Roo.isIE && this.tabs){
41855 this.tabs.el.repaint();
41860 * Returns the container element for this region.
41861 * @return {Roo.Element}
41863 getEl : function(){
41868 * Hides this region.
41871 //if(!this.collapsed){
41872 this.el.dom.style.left = "-2000px";
41875 // this.collapsedEl.dom.style.left = "-2000px";
41876 // this.collapsedEl.hide();
41878 this.visible = false;
41879 this.fireEvent("visibilitychange", this, false);
41883 * Shows this region if it was previously hidden.
41886 //if(!this.collapsed){
41889 // this.collapsedEl.show();
41891 this.visible = true;
41892 this.fireEvent("visibilitychange", this, true);
41895 closeClicked : function(){
41896 if(this.activePanel){
41897 this.remove(this.activePanel);
41901 collapseClick : function(e){
41903 e.stopPropagation();
41906 e.stopPropagation();
41912 * Collapses this region.
41913 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
41916 collapse : function(skipAnim, skipCheck = false){
41917 if(this.collapsed) {
41921 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
41923 this.collapsed = true;
41925 this.split.el.hide();
41927 if(this.config.animate && skipAnim !== true){
41928 this.fireEvent("invalidated", this);
41929 this.animateCollapse();
41931 this.el.setLocation(-20000,-20000);
41933 this.collapsedEl.show();
41934 this.fireEvent("collapsed", this);
41935 this.fireEvent("invalidated", this);
41941 animateCollapse : function(){
41946 * Expands this region if it was previously collapsed.
41947 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
41948 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
41951 expand : function(e, skipAnim){
41953 e.stopPropagation();
41955 if(!this.collapsed || this.el.hasActiveFx()) {
41959 this.afterSlideIn();
41962 this.collapsed = false;
41963 if(this.config.animate && skipAnim !== true){
41964 this.animateExpand();
41968 this.split.el.show();
41970 this.collapsedEl.setLocation(-2000,-2000);
41971 this.collapsedEl.hide();
41972 this.fireEvent("invalidated", this);
41973 this.fireEvent("expanded", this);
41977 animateExpand : function(){
41981 initTabs : function()
41983 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
41985 var ts = new Roo.bootstrap.panel.Tabs({
41986 el: this.bodyEl.dom,
41988 tabPosition: this.tabPosition ? this.tabPosition : 'top',
41989 disableTooltips: this.config.disableTabTips,
41990 toolbar : this.config.toolbar
41993 if(this.config.hideTabs){
41994 ts.stripWrap.setDisplayed(false);
41997 ts.resizeTabs = this.config.resizeTabs === true;
41998 ts.minTabWidth = this.config.minTabWidth || 40;
41999 ts.maxTabWidth = this.config.maxTabWidth || 250;
42000 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
42001 ts.monitorResize = false;
42002 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
42003 ts.bodyEl.addClass('roo-layout-tabs-body');
42004 this.panels.each(this.initPanelAsTab, this);
42007 initPanelAsTab : function(panel){
42008 var ti = this.tabs.addTab(
42012 this.config.closeOnTab && panel.isClosable(),
42015 if(panel.tabTip !== undefined){
42016 ti.setTooltip(panel.tabTip);
42018 ti.on("activate", function(){
42019 this.setActivePanel(panel);
42022 if(this.config.closeOnTab){
42023 ti.on("beforeclose", function(t, e){
42025 this.remove(panel);
42029 panel.tabItem = ti;
42034 updatePanelTitle : function(panel, title)
42036 if(this.activePanel == panel){
42037 this.updateTitle(title);
42040 var ti = this.tabs.getTab(panel.getEl().id);
42042 if(panel.tabTip !== undefined){
42043 ti.setTooltip(panel.tabTip);
42048 updateTitle : function(title){
42049 if(this.titleTextEl && !this.config.title){
42050 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
42054 setActivePanel : function(panel)
42056 panel = this.getPanel(panel);
42057 if(this.activePanel && this.activePanel != panel){
42058 if(this.activePanel.setActiveState(false) === false){
42062 this.activePanel = panel;
42063 panel.setActiveState(true);
42064 if(this.panelSize){
42065 panel.setSize(this.panelSize.width, this.panelSize.height);
42068 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
42070 this.updateTitle(panel.getTitle());
42072 this.fireEvent("invalidated", this);
42074 this.fireEvent("panelactivated", this, panel);
42078 * Shows the specified panel.
42079 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
42080 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
42082 showPanel : function(panel)
42084 panel = this.getPanel(panel);
42087 var tab = this.tabs.getTab(panel.getEl().id);
42088 if(tab.isHidden()){
42089 this.tabs.unhideTab(tab.id);
42093 this.setActivePanel(panel);
42100 * Get the active panel for this region.
42101 * @return {Roo.ContentPanel} The active panel or null
42103 getActivePanel : function(){
42104 return this.activePanel;
42107 validateVisibility : function(){
42108 if(this.panels.getCount() < 1){
42109 this.updateTitle(" ");
42110 this.closeBtn.hide();
42113 if(!this.isVisible()){
42120 * Adds the passed ContentPanel(s) to this region.
42121 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
42122 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
42124 add : function(panel)
42126 if(arguments.length > 1){
42127 for(var i = 0, len = arguments.length; i < len; i++) {
42128 this.add(arguments[i]);
42133 // if we have not been rendered yet, then we can not really do much of this..
42134 if (!this.bodyEl) {
42135 this.unrendered_panels.push(panel);
42142 if(this.hasPanel(panel)){
42143 this.showPanel(panel);
42146 panel.setRegion(this);
42147 this.panels.add(panel);
42148 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
42149 // sinle panel - no tab...?? would it not be better to render it with the tabs,
42150 // and hide them... ???
42151 this.bodyEl.dom.appendChild(panel.getEl().dom);
42152 if(panel.background !== true){
42153 this.setActivePanel(panel);
42155 this.fireEvent("paneladded", this, panel);
42162 this.initPanelAsTab(panel);
42166 if(panel.background !== true){
42167 this.tabs.activate(panel.getEl().id);
42169 this.fireEvent("paneladded", this, panel);
42174 * Hides the tab for the specified panel.
42175 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42177 hidePanel : function(panel){
42178 if(this.tabs && (panel = this.getPanel(panel))){
42179 this.tabs.hideTab(panel.getEl().id);
42184 * Unhides the tab for a previously hidden panel.
42185 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42187 unhidePanel : function(panel){
42188 if(this.tabs && (panel = this.getPanel(panel))){
42189 this.tabs.unhideTab(panel.getEl().id);
42193 clearPanels : function(){
42194 while(this.panels.getCount() > 0){
42195 this.remove(this.panels.first());
42200 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
42201 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
42202 * @param {Boolean} preservePanel Overrides the config preservePanel option
42203 * @return {Roo.ContentPanel} The panel that was removed
42205 remove : function(panel, preservePanel)
42207 panel = this.getPanel(panel);
42212 this.fireEvent("beforeremove", this, panel, e);
42213 if(e.cancel === true){
42216 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
42217 var panelId = panel.getId();
42218 this.panels.removeKey(panelId);
42220 document.body.appendChild(panel.getEl().dom);
42223 this.tabs.removeTab(panel.getEl().id);
42224 }else if (!preservePanel){
42225 this.bodyEl.dom.removeChild(panel.getEl().dom);
42227 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
42228 var p = this.panels.first();
42229 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
42230 tempEl.appendChild(p.getEl().dom);
42231 this.bodyEl.update("");
42232 this.bodyEl.dom.appendChild(p.getEl().dom);
42234 this.updateTitle(p.getTitle());
42236 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
42237 this.setActivePanel(p);
42239 panel.setRegion(null);
42240 if(this.activePanel == panel){
42241 this.activePanel = null;
42243 if(this.config.autoDestroy !== false && preservePanel !== true){
42244 try{panel.destroy();}catch(e){}
42246 this.fireEvent("panelremoved", this, panel);
42251 * Returns the TabPanel component used by this region
42252 * @return {Roo.TabPanel}
42254 getTabs : function(){
42258 createTool : function(parentEl, className){
42259 var btn = Roo.DomHelper.append(parentEl, {
42261 cls: "x-layout-tools-button",
42264 cls: "roo-layout-tools-button-inner " + className,
42268 btn.addClassOnOver("roo-layout-tools-button-over");
42273 * Ext JS Library 1.1.1
42274 * Copyright(c) 2006-2007, Ext JS, LLC.
42276 * Originally Released Under LGPL - original licence link has changed is not relivant.
42279 * <script type="text/javascript">
42285 * @class Roo.SplitLayoutRegion
42286 * @extends Roo.LayoutRegion
42287 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
42289 Roo.bootstrap.layout.Split = function(config){
42290 this.cursor = config.cursor;
42291 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
42294 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
42296 splitTip : "Drag to resize.",
42297 collapsibleSplitTip : "Drag to resize. Double click to hide.",
42298 useSplitTips : false,
42300 applyConfig : function(config){
42301 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
42304 onRender : function(ctr,pos) {
42306 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
42307 if(!this.config.split){
42312 var splitEl = Roo.DomHelper.append(ctr.dom, {
42314 id: this.el.id + "-split",
42315 cls: "roo-layout-split roo-layout-split-"+this.position,
42318 /** The SplitBar for this region
42319 * @type Roo.SplitBar */
42320 // does not exist yet...
42321 Roo.log([this.position, this.orientation]);
42323 this.split = new Roo.bootstrap.SplitBar({
42324 dragElement : splitEl,
42325 resizingElement: this.el,
42326 orientation : this.orientation
42329 this.split.on("moved", this.onSplitMove, this);
42330 this.split.useShim = this.config.useShim === true;
42331 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
42332 if(this.useSplitTips){
42333 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
42335 //if(config.collapsible){
42336 // this.split.el.on("dblclick", this.collapse, this);
42339 if(typeof this.config.minSize != "undefined"){
42340 this.split.minSize = this.config.minSize;
42342 if(typeof this.config.maxSize != "undefined"){
42343 this.split.maxSize = this.config.maxSize;
42345 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
42346 this.hideSplitter();
42351 getHMaxSize : function(){
42352 var cmax = this.config.maxSize || 10000;
42353 var center = this.mgr.getRegion("center");
42354 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
42357 getVMaxSize : function(){
42358 var cmax = this.config.maxSize || 10000;
42359 var center = this.mgr.getRegion("center");
42360 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
42363 onSplitMove : function(split, newSize){
42364 this.fireEvent("resized", this, newSize);
42368 * Returns the {@link Roo.SplitBar} for this region.
42369 * @return {Roo.SplitBar}
42371 getSplitBar : function(){
42376 this.hideSplitter();
42377 Roo.bootstrap.layout.Split.superclass.hide.call(this);
42380 hideSplitter : function(){
42382 this.split.el.setLocation(-2000,-2000);
42383 this.split.el.hide();
42389 this.split.el.show();
42391 Roo.bootstrap.layout.Split.superclass.show.call(this);
42394 beforeSlide: function(){
42395 if(Roo.isGecko){// firefox overflow auto bug workaround
42396 this.bodyEl.clip();
42398 this.tabs.bodyEl.clip();
42400 if(this.activePanel){
42401 this.activePanel.getEl().clip();
42403 if(this.activePanel.beforeSlide){
42404 this.activePanel.beforeSlide();
42410 afterSlide : function(){
42411 if(Roo.isGecko){// firefox overflow auto bug workaround
42412 this.bodyEl.unclip();
42414 this.tabs.bodyEl.unclip();
42416 if(this.activePanel){
42417 this.activePanel.getEl().unclip();
42418 if(this.activePanel.afterSlide){
42419 this.activePanel.afterSlide();
42425 initAutoHide : function(){
42426 if(this.autoHide !== false){
42427 if(!this.autoHideHd){
42428 var st = new Roo.util.DelayedTask(this.slideIn, this);
42429 this.autoHideHd = {
42430 "mouseout": function(e){
42431 if(!e.within(this.el, true)){
42435 "mouseover" : function(e){
42441 this.el.on(this.autoHideHd);
42445 clearAutoHide : function(){
42446 if(this.autoHide !== false){
42447 this.el.un("mouseout", this.autoHideHd.mouseout);
42448 this.el.un("mouseover", this.autoHideHd.mouseover);
42452 clearMonitor : function(){
42453 Roo.get(document).un("click", this.slideInIf, this);
42456 // these names are backwards but not changed for compat
42457 slideOut : function(){
42458 if(this.isSlid || this.el.hasActiveFx()){
42461 this.isSlid = true;
42462 if(this.collapseBtn){
42463 this.collapseBtn.hide();
42465 this.closeBtnState = this.closeBtn.getStyle('display');
42466 this.closeBtn.hide();
42468 this.stickBtn.show();
42471 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
42472 this.beforeSlide();
42473 this.el.setStyle("z-index", 10001);
42474 this.el.slideIn(this.getSlideAnchor(), {
42475 callback: function(){
42477 this.initAutoHide();
42478 Roo.get(document).on("click", this.slideInIf, this);
42479 this.fireEvent("slideshow", this);
42486 afterSlideIn : function(){
42487 this.clearAutoHide();
42488 this.isSlid = false;
42489 this.clearMonitor();
42490 this.el.setStyle("z-index", "");
42491 if(this.collapseBtn){
42492 this.collapseBtn.show();
42494 this.closeBtn.setStyle('display', this.closeBtnState);
42496 this.stickBtn.hide();
42498 this.fireEvent("slidehide", this);
42501 slideIn : function(cb){
42502 if(!this.isSlid || this.el.hasActiveFx()){
42506 this.isSlid = false;
42507 this.beforeSlide();
42508 this.el.slideOut(this.getSlideAnchor(), {
42509 callback: function(){
42510 this.el.setLeftTop(-10000, -10000);
42512 this.afterSlideIn();
42520 slideInIf : function(e){
42521 if(!e.within(this.el)){
42526 animateCollapse : function(){
42527 this.beforeSlide();
42528 this.el.setStyle("z-index", 20000);
42529 var anchor = this.getSlideAnchor();
42530 this.el.slideOut(anchor, {
42531 callback : function(){
42532 this.el.setStyle("z-index", "");
42533 this.collapsedEl.slideIn(anchor, {duration:.3});
42535 this.el.setLocation(-10000,-10000);
42537 this.fireEvent("collapsed", this);
42544 animateExpand : function(){
42545 this.beforeSlide();
42546 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
42547 this.el.setStyle("z-index", 20000);
42548 this.collapsedEl.hide({
42551 this.el.slideIn(this.getSlideAnchor(), {
42552 callback : function(){
42553 this.el.setStyle("z-index", "");
42556 this.split.el.show();
42558 this.fireEvent("invalidated", this);
42559 this.fireEvent("expanded", this);
42587 getAnchor : function(){
42588 return this.anchors[this.position];
42591 getCollapseAnchor : function(){
42592 return this.canchors[this.position];
42595 getSlideAnchor : function(){
42596 return this.sanchors[this.position];
42599 getAlignAdj : function(){
42600 var cm = this.cmargins;
42601 switch(this.position){
42617 getExpandAdj : function(){
42618 var c = this.collapsedEl, cm = this.cmargins;
42619 switch(this.position){
42621 return [-(cm.right+c.getWidth()+cm.left), 0];
42624 return [cm.right+c.getWidth()+cm.left, 0];
42627 return [0, -(cm.top+cm.bottom+c.getHeight())];
42630 return [0, cm.top+cm.bottom+c.getHeight()];
42636 * Ext JS Library 1.1.1
42637 * Copyright(c) 2006-2007, Ext JS, LLC.
42639 * Originally Released Under LGPL - original licence link has changed is not relivant.
42642 * <script type="text/javascript">
42645 * These classes are private internal classes
42647 Roo.bootstrap.layout.Center = function(config){
42648 config.region = "center";
42649 Roo.bootstrap.layout.Region.call(this, config);
42650 this.visible = true;
42651 this.minWidth = config.minWidth || 20;
42652 this.minHeight = config.minHeight || 20;
42655 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
42657 // center panel can't be hidden
42661 // center panel can't be hidden
42664 getMinWidth: function(){
42665 return this.minWidth;
42668 getMinHeight: function(){
42669 return this.minHeight;
42683 Roo.bootstrap.layout.North = function(config)
42685 config.region = 'north';
42686 config.cursor = 'n-resize';
42688 Roo.bootstrap.layout.Split.call(this, config);
42692 this.split.placement = Roo.bootstrap.SplitBar.TOP;
42693 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42694 this.split.el.addClass("roo-layout-split-v");
42696 //var size = config.initialSize || config.height;
42697 //if(this.el && typeof size != "undefined"){
42698 // this.el.setHeight(size);
42701 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
42703 orientation: Roo.bootstrap.SplitBar.VERTICAL,
42706 onRender : function(ctr, pos)
42708 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42709 var size = this.config.initialSize || this.config.height;
42710 if(this.el && typeof size != "undefined"){
42711 this.el.setHeight(size);
42716 getBox : function(){
42717 if(this.collapsed){
42718 return this.collapsedEl.getBox();
42720 var box = this.el.getBox();
42722 box.height += this.split.el.getHeight();
42727 updateBox : function(box){
42728 if(this.split && !this.collapsed){
42729 box.height -= this.split.el.getHeight();
42730 this.split.el.setLeft(box.x);
42731 this.split.el.setTop(box.y+box.height);
42732 this.split.el.setWidth(box.width);
42734 if(this.collapsed){
42735 this.updateBody(box.width, null);
42737 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42745 Roo.bootstrap.layout.South = function(config){
42746 config.region = 'south';
42747 config.cursor = 's-resize';
42748 Roo.bootstrap.layout.Split.call(this, config);
42750 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
42751 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
42752 this.split.el.addClass("roo-layout-split-v");
42757 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
42758 orientation: Roo.bootstrap.SplitBar.VERTICAL,
42760 onRender : function(ctr, pos)
42762 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42763 var size = this.config.initialSize || this.config.height;
42764 if(this.el && typeof size != "undefined"){
42765 this.el.setHeight(size);
42770 getBox : function(){
42771 if(this.collapsed){
42772 return this.collapsedEl.getBox();
42774 var box = this.el.getBox();
42776 var sh = this.split.el.getHeight();
42783 updateBox : function(box){
42784 if(this.split && !this.collapsed){
42785 var sh = this.split.el.getHeight();
42788 this.split.el.setLeft(box.x);
42789 this.split.el.setTop(box.y-sh);
42790 this.split.el.setWidth(box.width);
42792 if(this.collapsed){
42793 this.updateBody(box.width, null);
42795 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42799 Roo.bootstrap.layout.East = function(config){
42800 config.region = "east";
42801 config.cursor = "e-resize";
42802 Roo.bootstrap.layout.Split.call(this, config);
42804 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
42805 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42806 this.split.el.addClass("roo-layout-split-h");
42810 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
42811 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42813 onRender : function(ctr, pos)
42815 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
42816 var size = this.config.initialSize || this.config.width;
42817 if(this.el && typeof size != "undefined"){
42818 this.el.setWidth(size);
42823 getBox : function(){
42824 if(this.collapsed){
42825 return this.collapsedEl.getBox();
42827 var box = this.el.getBox();
42829 var sw = this.split.el.getWidth();
42836 updateBox : function(box){
42837 if(this.split && !this.collapsed){
42838 var sw = this.split.el.getWidth();
42840 this.split.el.setLeft(box.x);
42841 this.split.el.setTop(box.y);
42842 this.split.el.setHeight(box.height);
42845 if(this.collapsed){
42846 this.updateBody(null, box.height);
42848 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42852 Roo.bootstrap.layout.West = function(config){
42853 config.region = "west";
42854 config.cursor = "w-resize";
42856 Roo.bootstrap.layout.Split.call(this, config);
42858 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
42859 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
42860 this.split.el.addClass("roo-layout-split-h");
42864 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
42865 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
42867 onRender: function(ctr, pos)
42869 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
42870 var size = this.config.initialSize || this.config.width;
42871 if(typeof size != "undefined"){
42872 this.el.setWidth(size);
42876 getBox : function(){
42877 if(this.collapsed){
42878 return this.collapsedEl.getBox();
42880 var box = this.el.getBox();
42881 if (box.width == 0) {
42882 box.width = this.config.width; // kludge?
42885 box.width += this.split.el.getWidth();
42890 updateBox : function(box){
42891 if(this.split && !this.collapsed){
42892 var sw = this.split.el.getWidth();
42894 this.split.el.setLeft(box.x+box.width);
42895 this.split.el.setTop(box.y);
42896 this.split.el.setHeight(box.height);
42898 if(this.collapsed){
42899 this.updateBody(null, box.height);
42901 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
42905 * Ext JS Library 1.1.1
42906 * Copyright(c) 2006-2007, Ext JS, LLC.
42908 * Originally Released Under LGPL - original licence link has changed is not relivant.
42911 * <script type="text/javascript">
42914 * @class Roo.bootstrap.paenl.Content
42915 * @extends Roo.util.Observable
42916 * @children Roo.bootstrap.Component
42917 * @parent builder Roo.bootstrap.layout.Border
42918 * A basic ContentPanel element. - a panel that contain any content (eg. forms etc.)
42919 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
42920 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
42921 * @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
42922 * @cfg {Boolean} closable True if the panel can be closed/removed
42923 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
42924 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
42925 * @cfg {Toolbar} toolbar A toolbar for this panel
42926 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
42927 * @cfg {String} title The title for this panel
42928 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
42929 * @cfg {String} url Calls {@link #setUrl} with this value
42930 * @cfg {String} region [required] (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
42931 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
42932 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
42933 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
42934 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
42935 * @cfg {Boolean} badges render the badges
42936 * @cfg {String} cls extra classes to use
42937 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
42940 * Create a new ContentPanel.
42941 * @param {String/Object} config A string to set only the title or a config object
42944 Roo.bootstrap.panel.Content = function( config){
42946 this.tpl = config.tpl || false;
42948 var el = config.el;
42949 var content = config.content;
42951 if(config.autoCreate){ // xtype is available if this is called from factory
42954 this.el = Roo.get(el);
42955 if(!this.el && config && config.autoCreate){
42956 if(typeof config.autoCreate == "object"){
42957 if(!config.autoCreate.id){
42958 config.autoCreate.id = config.id||el;
42960 this.el = Roo.DomHelper.append(document.body,
42961 config.autoCreate, true);
42965 cls: (config.cls || '') +
42966 (config.background ? ' bg-' + config.background : '') +
42967 " roo-layout-inactive-content",
42970 if (config.iframe) {
42974 style : 'border: 0px',
42975 src : 'about:blank'
42981 elcfg.html = config.html;
42985 this.el = Roo.DomHelper.append(document.body, elcfg , true);
42986 if (config.iframe) {
42987 this.iframeEl = this.el.select('iframe',true).first();
42992 this.closable = false;
42993 this.loaded = false;
42994 this.active = false;
42997 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
42999 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
43001 this.wrapEl = this.el; //this.el.wrap();
43003 if (config.toolbar.items) {
43004 ti = config.toolbar.items ;
43005 delete config.toolbar.items ;
43009 this.toolbar.render(this.wrapEl, 'before');
43010 for(var i =0;i < ti.length;i++) {
43011 // Roo.log(['add child', items[i]]);
43012 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43014 this.toolbar.items = nitems;
43015 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
43016 delete config.toolbar;
43020 // xtype created footer. - not sure if will work as we normally have to render first..
43021 if (this.footer && !this.footer.el && this.footer.xtype) {
43022 if (!this.wrapEl) {
43023 this.wrapEl = this.el.wrap();
43026 this.footer.container = this.wrapEl.createChild();
43028 this.footer = Roo.factory(this.footer, Roo);
43033 if(typeof config == "string"){
43034 this.title = config;
43036 Roo.apply(this, config);
43040 this.resizeEl = Roo.get(this.resizeEl, true);
43042 this.resizeEl = this.el;
43044 // handle view.xtype
43052 * Fires when this panel is activated.
43053 * @param {Roo.ContentPanel} this
43057 * @event deactivate
43058 * Fires when this panel is activated.
43059 * @param {Roo.ContentPanel} this
43061 "deactivate" : true,
43065 * Fires when this panel is resized if fitToFrame is true.
43066 * @param {Roo.ContentPanel} this
43067 * @param {Number} width The width after any component adjustments
43068 * @param {Number} height The height after any component adjustments
43074 * Fires when this tab is created
43075 * @param {Roo.ContentPanel} this
43081 * Fires when this content is scrolled
43082 * @param {Roo.ContentPanel} this
43083 * @param {Event} scrollEvent
43094 if(this.autoScroll && !this.iframe){
43095 this.resizeEl.setStyle("overflow", "auto");
43096 this.resizeEl.on('scroll', this.onScroll, this);
43098 // fix randome scrolling
43099 //this.el.on('scroll', function() {
43100 // Roo.log('fix random scolling');
43101 // this.scrollTo('top',0);
43104 content = content || this.content;
43106 this.setContent(content);
43108 if(config && config.url){
43109 this.setUrl(this.url, this.params, this.loadOnce);
43114 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
43116 if (this.view && typeof(this.view.xtype) != 'undefined') {
43117 this.view.el = this.el.appendChild(document.createElement("div"));
43118 this.view = Roo.factory(this.view);
43119 this.view.render && this.view.render(false, '');
43123 this.fireEvent('render', this);
43126 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
43136 /* Resize Element - use this to work out scroll etc. */
43139 setRegion : function(region){
43140 this.region = region;
43141 this.setActiveClass(region && !this.background);
43145 setActiveClass: function(state)
43148 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
43149 this.el.setStyle('position','relative');
43151 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
43152 this.el.setStyle('position', 'absolute');
43157 * Returns the toolbar for this Panel if one was configured.
43158 * @return {Roo.Toolbar}
43160 getToolbar : function(){
43161 return this.toolbar;
43164 setActiveState : function(active)
43166 this.active = active;
43167 this.setActiveClass(active);
43169 if(this.fireEvent("deactivate", this) === false){
43174 this.fireEvent("activate", this);
43178 * Updates this panel's element (not for iframe)
43179 * @param {String} content The new content
43180 * @param {Boolean} loadScripts (optional) true to look for and process scripts
43182 setContent : function(content, loadScripts){
43187 this.el.update(content, loadScripts);
43190 ignoreResize : function(w, h)
43192 //return false; // always resize?
43193 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
43196 this.lastSize = {width: w, height: h};
43201 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
43202 * @return {Roo.UpdateManager} The UpdateManager
43204 getUpdateManager : function(){
43208 return this.el.getUpdateManager();
43211 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
43212 * Does not work with IFRAME contents
43213 * @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:
43216 url: "your-url.php",
43217 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
43218 callback: yourFunction,
43219 scope: yourObject, //(optional scope)
43222 text: "Loading...",
43228 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
43229 * 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.
43230 * @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}
43231 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
43232 * @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.
43233 * @return {Roo.ContentPanel} this
43241 var um = this.el.getUpdateManager();
43242 um.update.apply(um, arguments);
43248 * 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.
43249 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
43250 * @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)
43251 * @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)
43252 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
43254 setUrl : function(url, params, loadOnce){
43256 this.iframeEl.dom.src = url;
43260 if(this.refreshDelegate){
43261 this.removeListener("activate", this.refreshDelegate);
43263 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
43264 this.on("activate", this.refreshDelegate);
43265 return this.el.getUpdateManager();
43268 _handleRefresh : function(url, params, loadOnce){
43269 if(!loadOnce || !this.loaded){
43270 var updater = this.el.getUpdateManager();
43271 updater.update(url, params, this._setLoaded.createDelegate(this));
43275 _setLoaded : function(){
43276 this.loaded = true;
43280 * Returns this panel's id
43283 getId : function(){
43288 * Returns this panel's element - used by regiosn to add.
43289 * @return {Roo.Element}
43291 getEl : function(){
43292 return this.wrapEl || this.el;
43297 adjustForComponents : function(width, height)
43299 //Roo.log('adjustForComponents ');
43300 if(this.resizeEl != this.el){
43301 width -= this.el.getFrameWidth('lr');
43302 height -= this.el.getFrameWidth('tb');
43305 var te = this.toolbar.getEl();
43306 te.setWidth(width);
43307 height -= te.getHeight();
43310 var te = this.footer.getEl();
43311 te.setWidth(width);
43312 height -= te.getHeight();
43316 if(this.adjustments){
43317 width += this.adjustments[0];
43318 height += this.adjustments[1];
43320 return {"width": width, "height": height};
43323 setSize : function(width, height){
43324 if(this.fitToFrame && !this.ignoreResize(width, height)){
43325 if(this.fitContainer && this.resizeEl != this.el){
43326 this.el.setSize(width, height);
43328 var size = this.adjustForComponents(width, height);
43330 this.iframeEl.setSize(width,height);
43333 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
43334 this.fireEvent('resize', this, size.width, size.height);
43341 * Returns this panel's title
43344 getTitle : function(){
43346 if (typeof(this.title) != 'object') {
43351 for (var k in this.title) {
43352 if (!this.title.hasOwnProperty(k)) {
43356 if (k.indexOf('-') >= 0) {
43357 var s = k.split('-');
43358 for (var i = 0; i<s.length; i++) {
43359 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
43362 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
43369 * Set this panel's title
43370 * @param {String} title
43372 setTitle : function(title){
43373 this.title = title;
43375 this.region.updatePanelTitle(this, title);
43380 * Returns true is this panel was configured to be closable
43381 * @return {Boolean}
43383 isClosable : function(){
43384 return this.closable;
43387 beforeSlide : function(){
43389 this.resizeEl.clip();
43392 afterSlide : function(){
43394 this.resizeEl.unclip();
43398 * Force a content refresh from the URL specified in the {@link #setUrl} method.
43399 * Will fail silently if the {@link #setUrl} method has not been called.
43400 * This does not activate the panel, just updates its content.
43402 refresh : function(){
43403 if(this.refreshDelegate){
43404 this.loaded = false;
43405 this.refreshDelegate();
43410 * Destroys this panel
43412 destroy : function(){
43413 this.el.removeAllListeners();
43414 var tempEl = document.createElement("span");
43415 tempEl.appendChild(this.el.dom);
43416 tempEl.innerHTML = "";
43422 * form - if the content panel contains a form - this is a reference to it.
43423 * @type {Roo.form.Form}
43427 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
43428 * This contains a reference to it.
43434 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
43444 * @param {Object} cfg Xtype definition of item to add.
43448 getChildContainer: function () {
43449 return this.getEl();
43453 onScroll : function(e)
43455 this.fireEvent('scroll', this, e);
43460 var ret = new Roo.factory(cfg);
43465 if (cfg.xtype.match(/^Form$/)) {
43468 //if (this.footer) {
43469 // el = this.footer.container.insertSibling(false, 'before');
43471 el = this.el.createChild();
43474 this.form = new Roo.form.Form(cfg);
43477 if ( this.form.allItems.length) {
43478 this.form.render(el.dom);
43482 // should only have one of theses..
43483 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
43484 // views.. should not be just added - used named prop 'view''
43486 cfg.el = this.el.appendChild(document.createElement("div"));
43489 var ret = new Roo.factory(cfg);
43491 ret.render && ret.render(false, ''); // render blank..
43501 * @class Roo.bootstrap.panel.Grid
43502 * @extends Roo.bootstrap.panel.Content
43504 * Create a new GridPanel.
43505 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
43506 * @cfg {Roo.bootstrap.nav.Simplebar} toolbar the toolbar at the top of the grid.
43507 * @param {Object} config A the config object
43513 Roo.bootstrap.panel.Grid = function(config)
43517 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
43518 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
43520 config.el = this.wrapper;
43521 //this.el = this.wrapper;
43523 if (config.container) {
43524 // ctor'ed from a Border/panel.grid
43527 this.wrapper.setStyle("overflow", "hidden");
43528 this.wrapper.addClass('roo-grid-container');
43533 if(config.toolbar){
43534 var tool_el = this.wrapper.createChild();
43535 this.toolbar = Roo.factory(config.toolbar);
43537 if (config.toolbar.items) {
43538 ti = config.toolbar.items ;
43539 delete config.toolbar.items ;
43543 this.toolbar.render(tool_el);
43544 for(var i =0;i < ti.length;i++) {
43545 // Roo.log(['add child', items[i]]);
43546 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
43548 this.toolbar.items = nitems;
43550 delete config.toolbar;
43553 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
43554 config.grid.scrollBody = true;;
43555 config.grid.monitorWindowResize = false; // turn off autosizing
43556 config.grid.autoHeight = false;
43557 config.grid.autoWidth = false;
43559 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
43561 if (config.background) {
43562 // render grid on panel activation (if panel background)
43563 this.on('activate', function(gp) {
43564 if (!gp.grid.rendered) {
43565 gp.grid.render(this.wrapper);
43566 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
43571 this.grid.render(this.wrapper);
43572 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
43575 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
43576 // ??? needed ??? config.el = this.wrapper;
43581 // xtype created footer. - not sure if will work as we normally have to render first..
43582 if (this.footer && !this.footer.el && this.footer.xtype) {
43584 var ctr = this.grid.getView().getFooterPanel(true);
43585 this.footer.dataSource = this.grid.dataSource;
43586 this.footer = Roo.factory(this.footer, Roo);
43587 this.footer.render(ctr);
43597 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content,
43600 getId : function(){
43601 return this.grid.id;
43605 * Returns the grid for this panel
43606 * @return {Roo.bootstrap.Table}
43608 getGrid : function(){
43612 setSize : function(width, height)
43615 //if(!this.ignoreResize(width, height)){
43616 var grid = this.grid;
43617 var size = this.adjustForComponents(width, height);
43618 // tfoot is not a footer?
43621 var gridel = grid.getGridEl();
43622 gridel.setSize(size.width, size.height);
43624 var tbd = grid.getGridEl().select('tbody', true).first();
43625 var thd = grid.getGridEl().select('thead',true).first();
43626 var tbf= grid.getGridEl().select('tfoot', true).first();
43629 size.height -= tbf.getHeight();
43632 size.height -= thd.getHeight();
43635 tbd.setSize(size.width, size.height );
43636 // this is for the account management tab -seems to work there.
43637 var thd = grid.getGridEl().select('thead',true).first();
43639 // tbd.setSize(size.width, size.height - thd.getHeight());
43649 beforeSlide : function(){
43650 this.grid.getView().scroller.clip();
43653 afterSlide : function(){
43654 this.grid.getView().scroller.unclip();
43657 destroy : function(){
43658 this.grid.destroy();
43660 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
43665 * @class Roo.bootstrap.panel.Nest
43666 * @extends Roo.bootstrap.panel.Content
43668 * Create a new Panel, that can contain a layout.Border.
43671 * @param {String/Object} config A string to set only the title or a config object
43673 Roo.bootstrap.panel.Nest = function(config)
43675 // construct with only one argument..
43676 /* FIXME - implement nicer consturctors
43677 if (layout.layout) {
43679 layout = config.layout;
43680 delete config.layout;
43682 if (layout.xtype && !layout.getEl) {
43683 // then layout needs constructing..
43684 layout = Roo.factory(layout, Roo);
43688 config.el = config.layout.getEl();
43690 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
43692 config.layout.monitorWindowResize = false; // turn off autosizing
43693 this.layout = config.layout;
43694 this.layout.getEl().addClass("roo-layout-nested-layout");
43695 this.layout.parent = this;
43702 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
43704 * @cfg {Roo.BorderLayout} layout The layout for this panel
43708 setSize : function(width, height){
43709 if(!this.ignoreResize(width, height)){
43710 var size = this.adjustForComponents(width, height);
43711 var el = this.layout.getEl();
43712 if (size.height < 1) {
43713 el.setWidth(size.width);
43715 el.setSize(size.width, size.height);
43717 var touch = el.dom.offsetWidth;
43718 this.layout.layout();
43719 // ie requires a double layout on the first pass
43720 if(Roo.isIE && !this.initialized){
43721 this.initialized = true;
43722 this.layout.layout();
43727 // activate all subpanels if not currently active..
43729 setActiveState : function(active){
43730 this.active = active;
43731 this.setActiveClass(active);
43734 this.fireEvent("deactivate", this);
43738 this.fireEvent("activate", this);
43739 // not sure if this should happen before or after..
43740 if (!this.layout) {
43741 return; // should not happen..
43744 for (var r in this.layout.regions) {
43745 reg = this.layout.getRegion(r);
43746 if (reg.getActivePanel()) {
43747 //reg.showPanel(reg.getActivePanel()); // force it to activate..
43748 reg.setActivePanel(reg.getActivePanel());
43751 if (!reg.panels.length) {
43754 reg.showPanel(reg.getPanel(0));
43763 * Returns the nested BorderLayout for this panel
43764 * @return {Roo.BorderLayout}
43766 getLayout : function(){
43767 return this.layout;
43771 * Adds a xtype elements to the layout of the nested panel
43775 xtype : 'ContentPanel',
43782 xtype : 'NestedLayoutPanel',
43788 items : [ ... list of content panels or nested layout panels.. ]
43792 * @param {Object} cfg Xtype definition of item to add.
43794 addxtype : function(cfg) {
43795 return this.layout.addxtype(cfg);
43800 * Ext JS Library 1.1.1
43801 * Copyright(c) 2006-2007, Ext JS, LLC.
43803 * Originally Released Under LGPL - original licence link has changed is not relivant.
43806 * <script type="text/javascript">
43809 * @class Roo.TabPanel
43810 * @extends Roo.util.Observable
43811 * A lightweight tab container.
43815 // basic tabs 1, built from existing content
43816 var tabs = new Roo.TabPanel("tabs1");
43817 tabs.addTab("script", "View Script");
43818 tabs.addTab("markup", "View Markup");
43819 tabs.activate("script");
43821 // more advanced tabs, built from javascript
43822 var jtabs = new Roo.TabPanel("jtabs");
43823 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
43825 // set up the UpdateManager
43826 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
43827 var updater = tab2.getUpdateManager();
43828 updater.setDefaultUrl("ajax1.htm");
43829 tab2.on('activate', updater.refresh, updater, true);
43831 // Use setUrl for Ajax loading
43832 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
43833 tab3.setUrl("ajax2.htm", null, true);
43836 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
43839 jtabs.activate("jtabs-1");
43842 * Create a new TabPanel.
43843 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
43844 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
43846 Roo.bootstrap.panel.Tabs = function(config){
43848 * The container element for this TabPanel.
43849 * @type Roo.Element
43851 this.el = Roo.get(config.el);
43854 if(typeof config == "boolean"){
43855 this.tabPosition = config ? "bottom" : "top";
43857 Roo.apply(this, config);
43861 if(this.tabPosition == "bottom"){
43862 // if tabs are at the bottom = create the body first.
43863 this.bodyEl = Roo.get(this.createBody(this.el.dom));
43864 this.el.addClass("roo-tabs-bottom");
43866 // next create the tabs holders
43868 if (this.tabPosition == "west"){
43870 var reg = this.region; // fake it..
43872 if (!reg.mgr.parent) {
43875 reg = reg.mgr.parent.region;
43877 Roo.log("got nest?");
43879 if (reg.mgr.getRegion('west')) {
43880 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
43881 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
43882 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43883 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43884 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43892 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
43893 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
43894 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
43895 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
43900 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
43903 // finally - if tabs are at the top, then create the body last..
43904 if(this.tabPosition != "bottom"){
43905 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
43906 * @type Roo.Element
43908 this.bodyEl = Roo.get(this.createBody(this.el.dom));
43909 this.el.addClass("roo-tabs-top");
43913 this.bodyEl.setStyle("position", "relative");
43915 this.active = null;
43916 this.activateDelegate = this.activate.createDelegate(this);
43921 * Fires when the active tab changes
43922 * @param {Roo.TabPanel} this
43923 * @param {Roo.TabPanelItem} activePanel The new active tab
43927 * @event beforetabchange
43928 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
43929 * @param {Roo.TabPanel} this
43930 * @param {Object} e Set cancel to true on this object to cancel the tab change
43931 * @param {Roo.TabPanelItem} tab The tab being changed to
43933 "beforetabchange" : true
43936 Roo.EventManager.onWindowResize(this.onResize, this);
43937 this.cpad = this.el.getPadding("lr");
43938 this.hiddenCount = 0;
43941 // toolbar on the tabbar support...
43942 if (this.toolbar) {
43943 alert("no toolbar support yet");
43944 this.toolbar = false;
43946 var tcfg = this.toolbar;
43947 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
43948 this.toolbar = new Roo.Toolbar(tcfg);
43949 if (Roo.isSafari) {
43950 var tbl = tcfg.container.child('table', true);
43951 tbl.setAttribute('width', '100%');
43959 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
43962 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
43964 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
43966 tabPosition : "top",
43968 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
43970 currentTabWidth : 0,
43972 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
43976 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
43980 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
43982 preferredTabWidth : 175,
43984 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
43986 resizeTabs : false,
43988 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
43990 monitorResize : true,
43992 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
43994 toolbar : false, // set by caller..
43996 region : false, /// set by caller
43998 disableTooltips : true, // not used yet...
44001 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
44002 * @param {String} id The id of the div to use <b>or create</b>
44003 * @param {String} text The text for the tab
44004 * @param {String} content (optional) Content to put in the TabPanelItem body
44005 * @param {Boolean} closable (optional) True to create a close icon on the tab
44006 * @return {Roo.TabPanelItem} The created TabPanelItem
44008 addTab : function(id, text, content, closable, tpl)
44010 var item = new Roo.bootstrap.panel.TabItem({
44014 closable : closable,
44017 this.addTabItem(item);
44019 item.setContent(content);
44025 * Returns the {@link Roo.TabPanelItem} with the specified id/index
44026 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
44027 * @return {Roo.TabPanelItem}
44029 getTab : function(id){
44030 return this.items[id];
44034 * Hides the {@link Roo.TabPanelItem} with the specified id/index
44035 * @param {String/Number} id The id or index of the TabPanelItem to hide.
44037 hideTab : function(id){
44038 var t = this.items[id];
44041 this.hiddenCount++;
44042 this.autoSizeTabs();
44047 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
44048 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
44050 unhideTab : function(id){
44051 var t = this.items[id];
44053 t.setHidden(false);
44054 this.hiddenCount--;
44055 this.autoSizeTabs();
44060 * Adds an existing {@link Roo.TabPanelItem}.
44061 * @param {Roo.TabPanelItem} item The TabPanelItem to add
44063 addTabItem : function(item)
44065 this.items[item.id] = item;
44066 this.items.push(item);
44067 this.autoSizeTabs();
44068 // if(this.resizeTabs){
44069 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
44070 // this.autoSizeTabs();
44072 // item.autoSize();
44077 * Removes a {@link Roo.TabPanelItem}.
44078 * @param {String/Number} id The id or index of the TabPanelItem to remove.
44080 removeTab : function(id){
44081 var items = this.items;
44082 var tab = items[id];
44083 if(!tab) { return; }
44084 var index = items.indexOf(tab);
44085 if(this.active == tab && items.length > 1){
44086 var newTab = this.getNextAvailable(index);
44091 this.stripEl.dom.removeChild(tab.pnode.dom);
44092 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
44093 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
44095 items.splice(index, 1);
44096 delete this.items[tab.id];
44097 tab.fireEvent("close", tab);
44098 tab.purgeListeners();
44099 this.autoSizeTabs();
44102 getNextAvailable : function(start){
44103 var items = this.items;
44105 // look for a next tab that will slide over to
44106 // replace the one being removed
44107 while(index < items.length){
44108 var item = items[++index];
44109 if(item && !item.isHidden()){
44113 // if one isn't found select the previous tab (on the left)
44116 var item = items[--index];
44117 if(item && !item.isHidden()){
44125 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
44126 * @param {String/Number} id The id or index of the TabPanelItem to disable.
44128 disableTab : function(id){
44129 var tab = this.items[id];
44130 if(tab && this.active != tab){
44136 * Enables a {@link Roo.TabPanelItem} that is disabled.
44137 * @param {String/Number} id The id or index of the TabPanelItem to enable.
44139 enableTab : function(id){
44140 var tab = this.items[id];
44145 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
44146 * @param {String/Number} id The id or index of the TabPanelItem to activate.
44147 * @return {Roo.TabPanelItem} The TabPanelItem.
44149 activate : function(id)
44151 //Roo.log('activite:' + id);
44153 var tab = this.items[id];
44157 if(tab == this.active || tab.disabled){
44161 this.fireEvent("beforetabchange", this, e, tab);
44162 if(e.cancel !== true && !tab.disabled){
44164 this.active.hide();
44166 this.active = this.items[id];
44167 this.active.show();
44168 this.fireEvent("tabchange", this, this.active);
44174 * Gets the active {@link Roo.TabPanelItem}.
44175 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
44177 getActiveTab : function(){
44178 return this.active;
44182 * Updates the tab body element to fit the height of the container element
44183 * for overflow scrolling
44184 * @param {Number} targetHeight (optional) Override the starting height from the elements height
44186 syncHeight : function(targetHeight){
44187 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
44188 var bm = this.bodyEl.getMargins();
44189 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
44190 this.bodyEl.setHeight(newHeight);
44194 onResize : function(){
44195 if(this.monitorResize){
44196 this.autoSizeTabs();
44201 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
44203 beginUpdate : function(){
44204 this.updating = true;
44208 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
44210 endUpdate : function(){
44211 this.updating = false;
44212 this.autoSizeTabs();
44216 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
44218 autoSizeTabs : function()
44220 var count = this.items.length;
44221 var vcount = count - this.hiddenCount;
44224 this.stripEl.hide();
44226 this.stripEl.show();
44229 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
44234 var w = Math.max(this.el.getWidth() - this.cpad, 10);
44235 var availWidth = Math.floor(w / vcount);
44236 var b = this.stripBody;
44237 if(b.getWidth() > w){
44238 var tabs = this.items;
44239 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
44240 if(availWidth < this.minTabWidth){
44241 /*if(!this.sleft){ // incomplete scrolling code
44242 this.createScrollButtons();
44245 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
44248 if(this.currentTabWidth < this.preferredTabWidth){
44249 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
44255 * Returns the number of tabs in this TabPanel.
44258 getCount : function(){
44259 return this.items.length;
44263 * Resizes all the tabs to the passed width
44264 * @param {Number} The new width
44266 setTabWidth : function(width){
44267 this.currentTabWidth = width;
44268 for(var i = 0, len = this.items.length; i < len; i++) {
44269 if(!this.items[i].isHidden()) {
44270 this.items[i].setWidth(width);
44276 * Destroys this TabPanel
44277 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
44279 destroy : function(removeEl){
44280 Roo.EventManager.removeResizeListener(this.onResize, this);
44281 for(var i = 0, len = this.items.length; i < len; i++){
44282 this.items[i].purgeListeners();
44284 if(removeEl === true){
44285 this.el.update("");
44290 createStrip : function(container)
44292 var strip = document.createElement("nav");
44293 strip.className = Roo.bootstrap.version == 4 ?
44294 "navbar-light bg-light" :
44295 "navbar navbar-default"; //"x-tabs-wrap";
44296 container.appendChild(strip);
44300 createStripList : function(strip)
44302 // div wrapper for retard IE
44303 // returns the "tr" element.
44304 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
44305 //'<div class="x-tabs-strip-wrap">'+
44306 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
44307 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
44308 return strip.firstChild; //.firstChild.firstChild.firstChild;
44310 createBody : function(container)
44312 var body = document.createElement("div");
44313 Roo.id(body, "tab-body");
44314 //Roo.fly(body).addClass("x-tabs-body");
44315 Roo.fly(body).addClass("tab-content");
44316 container.appendChild(body);
44319 createItemBody :function(bodyEl, id){
44320 var body = Roo.getDom(id);
44322 body = document.createElement("div");
44325 //Roo.fly(body).addClass("x-tabs-item-body");
44326 Roo.fly(body).addClass("tab-pane");
44327 bodyEl.insertBefore(body, bodyEl.firstChild);
44331 createStripElements : function(stripEl, text, closable, tpl)
44333 var td = document.createElement("li"); // was td..
44334 td.className = 'nav-item';
44336 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
44339 stripEl.appendChild(td);
44341 td.className = "x-tabs-closable";
44342 if(!this.closeTpl){
44343 this.closeTpl = new Roo.Template(
44344 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44345 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
44346 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
44349 var el = this.closeTpl.overwrite(td, {"text": text});
44350 var close = el.getElementsByTagName("div")[0];
44351 var inner = el.getElementsByTagName("em")[0];
44352 return {"el": el, "close": close, "inner": inner};
44355 // not sure what this is..
44356 // if(!this.tabTpl){
44357 //this.tabTpl = new Roo.Template(
44358 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
44359 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
44361 // this.tabTpl = new Roo.Template(
44362 // '<a href="#">' +
44363 // '<span unselectable="on"' +
44364 // (this.disableTooltips ? '' : ' title="{text}"') +
44365 // ' >{text}</span></a>'
44371 var template = tpl || this.tabTpl || false;
44374 template = new Roo.Template(
44375 Roo.bootstrap.version == 4 ?
44377 '<a class="nav-link" href="#" unselectable="on"' +
44378 (this.disableTooltips ? '' : ' title="{text}"') +
44381 '<a class="nav-link" href="#">' +
44382 '<span unselectable="on"' +
44383 (this.disableTooltips ? '' : ' title="{text}"') +
44384 ' >{text}</span></a>'
44389 switch (typeof(template)) {
44393 template = new Roo.Template(template);
44399 var el = template.overwrite(td, {"text": text});
44401 var inner = el.getElementsByTagName("span")[0];
44403 return {"el": el, "inner": inner};
44411 * @class Roo.TabPanelItem
44412 * @extends Roo.util.Observable
44413 * Represents an individual item (tab plus body) in a TabPanel.
44414 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
44415 * @param {String} id The id of this TabPanelItem
44416 * @param {String} text The text for the tab of this TabPanelItem
44417 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
44419 Roo.bootstrap.panel.TabItem = function(config){
44421 * The {@link Roo.TabPanel} this TabPanelItem belongs to
44422 * @type Roo.TabPanel
44424 this.tabPanel = config.panel;
44426 * The id for this TabPanelItem
44429 this.id = config.id;
44431 this.disabled = false;
44433 this.text = config.text;
44435 this.loaded = false;
44436 this.closable = config.closable;
44439 * The body element for this TabPanelItem.
44440 * @type Roo.Element
44442 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
44443 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
44444 this.bodyEl.setStyle("display", "block");
44445 this.bodyEl.setStyle("zoom", "1");
44446 //this.hideAction();
44448 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
44450 this.el = Roo.get(els.el);
44451 this.inner = Roo.get(els.inner, true);
44452 this.textEl = Roo.bootstrap.version == 4 ?
44453 this.el : Roo.get(this.el.dom.firstChild, true);
44455 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
44456 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
44459 // this.el.on("mousedown", this.onTabMouseDown, this);
44460 this.el.on("click", this.onTabClick, this);
44462 if(config.closable){
44463 var c = Roo.get(els.close, true);
44464 c.dom.title = this.closeText;
44465 c.addClassOnOver("close-over");
44466 c.on("click", this.closeClick, this);
44472 * Fires when this tab becomes the active tab.
44473 * @param {Roo.TabPanel} tabPanel The parent TabPanel
44474 * @param {Roo.TabPanelItem} this
44478 * @event beforeclose
44479 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
44480 * @param {Roo.TabPanelItem} this
44481 * @param {Object} e Set cancel to true on this object to cancel the close.
44483 "beforeclose": true,
44486 * Fires when this tab is closed.
44487 * @param {Roo.TabPanelItem} this
44491 * @event deactivate
44492 * Fires when this tab is no longer the active tab.
44493 * @param {Roo.TabPanel} tabPanel The parent TabPanel
44494 * @param {Roo.TabPanelItem} this
44496 "deactivate" : true
44498 this.hidden = false;
44500 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
44503 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
44505 purgeListeners : function(){
44506 Roo.util.Observable.prototype.purgeListeners.call(this);
44507 this.el.removeAllListeners();
44510 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
44513 this.status_node.addClass("active");
44516 this.tabPanel.stripWrap.repaint();
44518 this.fireEvent("activate", this.tabPanel, this);
44522 * Returns true if this tab is the active tab.
44523 * @return {Boolean}
44525 isActive : function(){
44526 return this.tabPanel.getActiveTab() == this;
44530 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
44533 this.status_node.removeClass("active");
44535 this.fireEvent("deactivate", this.tabPanel, this);
44538 hideAction : function(){
44539 this.bodyEl.hide();
44540 this.bodyEl.setStyle("position", "absolute");
44541 this.bodyEl.setLeft("-20000px");
44542 this.bodyEl.setTop("-20000px");
44545 showAction : function(){
44546 this.bodyEl.setStyle("position", "relative");
44547 this.bodyEl.setTop("");
44548 this.bodyEl.setLeft("");
44549 this.bodyEl.show();
44553 * Set the tooltip for the tab.
44554 * @param {String} tooltip The tab's tooltip
44556 setTooltip : function(text){
44557 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
44558 this.textEl.dom.qtip = text;
44559 this.textEl.dom.removeAttribute('title');
44561 this.textEl.dom.title = text;
44565 onTabClick : function(e){
44566 e.preventDefault();
44567 this.tabPanel.activate(this.id);
44570 onTabMouseDown : function(e){
44571 e.preventDefault();
44572 this.tabPanel.activate(this.id);
44575 getWidth : function(){
44576 return this.inner.getWidth();
44579 setWidth : function(width){
44580 var iwidth = width - this.linode.getPadding("lr");
44581 this.inner.setWidth(iwidth);
44582 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
44583 this.linode.setWidth(width);
44587 * Show or hide the tab
44588 * @param {Boolean} hidden True to hide or false to show.
44590 setHidden : function(hidden){
44591 this.hidden = hidden;
44592 this.linode.setStyle("display", hidden ? "none" : "");
44596 * Returns true if this tab is "hidden"
44597 * @return {Boolean}
44599 isHidden : function(){
44600 return this.hidden;
44604 * Returns the text for this tab
44607 getText : function(){
44611 autoSize : function(){
44612 //this.el.beginMeasure();
44613 this.textEl.setWidth(1);
44615 * #2804 [new] Tabs in Roojs
44616 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
44618 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
44619 //this.el.endMeasure();
44623 * Sets the text for the tab (Note: this also sets the tooltip text)
44624 * @param {String} text The tab's text and tooltip
44626 setText : function(text){
44628 this.textEl.update(text);
44629 this.setTooltip(text);
44630 //if(!this.tabPanel.resizeTabs){
44631 // this.autoSize();
44635 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
44637 activate : function(){
44638 this.tabPanel.activate(this.id);
44642 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
44644 disable : function(){
44645 if(this.tabPanel.active != this){
44646 this.disabled = true;
44647 this.status_node.addClass("disabled");
44652 * Enables this TabPanelItem if it was previously disabled.
44654 enable : function(){
44655 this.disabled = false;
44656 this.status_node.removeClass("disabled");
44660 * Sets the content for this TabPanelItem.
44661 * @param {String} content The content
44662 * @param {Boolean} loadScripts true to look for and load scripts
44664 setContent : function(content, loadScripts){
44665 this.bodyEl.update(content, loadScripts);
44669 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
44670 * @return {Roo.UpdateManager} The UpdateManager
44672 getUpdateManager : function(){
44673 return this.bodyEl.getUpdateManager();
44677 * Set a URL to be used to load the content for this TabPanelItem.
44678 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
44679 * @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)
44680 * @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)
44681 * @return {Roo.UpdateManager} The UpdateManager
44683 setUrl : function(url, params, loadOnce){
44684 if(this.refreshDelegate){
44685 this.un('activate', this.refreshDelegate);
44687 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44688 this.on("activate", this.refreshDelegate);
44689 return this.bodyEl.getUpdateManager();
44693 _handleRefresh : function(url, params, loadOnce){
44694 if(!loadOnce || !this.loaded){
44695 var updater = this.bodyEl.getUpdateManager();
44696 updater.update(url, params, this._setLoaded.createDelegate(this));
44701 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
44702 * Will fail silently if the setUrl method has not been called.
44703 * This does not activate the panel, just updates its content.
44705 refresh : function(){
44706 if(this.refreshDelegate){
44707 this.loaded = false;
44708 this.refreshDelegate();
44713 _setLoaded : function(){
44714 this.loaded = true;
44718 closeClick : function(e){
44721 this.fireEvent("beforeclose", this, o);
44722 if(o.cancel !== true){
44723 this.tabPanel.removeTab(this.id);
44727 * The text displayed in the tooltip for the close icon.
44730 closeText : "Close this tab"
44733 * This script refer to:
44734 * Title: International Telephone Input
44735 * Author: Jack O'Connor
44736 * Code version: v12.1.12
44737 * Availability: https://github.com/jackocnr/intl-tel-input.git
44740 Roo.bootstrap.form.PhoneInputData = function() {
44743 "Afghanistan (افغانستان)",
44748 "Albania (Shqipëri)",
44753 "Algeria (الجزائر)",
44778 "Antigua and Barbuda",
44788 "Armenia (Հայաստան)",
44804 "Austria (Österreich)",
44809 "Azerbaijan (Azərbaycan)",
44819 "Bahrain (البحرين)",
44824 "Bangladesh (বাংলাদেশ)",
44834 "Belarus (Беларусь)",
44839 "Belgium (België)",
44869 "Bosnia and Herzegovina (Босна и Херцеговина)",
44884 "British Indian Ocean Territory",
44889 "British Virgin Islands",
44899 "Bulgaria (България)",
44909 "Burundi (Uburundi)",
44914 "Cambodia (កម្ពុជា)",
44919 "Cameroon (Cameroun)",
44928 ["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"]
44931 "Cape Verde (Kabu Verdi)",
44936 "Caribbean Netherlands",
44947 "Central African Republic (République centrafricaine)",
44967 "Christmas Island",
44973 "Cocos (Keeling) Islands",
44984 "Comoros (جزر القمر)",
44989 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
44994 "Congo (Republic) (Congo-Brazzaville)",
45014 "Croatia (Hrvatska)",
45035 "Czech Republic (Česká republika)",
45040 "Denmark (Danmark)",
45055 "Dominican Republic (República Dominicana)",
45059 ["809", "829", "849"]
45077 "Equatorial Guinea (Guinea Ecuatorial)",
45097 "Falkland Islands (Islas Malvinas)",
45102 "Faroe Islands (Føroyar)",
45123 "French Guiana (Guyane française)",
45128 "French Polynesia (Polynésie française)",
45143 "Georgia (საქართველო)",
45148 "Germany (Deutschland)",
45168 "Greenland (Kalaallit Nunaat)",
45205 "Guinea-Bissau (Guiné Bissau)",
45230 "Hungary (Magyarország)",
45235 "Iceland (Ísland)",
45255 "Iraq (العراق)",
45271 "Israel (ישראל)",
45298 "Jordan (الأردن)",
45303 "Kazakhstan (Казахстан)",
45324 "Kuwait (الكويت)",
45329 "Kyrgyzstan (Кыргызстан)",
45339 "Latvia (Latvija)",
45344 "Lebanon (لبنان)",
45359 "Libya (ليبيا)",
45369 "Lithuania (Lietuva)",
45384 "Macedonia (FYROM) (Македонија)",
45389 "Madagascar (Madagasikara)",
45419 "Marshall Islands",
45429 "Mauritania (موريتانيا)",
45434 "Mauritius (Moris)",
45455 "Moldova (Republica Moldova)",
45465 "Mongolia (Монгол)",
45470 "Montenegro (Crna Gora)",
45480 "Morocco (المغرب)",
45486 "Mozambique (Moçambique)",
45491 "Myanmar (Burma) (မြန်မာ)",
45496 "Namibia (Namibië)",
45511 "Netherlands (Nederland)",
45516 "New Caledonia (Nouvelle-Calédonie)",
45551 "North Korea (조선 민주주의 인민 공화국)",
45556 "Northern Mariana Islands",
45572 "Pakistan (پاکستان)",
45582 "Palestine (فلسطين)",
45592 "Papua New Guinea",
45634 "Réunion (La Réunion)",
45640 "Romania (România)",
45656 "Saint Barthélemy",
45667 "Saint Kitts and Nevis",
45677 "Saint Martin (Saint-Martin (partie française))",
45683 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
45688 "Saint Vincent and the Grenadines",
45703 "São Tomé and Príncipe (São Tomé e Príncipe)",
45708 "Saudi Arabia (المملكة العربية السعودية)",
45713 "Senegal (Sénégal)",
45743 "Slovakia (Slovensko)",
45748 "Slovenia (Slovenija)",
45758 "Somalia (Soomaaliya)",
45768 "South Korea (대한민국)",
45773 "South Sudan (جنوب السودان)",
45783 "Sri Lanka (ශ්රී ලංකාව)",
45788 "Sudan (السودان)",
45798 "Svalbard and Jan Mayen",
45809 "Sweden (Sverige)",
45814 "Switzerland (Schweiz)",
45819 "Syria (سوريا)",
45864 "Trinidad and Tobago",
45869 "Tunisia (تونس)",
45874 "Turkey (Türkiye)",
45884 "Turks and Caicos Islands",
45894 "U.S. Virgin Islands",
45904 "Ukraine (Україна)",
45909 "United Arab Emirates (الإمارات العربية المتحدة)",
45931 "Uzbekistan (Oʻzbekiston)",
45941 "Vatican City (Città del Vaticano)",
45952 "Vietnam (Việt Nam)",
45957 "Wallis and Futuna (Wallis-et-Futuna)",
45962 "Western Sahara (الصحراء الغربية)",
45968 "Yemen (اليمن)",
45992 * This script refer to:
45993 * Title: International Telephone Input
45994 * Author: Jack O'Connor
45995 * Code version: v12.1.12
45996 * Availability: https://github.com/jackocnr/intl-tel-input.git
46000 * @class Roo.bootstrap.form.PhoneInput
46001 * @extends Roo.bootstrap.form.TriggerField
46002 * An input with International dial-code selection
46004 * @cfg {String} defaultDialCode default '+852'
46005 * @cfg {Array} preferedCountries default []
46008 * Create a new PhoneInput.
46009 * @param {Object} config Configuration options
46012 Roo.bootstrap.form.PhoneInput = function(config) {
46013 Roo.bootstrap.form.PhoneInput.superclass.constructor.call(this, config);
46016 Roo.extend(Roo.bootstrap.form.PhoneInput, Roo.bootstrap.form.TriggerField, {
46018 * @cfg {Roo.data.Store} store [required] The data store to which this combo is bound (defaults to undefined)
46020 listWidth: undefined,
46022 selectedClass: 'active',
46024 invalidClass : "has-warning",
46026 validClass: 'has-success',
46028 allowed: '0123456789',
46033 * @cfg {String} defaultDialCode The default dial code when initializing the input
46035 defaultDialCode: '+852',
46038 * @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
46040 preferedCountries: false,
46042 getAutoCreate : function()
46044 var data = Roo.bootstrap.form.PhoneInputData();
46045 var align = this.labelAlign || this.parentLabelAlign();
46048 this.allCountries = [];
46049 this.dialCodeMapping = [];
46051 for (var i = 0; i < data.length; i++) {
46053 this.allCountries[i] = {
46057 priority: c[3] || 0,
46058 areaCodes: c[4] || null
46060 this.dialCodeMapping[c[2]] = {
46063 priority: c[3] || 0,
46064 areaCodes: c[4] || null
46076 // type: 'number', -- do not use number - we get the flaky up/down arrows.
46077 maxlength: this.max_length,
46078 cls : 'form-control tel-input',
46079 autocomplete: 'new-password'
46082 var hiddenInput = {
46085 cls: 'hidden-tel-input'
46089 hiddenInput.name = this.name;
46092 if (this.disabled) {
46093 input.disabled = true;
46096 var flag_container = {
46113 cls: this.hasFeedback ? 'has-feedback' : '',
46119 cls: 'dial-code-holder',
46126 cls: 'roo-select2-container input-group',
46133 if (this.fieldLabel.length) {
46136 tooltip: 'This field is required'
46142 cls: 'control-label',
46148 html: this.fieldLabel
46151 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46157 if(this.indicatorpos == 'right') {
46158 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46165 if(align == 'left') {
46173 if(this.labelWidth > 12){
46174 label.style = "width: " + this.labelWidth + 'px';
46176 if(this.labelWidth < 13 && this.labelmd == 0){
46177 this.labelmd = this.labelWidth;
46179 if(this.labellg > 0){
46180 label.cls += ' col-lg-' + this.labellg;
46181 input.cls += ' col-lg-' + (12 - this.labellg);
46183 if(this.labelmd > 0){
46184 label.cls += ' col-md-' + this.labelmd;
46185 container.cls += ' col-md-' + (12 - this.labelmd);
46187 if(this.labelsm > 0){
46188 label.cls += ' col-sm-' + this.labelsm;
46189 container.cls += ' col-sm-' + (12 - this.labelsm);
46191 if(this.labelxs > 0){
46192 label.cls += ' col-xs-' + this.labelxs;
46193 container.cls += ' col-xs-' + (12 - this.labelxs);
46203 var settings = this;
46205 ['xs','sm','md','lg'].map(function(size){
46206 if (settings[size]) {
46207 cfg.cls += ' col-' + size + '-' + settings[size];
46211 this.store = new Roo.data.Store({
46212 proxy : new Roo.data.MemoryProxy({}),
46213 reader : new Roo.data.JsonReader({
46224 'name' : 'dialCode',
46228 'name' : 'priority',
46232 'name' : 'areaCodes',
46239 if(!this.preferedCountries) {
46240 this.preferedCountries = [
46247 var p = this.preferedCountries.reverse();
46250 for (var i = 0; i < p.length; i++) {
46251 for (var j = 0; j < this.allCountries.length; j++) {
46252 if(this.allCountries[j].iso2 == p[i]) {
46253 var t = this.allCountries[j];
46254 this.allCountries.splice(j,1);
46255 this.allCountries.unshift(t);
46261 this.store.proxy.data = {
46263 data: this.allCountries
46269 initEvents : function()
46272 Roo.bootstrap.form.PhoneInput.superclass.initEvents.call(this);
46274 this.indicator = this.indicatorEl();
46275 this.flag = this.flagEl();
46276 this.dialCodeHolder = this.dialCodeHolderEl();
46278 this.trigger = this.el.select('div.flag-box',true).first();
46279 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
46284 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46285 _this.list.setWidth(lw);
46288 this.list.on('mouseover', this.onViewOver, this);
46289 this.list.on('mousemove', this.onViewMove, this);
46290 this.inputEl().on("keyup", this.onKeyUp, this);
46291 this.inputEl().on("keypress", this.onKeyPress, this);
46293 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
46295 this.view = new Roo.View(this.list, this.tpl, {
46296 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46299 this.view.on('click', this.onViewClick, this);
46300 this.setValue(this.defaultDialCode);
46303 onTriggerClick : function(e)
46305 Roo.log('trigger click');
46310 if(this.isExpanded()){
46312 this.hasFocus = false;
46314 this.store.load({});
46315 this.hasFocus = true;
46320 isExpanded : function()
46322 return this.list.isVisible();
46325 collapse : function()
46327 if(!this.isExpanded()){
46331 Roo.get(document).un('mousedown', this.collapseIf, this);
46332 Roo.get(document).un('mousewheel', this.collapseIf, this);
46333 this.fireEvent('collapse', this);
46337 expand : function()
46341 if(this.isExpanded() || !this.hasFocus){
46345 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
46346 this.list.setWidth(lw);
46349 this.restrictHeight();
46351 Roo.get(document).on('mousedown', this.collapseIf, this);
46352 Roo.get(document).on('mousewheel', this.collapseIf, this);
46354 this.fireEvent('expand', this);
46357 restrictHeight : function()
46359 this.list.alignTo(this.inputEl(), this.listAlign);
46360 this.list.alignTo(this.inputEl(), this.listAlign);
46363 onViewOver : function(e, t)
46365 if(this.inKeyMode){
46368 var item = this.view.findItemFromChild(t);
46371 var index = this.view.indexOf(item);
46372 this.select(index, false);
46377 onViewClick : function(view, doFocus, el, e)
46379 var index = this.view.getSelectedIndexes()[0];
46381 var r = this.store.getAt(index);
46384 this.onSelect(r, index);
46386 if(doFocus !== false && !this.blockFocus){
46387 this.inputEl().focus();
46391 onViewMove : function(e, t)
46393 this.inKeyMode = false;
46396 select : function(index, scrollIntoView)
46398 this.selectedIndex = index;
46399 this.view.select(index);
46400 if(scrollIntoView !== false){
46401 var el = this.view.getNode(index);
46403 this.list.scrollChildIntoView(el, false);
46408 createList : function()
46410 this.list = Roo.get(document.body).createChild({
46412 cls: 'typeahead typeahead-long dropdown-menu tel-list',
46413 style: 'display:none'
46416 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
46419 collapseIf : function(e)
46421 var in_combo = e.within(this.el);
46422 var in_list = e.within(this.list);
46423 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
46425 if (in_combo || in_list || is_list) {
46431 onSelect : function(record, index)
46433 if(this.fireEvent('beforeselect', this, record, index) !== false){
46435 this.setFlagClass(record.data.iso2);
46436 this.setDialCode(record.data.dialCode);
46437 this.hasFocus = false;
46439 this.fireEvent('select', this, record, index);
46443 flagEl : function()
46445 var flag = this.el.select('div.flag',true).first();
46452 dialCodeHolderEl : function()
46454 var d = this.el.select('input.dial-code-holder',true).first();
46461 setDialCode : function(v)
46463 this.dialCodeHolder.dom.value = '+'+v;
46466 setFlagClass : function(n)
46468 this.flag.dom.className = 'flag '+n;
46471 getValue : function()
46473 var v = this.inputEl().getValue();
46474 if(this.dialCodeHolder) {
46475 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
46480 setValue : function(v)
46482 var d = this.getDialCode(v);
46484 //invalid dial code
46485 if(v.length == 0 || !d || d.length == 0) {
46487 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
46488 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
46494 this.setFlagClass(this.dialCodeMapping[d].iso2);
46495 this.setDialCode(d);
46496 this.inputEl().dom.value = v.replace('+'+d,'');
46497 this.hiddenEl().dom.value = this.getValue();
46502 getDialCode : function(v)
46506 if (v.length == 0) {
46507 return this.dialCodeHolder.dom.value;
46511 if (v.charAt(0) != "+") {
46514 var numericChars = "";
46515 for (var i = 1; i < v.length; i++) {
46516 var c = v.charAt(i);
46519 if (this.dialCodeMapping[numericChars]) {
46520 dialCode = v.substr(1, i);
46522 if (numericChars.length == 4) {
46532 this.setValue(this.defaultDialCode);
46536 hiddenEl : function()
46538 return this.el.select('input.hidden-tel-input',true).first();
46541 // after setting val
46542 onKeyUp : function(e){
46543 this.setValue(this.getValue());
46546 onKeyPress : function(e){
46547 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
46554 * @class Roo.bootstrap.form.MoneyField
46555 * @extends Roo.bootstrap.form.ComboBox
46556 * Bootstrap MoneyField class
46559 * Create a new MoneyField.
46560 * @param {Object} config Configuration options
46563 Roo.bootstrap.form.MoneyField = function(config) {
46565 Roo.bootstrap.form.MoneyField.superclass.constructor.call(this, config);
46569 Roo.extend(Roo.bootstrap.form.MoneyField, Roo.bootstrap.form.ComboBox, {
46572 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
46574 allowDecimals : true,
46576 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
46578 decimalSeparator : ".",
46580 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
46582 decimalPrecision : 0,
46584 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
46586 allowNegative : true,
46588 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
46592 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
46594 minValue : Number.NEGATIVE_INFINITY,
46596 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
46598 maxValue : Number.MAX_VALUE,
46600 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
46602 minText : "The minimum value for this field is {0}",
46604 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
46606 maxText : "The maximum value for this field is {0}",
46608 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
46609 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
46611 nanText : "{0} is not a valid number",
46613 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
46617 * @cfg {String} defaults currency of the MoneyField
46618 * value should be in lkey
46620 defaultCurrency : false,
46622 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
46624 thousandsDelimiter : false,
46626 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
46635 * @cfg {Roo.data.Store} store Store to lookup currency??
46639 getAutoCreate : function()
46641 var align = this.labelAlign || this.parentLabelAlign();
46653 cls : 'form-control roo-money-amount-input',
46654 autocomplete: 'new-password'
46657 var hiddenInput = {
46661 cls: 'hidden-number-input'
46664 if(this.max_length) {
46665 input.maxlength = this.max_length;
46669 hiddenInput.name = this.name;
46672 if (this.disabled) {
46673 input.disabled = true;
46676 var clg = 12 - this.inputlg;
46677 var cmd = 12 - this.inputmd;
46678 var csm = 12 - this.inputsm;
46679 var cxs = 12 - this.inputxs;
46683 cls : 'row roo-money-field',
46687 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
46691 cls: 'roo-select2-container input-group',
46695 cls : 'form-control roo-money-currency-input',
46696 autocomplete: 'new-password',
46698 name : this.currencyName
46702 cls : 'input-group-addon',
46716 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
46720 cls: this.hasFeedback ? 'has-feedback' : '',
46731 if (this.fieldLabel.length) {
46734 tooltip: 'This field is required'
46740 cls: 'control-label',
46746 html: this.fieldLabel
46749 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
46755 if(this.indicatorpos == 'right') {
46756 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
46763 if(align == 'left') {
46771 if(this.labelWidth > 12){
46772 label.style = "width: " + this.labelWidth + 'px';
46774 if(this.labelWidth < 13 && this.labelmd == 0){
46775 this.labelmd = this.labelWidth;
46777 if(this.labellg > 0){
46778 label.cls += ' col-lg-' + this.labellg;
46779 input.cls += ' col-lg-' + (12 - this.labellg);
46781 if(this.labelmd > 0){
46782 label.cls += ' col-md-' + this.labelmd;
46783 container.cls += ' col-md-' + (12 - this.labelmd);
46785 if(this.labelsm > 0){
46786 label.cls += ' col-sm-' + this.labelsm;
46787 container.cls += ' col-sm-' + (12 - this.labelsm);
46789 if(this.labelxs > 0){
46790 label.cls += ' col-xs-' + this.labelxs;
46791 container.cls += ' col-xs-' + (12 - this.labelxs);
46802 var settings = this;
46804 ['xs','sm','md','lg'].map(function(size){
46805 if (settings[size]) {
46806 cfg.cls += ' col-' + size + '-' + settings[size];
46813 initEvents : function()
46815 this.indicator = this.indicatorEl();
46817 this.initCurrencyEvent();
46819 this.initNumberEvent();
46822 initCurrencyEvent : function()
46825 throw "can not find store for combo";
46828 this.store = Roo.factory(this.store, Roo.data);
46829 this.store.parent = this;
46833 this.triggerEl = this.el.select('.input-group-addon', true).first();
46835 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
46840 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
46841 _this.list.setWidth(lw);
46844 this.list.on('mouseover', this.onViewOver, this);
46845 this.list.on('mousemove', this.onViewMove, this);
46846 this.list.on('scroll', this.onViewScroll, this);
46849 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
46852 this.view = new Roo.View(this.list, this.tpl, {
46853 singleSelect:true, store: this.store, selectedClass: this.selectedClass
46856 this.view.on('click', this.onViewClick, this);
46858 this.store.on('beforeload', this.onBeforeLoad, this);
46859 this.store.on('load', this.onLoad, this);
46860 this.store.on('loadexception', this.onLoadException, this);
46862 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
46863 "up" : function(e){
46864 this.inKeyMode = true;
46868 "down" : function(e){
46869 if(!this.isExpanded()){
46870 this.onTriggerClick();
46872 this.inKeyMode = true;
46877 "enter" : function(e){
46880 if(this.fireEvent("specialkey", this, e)){
46881 this.onViewClick(false);
46887 "esc" : function(e){
46891 "tab" : function(e){
46894 if(this.fireEvent("specialkey", this, e)){
46895 this.onViewClick(false);
46903 doRelay : function(foo, bar, hname){
46904 if(hname == 'down' || this.scope.isExpanded()){
46905 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46913 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
46917 initNumberEvent : function(e)
46919 this.inputEl().on("keydown" , this.fireKey, this);
46920 this.inputEl().on("focus", this.onFocus, this);
46921 this.inputEl().on("blur", this.onBlur, this);
46923 this.inputEl().relayEvent('keyup', this);
46925 if(this.indicator){
46926 this.indicator.addClass('invisible');
46929 this.originalValue = this.getValue();
46931 if(this.validationEvent == 'keyup'){
46932 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
46933 this.inputEl().on('keyup', this.filterValidation, this);
46935 else if(this.validationEvent !== false){
46936 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
46939 if(this.selectOnFocus){
46940 this.on("focus", this.preFocus, this);
46943 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
46944 this.inputEl().on("keypress", this.filterKeys, this);
46946 this.inputEl().relayEvent('keypress', this);
46949 var allowed = "0123456789";
46951 if(this.allowDecimals){
46952 allowed += this.decimalSeparator;
46955 if(this.allowNegative){
46959 if(this.thousandsDelimiter) {
46963 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
46965 var keyPress = function(e){
46967 var k = e.getKey();
46969 var c = e.getCharCode();
46972 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
46973 allowed.indexOf(String.fromCharCode(c)) === -1
46979 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
46983 if(allowed.indexOf(String.fromCharCode(c)) === -1){
46988 this.inputEl().on("keypress", keyPress, this);
46992 onTriggerClick : function(e)
46999 this.loadNext = false;
47001 if(this.isExpanded()){
47006 this.hasFocus = true;
47008 if(this.triggerAction == 'all') {
47009 this.doQuery(this.allQuery, true);
47013 this.doQuery(this.getRawValue());
47016 getCurrency : function()
47018 var v = this.currencyEl().getValue();
47023 restrictHeight : function()
47025 this.list.alignTo(this.currencyEl(), this.listAlign);
47026 this.list.alignTo(this.currencyEl(), this.listAlign);
47029 onViewClick : function(view, doFocus, el, e)
47031 var index = this.view.getSelectedIndexes()[0];
47033 var r = this.store.getAt(index);
47036 this.onSelect(r, index);
47040 onSelect : function(record, index){
47042 if(this.fireEvent('beforeselect', this, record, index) !== false){
47044 this.setFromCurrencyData(index > -1 ? record.data : false);
47048 this.fireEvent('select', this, record, index);
47052 setFromCurrencyData : function(o)
47056 this.lastCurrency = o;
47058 if (this.currencyField) {
47059 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
47061 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
47064 this.lastSelectionText = currency;
47066 //setting default currency
47067 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
47068 this.setCurrency(this.defaultCurrency);
47072 this.setCurrency(currency);
47075 setFromData : function(o)
47079 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
47081 this.setFromCurrencyData(c);
47086 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
47088 Roo.log('no value set for '+ (this.name ? this.name : this.id));
47091 this.setValue(value);
47095 setCurrency : function(v)
47097 this.currencyValue = v;
47100 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
47105 setValue : function(v)
47107 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
47113 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
47115 this.inputEl().dom.value = (v == '') ? '' :
47116 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
47118 if(!this.allowZero && v === '0') {
47119 this.hiddenEl().dom.value = '';
47120 this.inputEl().dom.value = '';
47127 getRawValue : function()
47129 var v = this.inputEl().getValue();
47134 getValue : function()
47136 return this.fixPrecision(this.parseValue(this.getRawValue()));
47139 parseValue : function(value)
47141 if(this.thousandsDelimiter) {
47143 r = new RegExp(",", "g");
47144 value = value.replace(r, "");
47147 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
47148 return isNaN(value) ? '' : value;
47152 fixPrecision : function(value)
47154 if(this.thousandsDelimiter) {
47156 r = new RegExp(",", "g");
47157 value = value.replace(r, "");
47160 var nan = isNaN(value);
47162 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
47163 return nan ? '' : value;
47165 return parseFloat(value).toFixed(this.decimalPrecision);
47168 decimalPrecisionFcn : function(v)
47170 return Math.floor(v);
47173 validateValue : function(value)
47175 if(!Roo.bootstrap.form.MoneyField.superclass.validateValue.call(this, value)){
47179 var num = this.parseValue(value);
47182 this.markInvalid(String.format(this.nanText, value));
47186 if(num < this.minValue){
47187 this.markInvalid(String.format(this.minText, this.minValue));
47191 if(num > this.maxValue){
47192 this.markInvalid(String.format(this.maxText, this.maxValue));
47199 validate : function()
47201 if(this.disabled || this.allowBlank){
47206 var currency = this.getCurrency();
47208 if(this.validateValue(this.getRawValue()) && currency.length){
47213 this.markInvalid();
47217 getName: function()
47222 beforeBlur : function()
47228 var v = this.parseValue(this.getRawValue());
47235 onBlur : function()
47239 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
47240 //this.el.removeClass(this.focusClass);
47243 this.hasFocus = false;
47245 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
47249 var v = this.getValue();
47251 if(String(v) !== String(this.startValue)){
47252 this.fireEvent('change', this, v, this.startValue);
47255 this.fireEvent("blur", this);
47258 inputEl : function()
47260 return this.el.select('.roo-money-amount-input', true).first();
47263 currencyEl : function()
47265 return this.el.select('.roo-money-currency-input', true).first();
47268 hiddenEl : function()
47270 return this.el.select('input.hidden-number-input',true).first();
47274 * @class Roo.bootstrap.BezierSignature
47275 * @extends Roo.bootstrap.Component
47276 * Bootstrap BezierSignature class
47277 * This script refer to:
47278 * Title: Signature Pad
47280 * Availability: https://github.com/szimek/signature_pad
47283 * Create a new BezierSignature
47284 * @param {Object} config The config object
47287 Roo.bootstrap.BezierSignature = function(config){
47288 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
47294 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
47301 mouse_btn_down: true,
47304 * @cfg {int} canvas height
47306 canvas_height: '200px',
47309 * @cfg {float|function} Radius of a single dot.
47314 * @cfg {float} Minimum width of a line. Defaults to 0.5.
47319 * @cfg {float} Maximum width of a line. Defaults to 2.5.
47324 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
47329 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
47334 * @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.
47336 bg_color: 'rgba(0, 0, 0, 0)',
47339 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
47341 dot_color: 'black',
47344 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
47346 velocity_filter_weight: 0.7,
47349 * @cfg {function} Callback when stroke begin.
47354 * @cfg {function} Callback when stroke end.
47358 getAutoCreate : function()
47360 var cls = 'roo-signature column';
47363 cls += ' ' + this.cls;
47373 for(var i = 0; i < col_sizes.length; i++) {
47374 if(this[col_sizes[i]]) {
47375 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
47385 cls: 'roo-signature-body',
47389 cls: 'roo-signature-body-canvas',
47390 height: this.canvas_height,
47391 width: this.canvas_width
47398 style: 'display: none'
47406 initEvents: function()
47408 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
47410 var canvas = this.canvasEl();
47412 // mouse && touch event swapping...
47413 canvas.dom.style.touchAction = 'none';
47414 canvas.dom.style.msTouchAction = 'none';
47416 this.mouse_btn_down = false;
47417 canvas.on('mousedown', this._handleMouseDown, this);
47418 canvas.on('mousemove', this._handleMouseMove, this);
47419 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
47421 if (window.PointerEvent) {
47422 canvas.on('pointerdown', this._handleMouseDown, this);
47423 canvas.on('pointermove', this._handleMouseMove, this);
47424 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
47427 if ('ontouchstart' in window) {
47428 canvas.on('touchstart', this._handleTouchStart, this);
47429 canvas.on('touchmove', this._handleTouchMove, this);
47430 canvas.on('touchend', this._handleTouchEnd, this);
47433 Roo.EventManager.onWindowResize(this.resize, this, true);
47435 // file input event
47436 this.fileEl().on('change', this.uploadImage, this);
47443 resize: function(){
47445 var canvas = this.canvasEl().dom;
47446 var ctx = this.canvasElCtx();
47447 var img_data = false;
47449 if(canvas.width > 0) {
47450 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
47452 // setting canvas width will clean img data
47455 var style = window.getComputedStyle ?
47456 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
47458 var padding_left = parseInt(style.paddingLeft) || 0;
47459 var padding_right = parseInt(style.paddingRight) || 0;
47461 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
47464 ctx.putImageData(img_data, 0, 0);
47468 _handleMouseDown: function(e)
47470 if (e.browserEvent.which === 1) {
47471 this.mouse_btn_down = true;
47472 this.strokeBegin(e);
47476 _handleMouseMove: function (e)
47478 if (this.mouse_btn_down) {
47479 this.strokeMoveUpdate(e);
47483 _handleMouseUp: function (e)
47485 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
47486 this.mouse_btn_down = false;
47491 _handleTouchStart: function (e) {
47493 e.preventDefault();
47494 if (e.browserEvent.targetTouches.length === 1) {
47495 // var touch = e.browserEvent.changedTouches[0];
47496 // this.strokeBegin(touch);
47498 this.strokeBegin(e); // assume e catching the correct xy...
47502 _handleTouchMove: function (e) {
47503 e.preventDefault();
47504 // var touch = event.targetTouches[0];
47505 // _this._strokeMoveUpdate(touch);
47506 this.strokeMoveUpdate(e);
47509 _handleTouchEnd: function (e) {
47510 var wasCanvasTouched = e.target === this.canvasEl().dom;
47511 if (wasCanvasTouched) {
47512 e.preventDefault();
47513 // var touch = event.changedTouches[0];
47514 // _this._strokeEnd(touch);
47519 reset: function () {
47520 this._lastPoints = [];
47521 this._lastVelocity = 0;
47522 this._lastWidth = (this.min_width + this.max_width) / 2;
47523 this.canvasElCtx().fillStyle = this.dot_color;
47526 strokeMoveUpdate: function(e)
47528 this.strokeUpdate(e);
47530 if (this.throttle) {
47531 this.throttleStroke(this.strokeUpdate, this.throttle);
47534 this.strokeUpdate(e);
47538 strokeBegin: function(e)
47540 var newPointGroup = {
47541 color: this.dot_color,
47545 if (typeof this.onBegin === 'function') {
47549 this.curve_data.push(newPointGroup);
47551 this.strokeUpdate(e);
47554 strokeUpdate: function(e)
47556 var rect = this.canvasEl().dom.getBoundingClientRect();
47557 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
47558 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
47559 var lastPoints = lastPointGroup.points;
47560 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
47561 var isLastPointTooClose = lastPoint
47562 ? point.distanceTo(lastPoint) <= this.min_distance
47564 var color = lastPointGroup.color;
47565 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
47566 var curve = this.addPoint(point);
47568 this.drawDot({color: color, point: point});
47571 this.drawCurve({color: color, curve: curve});
47581 strokeEnd: function(e)
47583 this.strokeUpdate(e);
47584 if (typeof this.onEnd === 'function') {
47589 addPoint: function (point) {
47590 var _lastPoints = this._lastPoints;
47591 _lastPoints.push(point);
47592 if (_lastPoints.length > 2) {
47593 if (_lastPoints.length === 3) {
47594 _lastPoints.unshift(_lastPoints[0]);
47596 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
47597 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
47598 _lastPoints.shift();
47604 calculateCurveWidths: function (startPoint, endPoint) {
47605 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
47606 (1 - this.velocity_filter_weight) * this._lastVelocity;
47608 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
47611 start: this._lastWidth
47614 this._lastVelocity = velocity;
47615 this._lastWidth = newWidth;
47619 drawDot: function (_a) {
47620 var color = _a.color, point = _a.point;
47621 var ctx = this.canvasElCtx();
47622 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
47624 this.drawCurveSegment(point.x, point.y, width);
47626 ctx.fillStyle = color;
47630 drawCurve: function (_a) {
47631 var color = _a.color, curve = _a.curve;
47632 var ctx = this.canvasElCtx();
47633 var widthDelta = curve.endWidth - curve.startWidth;
47634 var drawSteps = Math.floor(curve.length()) * 2;
47636 ctx.fillStyle = color;
47637 for (var i = 0; i < drawSteps; i += 1) {
47638 var t = i / drawSteps;
47644 var x = uuu * curve.startPoint.x;
47645 x += 3 * uu * t * curve.control1.x;
47646 x += 3 * u * tt * curve.control2.x;
47647 x += ttt * curve.endPoint.x;
47648 var y = uuu * curve.startPoint.y;
47649 y += 3 * uu * t * curve.control1.y;
47650 y += 3 * u * tt * curve.control2.y;
47651 y += ttt * curve.endPoint.y;
47652 var width = curve.startWidth + ttt * widthDelta;
47653 this.drawCurveSegment(x, y, width);
47659 drawCurveSegment: function (x, y, width) {
47660 var ctx = this.canvasElCtx();
47662 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
47663 this.is_empty = false;
47668 var ctx = this.canvasElCtx();
47669 var canvas = this.canvasEl().dom;
47670 ctx.fillStyle = this.bg_color;
47671 ctx.clearRect(0, 0, canvas.width, canvas.height);
47672 ctx.fillRect(0, 0, canvas.width, canvas.height);
47673 this.curve_data = [];
47675 this.is_empty = true;
47680 return this.el.select('input',true).first();
47683 canvasEl: function()
47685 return this.el.select('canvas',true).first();
47688 canvasElCtx: function()
47690 return this.el.select('canvas',true).first().dom.getContext('2d');
47693 getImage: function(type)
47695 if(this.is_empty) {
47700 return this.canvasEl().dom.toDataURL('image/'+type, 1);
47703 drawFromImage: function(img_src)
47705 var img = new Image();
47707 img.onload = function(){
47708 this.canvasElCtx().drawImage(img, 0, 0);
47713 this.is_empty = false;
47716 selectImage: function()
47718 this.fileEl().dom.click();
47721 uploadImage: function(e)
47723 var reader = new FileReader();
47725 reader.onload = function(e){
47726 var img = new Image();
47727 img.onload = function(){
47729 this.canvasElCtx().drawImage(img, 0, 0);
47731 img.src = e.target.result;
47734 reader.readAsDataURL(e.target.files[0]);
47737 // Bezier Point Constructor
47738 Point: (function () {
47739 function Point(x, y, time) {
47742 this.time = time || Date.now();
47744 Point.prototype.distanceTo = function (start) {
47745 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
47747 Point.prototype.equals = function (other) {
47748 return this.x === other.x && this.y === other.y && this.time === other.time;
47750 Point.prototype.velocityFrom = function (start) {
47751 return this.time !== start.time
47752 ? this.distanceTo(start) / (this.time - start.time)
47759 // Bezier Constructor
47760 Bezier: (function () {
47761 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
47762 this.startPoint = startPoint;
47763 this.control2 = control2;
47764 this.control1 = control1;
47765 this.endPoint = endPoint;
47766 this.startWidth = startWidth;
47767 this.endWidth = endWidth;
47769 Bezier.fromPoints = function (points, widths, scope) {
47770 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
47771 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
47772 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
47774 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
47775 var dx1 = s1.x - s2.x;
47776 var dy1 = s1.y - s2.y;
47777 var dx2 = s2.x - s3.x;
47778 var dy2 = s2.y - s3.y;
47779 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
47780 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
47781 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
47782 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
47783 var dxm = m1.x - m2.x;
47784 var dym = m1.y - m2.y;
47785 var k = l2 / (l1 + l2);
47786 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
47787 var tx = s2.x - cm.x;
47788 var ty = s2.y - cm.y;
47790 c1: new scope.Point(m1.x + tx, m1.y + ty),
47791 c2: new scope.Point(m2.x + tx, m2.y + ty)
47794 Bezier.prototype.length = function () {
47799 for (var i = 0; i <= steps; i += 1) {
47801 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
47802 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
47804 var xdiff = cx - px;
47805 var ydiff = cy - py;
47806 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
47813 Bezier.prototype.point = function (t, start, c1, c2, end) {
47814 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
47815 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
47816 + (3.0 * c2 * (1.0 - t) * t * t)
47817 + (end * t * t * t);
47822 throttleStroke: function(fn, wait) {
47823 if (wait === void 0) { wait = 250; }
47825 var timeout = null;
47829 var later = function () {
47830 previous = Date.now();
47832 result = fn.apply(storedContext, storedArgs);
47834 storedContext = null;
47838 return function wrapper() {
47840 for (var _i = 0; _i < arguments.length; _i++) {
47841 args[_i] = arguments[_i];
47843 var now = Date.now();
47844 var remaining = wait - (now - previous);
47845 storedContext = this;
47847 if (remaining <= 0 || remaining > wait) {
47849 clearTimeout(timeout);
47853 result = fn.apply(storedContext, storedArgs);
47855 storedContext = null;
47859 else if (!timeout) {
47860 timeout = window.setTimeout(later, remaining);
47870 // old names for form elements
47871 Roo.bootstrap.Form = Roo.bootstrap.form.Form;
47872 Roo.bootstrap.Input = Roo.bootstrap.form.Input;
47873 Roo.bootstrap.TextArea = Roo.bootstrap.form.TextArea;
47874 Roo.bootstrap.TriggerField = Roo.bootstrap.form.TriggerField;
47875 Roo.bootstrap.ComboBox = Roo.bootstrap.form.ComboBox;
47876 Roo.bootstrap.DateField = Roo.bootstrap.form.DateField;
47877 Roo.bootstrap.TimeField = Roo.bootstrap.form.TimeField;
47878 Roo.bootstrap.MonthField = Roo.bootstrap.form.MonthField;
47879 Roo.bootstrap.CheckBox = Roo.bootstrap.form.CheckBox;
47880 Roo.bootstrap.Radio = Roo.bootstrap.form.Radio;
47881 Roo.bootstrap.RadioSet = Roo.bootstrap.form.RadioSet;
47882 Roo.bootstrap.SecurePass = Roo.bootstrap.form.SecurePass;
47883 Roo.bootstrap.FieldLabel = Roo.bootstrap.form.FieldLabel;
47884 Roo.bootstrap.DateSplitField= Roo.bootstrap.form.DateSplitField;
47885 Roo.bootstrap.NumberField = Roo.bootstrap.form.NumberField;
47886 Roo.bootstrap.PhoneInput = Roo.bootstrap.form.PhoneInput;
47887 Roo.bootstrap.PhoneInputData= Roo.bootstrap.form.PhoneInputData;
47888 Roo.bootstrap.MoneyField = Roo.bootstrap.form.MoneyField;
47889 Roo.bootstrap.HtmlEditor = Roo.bootstrap.form.HtmlEditor;
47890 Roo.bootstrap.HtmlEditor.ToolbarStandard = Roo.bootstrap.form.HtmlEditorToolbarStandard;
47891 Roo.bootstrap.Markdown = Roo.bootstrap.form.Markdown;
47892 Roo.bootstrap.CardUploader = Roo.bootstrap.form.CardUploader;// depricated.
47893 Roo.bootstrap.Navbar = Roo.bootstrap.nav.Bar;
47894 Roo.bootstrap.NavGroup = Roo.bootstrap.nav.Group;
47895 Roo.bootstrap.NavHeaderbar = Roo.bootstrap.nav.Headerbar;
47896 Roo.bootstrap.NavItem = Roo.bootstrap.nav.Item;
47898 Roo.bootstrap.NavProgressBar = Roo.bootstrap.nav.ProgressBar;
47899 Roo.bootstrap.NavProgressBarItem = Roo.bootstrap.nav.ProgressBarItem;
47901 Roo.bootstrap.NavSidebar = Roo.bootstrap.nav.Sidebar;
47902 Roo.bootstrap.NavSidebarItem = Roo.bootstrap.nav.SidebarItem;
47904 Roo.bootstrap.NavSimplebar = Roo.bootstrap.nav.Simplebar;// deprciated
47905 Roo.bootstrap.Menu = Roo.bootstrap.menu.Menu;
47906 Roo.bootstrap.MenuItem = Roo.bootstrap.menu.Item;
47907 Roo.bootstrap.MenuSeparator = Roo.bootstrap.menu.Separator