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;
19 * Ext JS Library 1.1.1
20 * Copyright(c) 2006-2007, Ext JS, LLC.
22 * Originally Released Under LGPL - original licence link has changed is not relivant.
25 * <script type="text/javascript">
31 * Simple class that can provide a shadow effect for any element. Note that the element MUST be absolutely positioned,
32 * and the shadow does not provide any shimming. This should be used only in simple cases -- for more advanced
33 * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
36 * @param {Object} config The config object
38 Roo.Shadow = function(config){
39 Roo.apply(this, config);
40 if(typeof this.mode != "string"){
41 this.mode = this.defaultMode;
43 var o = this.offset, a = {h: 0};
44 var rad = Math.floor(this.offset/2);
45 switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
51 a.l -= this.offset + rad;
52 a.t -= this.offset + rad;
63 a.l -= (this.offset - rad);
64 a.t -= this.offset + rad;
66 a.w -= (this.offset - rad)*2;
77 a.l -= (this.offset - rad);
78 a.t -= (this.offset - rad);
80 a.w -= (this.offset + rad + 1);
81 a.h -= (this.offset + rad);
90 Roo.Shadow.prototype = {
93 * The shadow display mode. Supports the following options:<br />
94 * sides: Shadow displays on both sides and bottom only<br />
95 * frame: Shadow displays equally on all four sides<br />
96 * drop: Traditional bottom-right drop shadow (default)
99 * @cfg {String} offset
100 * The number of pixels to offset the shadow from the element (defaults to 4)
108 * Displays the shadow under the target element
109 * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111 show : function(target){
112 target = Roo.get(target);
114 this.el = Roo.Shadow.Pool.pull();
115 if(this.el.dom.nextSibling != target.dom){
116 this.el.insertBefore(target);
119 this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121 this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
124 target.getLeft(true),
129 this.el.dom.style.display = "block";
133 * Returns true if the shadow is visible, else false
135 isVisible : function(){
136 return this.el ? true : false;
140 * Direct alignment when values are already available. Show must be called at least once before
141 * calling this method to ensure it is initialized.
142 * @param {Number} left The target element left position
143 * @param {Number} top The target element top position
144 * @param {Number} width The target element width
145 * @param {Number} height The target element height
147 realign : function(l, t, w, h){
151 var a = this.adjusts, d = this.el.dom, s = d.style;
153 s.left = (l+a.l)+"px";
154 s.top = (t+a.t)+"px";
155 var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157 if(s.width != sws || s.height != shs){
161 var cn = d.childNodes;
162 var sww = Math.max(0, (sw-12))+"px";
163 cn[0].childNodes[1].style.width = sww;
164 cn[1].childNodes[1].style.width = sww;
165 cn[2].childNodes[1].style.width = sww;
166 cn[1].style.height = Math.max(0, (sh-12))+"px";
176 this.el.dom.style.display = "none";
177 Roo.Shadow.Pool.push(this.el);
183 * Adjust the z-index of this shadow
184 * @param {Number} zindex The new z-index
186 setZIndex : function(z){
189 this.el.setStyle("z-index", z);
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
197 var markup = Roo.isIE ?
198 '<div class="x-ie-shadow"></div>' :
199 '<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>';
204 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205 sh.autoBoxAdjust = false;
217 * base class for bootstrap elements.
221 Roo.bootstrap = Roo.bootstrap || {};
223 * @class Roo.bootstrap.Component
224 * @extends Roo.Component
225 * Bootstrap Component base class
226 * @cfg {String} cls css class
227 * @cfg {String} style any extra css
228 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
230 * @cfg {string} dataId cutomer id
231 * @cfg {string} name Specifies name attribute
232 * @cfg {string} tooltip Text for the tooltip
233 * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar - getHeaderChildContainer)
234 * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
237 * Do not use directly - it does not do anything..
238 * @param {Object} config The config object
243 Roo.bootstrap.Component = function(config){
244 Roo.bootstrap.Component.superclass.constructor.call(this, config);
248 * @event childrenrendered
249 * Fires when the children have been rendered..
250 * @param {Roo.bootstrap.Component} this
252 "childrenrendered" : true
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
264 allowDomMove : false, // to stop relocations in parent onRender...
274 * Initialize Events for the element
276 initEvents : function() { },
282 can_build_overlaid : true,
284 container_method : false,
291 // returns the parent component..
292 return Roo.ComponentMgr.get(this.parentId)
298 onRender : function(ct, position)
300 // Roo.log("Call onRender: " + this.xtype);
302 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
305 if (this.el.attr('xtype')) {
306 this.el.attr('xtypex', this.el.attr('xtype'));
307 this.el.dom.removeAttribute('xtype');
317 var cfg = Roo.apply({}, this.getAutoCreate());
319 cfg.id = this.id || Roo.id();
321 // fill in the extra attributes
322 if (this.xattr && typeof(this.xattr) =='object') {
323 for (var i in this.xattr) {
324 cfg[i] = this.xattr[i];
329 cfg.dataId = this.dataId;
333 cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
336 if (this.style) { // fixme needs to support more complex style data.
337 cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
341 cfg.name = this.name;
344 this.el = ct.createChild(cfg, position);
347 this.tooltipEl().attr('tooltip', this.tooltip);
350 if(this.tabIndex !== undefined){
351 this.el.dom.setAttribute('tabIndex', this.tabIndex);
358 * Fetch the element to add children to
359 * @return {Roo.Element} defaults to this.el
361 getChildContainer : function()
365 getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367 return Roo.get(document.body);
371 * Fetch the element to display the tooltip on.
372 * @return {Roo.Element} defaults to this.el
374 tooltipEl : function()
379 addxtype : function(tree,cntr)
383 cn = Roo.factory(tree);
384 //Roo.log(['addxtype', cn]);
386 cn.parentType = this.xtype; //??
387 cn.parentId = this.id;
389 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390 if (typeof(cn.container_method) == 'string') {
391 cntr = cn.container_method;
395 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
397 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
399 var build_from_html = Roo.XComponent.build_from_html;
401 var is_body = (tree.xtype == 'Body') ;
403 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405 var self_cntr_el = Roo.get(this[cntr](false));
407 // do not try and build conditional elements
408 if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
412 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414 return this.addxtypeChild(tree,cntr, is_body);
417 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
420 return this.addxtypeChild(Roo.apply({}, tree),cntr);
423 Roo.log('skipping render');
429 if (!build_from_html) {
433 // this i think handles overlaying multiple children of the same type
434 // with the sam eelement.. - which might be buggy..
436 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
442 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
446 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
453 addxtypeChild : function (tree, cntr, is_body)
455 Roo.debug && Roo.log('addxtypeChild:' + cntr);
457 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
460 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461 (typeof(tree['flexy:foreach']) != 'undefined');
465 skip_children = false;
466 // render the element if it's not BODY.
469 // if parent was disabled, then do not try and create the children..
470 if(!this[cntr](true)){
475 cn = Roo.factory(tree);
477 cn.parentType = this.xtype; //??
478 cn.parentId = this.id;
480 var build_from_html = Roo.XComponent.build_from_html;
483 // does the container contain child eleemnts with 'xtype' attributes.
484 // that match this xtype..
485 // note - when we render we create these as well..
486 // so we should check to see if body has xtype set.
487 if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489 var self_cntr_el = Roo.get(this[cntr](false));
490 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492 //Roo.log(Roo.XComponent.build_from_html);
493 //Roo.log("got echild:");
496 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497 // and are not displayed -this causes this to use up the wrong element when matching.
498 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
501 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
508 //echild.dom.removeAttribute('xtype');
510 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511 Roo.debug && Roo.log(self_cntr_el);
512 Roo.debug && Roo.log(echild);
513 Roo.debug && Roo.log(cn);
519 // if object has flexy:if - then it may or may not be rendered.
520 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
521 // skip a flexy if element.
522 Roo.debug && Roo.log('skipping render');
523 Roo.debug && Roo.log(tree);
525 Roo.debug && Roo.log('skipping all children');
526 skip_children = true;
531 // actually if flexy:foreach is found, we really want to create
532 // multiple copies here...
534 //Roo.log(this[cntr]());
535 // some elements do not have render methods.. like the layouts...
537 if(this[cntr](true) === false){
542 cn.render && cn.render(this[cntr](true));
545 // then add the element..
552 if (typeof (tree.menu) != 'undefined') {
553 tree.menu.parentType = cn.xtype;
554 tree.menu.triggerEl = cn.el;
555 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
559 if (!tree.items || !tree.items.length) {
561 //Roo.log(["no children", this]);
566 var items = tree.items;
569 //Roo.log(items.length);
571 if (!skip_children) {
572 for(var i =0;i < items.length;i++) {
573 // Roo.log(['add child', items[i]]);
574 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
580 //Roo.log("fire childrenrendered");
582 cn.fireEvent('childrenrendered', this);
588 * Set the element that will be used to show or hide
590 setVisibilityEl : function(el)
592 this.visibilityEl = el;
596 * Get the element that will be used to show or hide
598 getVisibilityEl : function()
600 if (typeof(this.visibilityEl) == 'object') {
601 return this.visibilityEl;
604 if (typeof(this.visibilityEl) == 'string') {
605 return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
612 * Show a component - removes 'hidden' class
616 if(!this.getVisibilityEl()){
620 this.getVisibilityEl().removeClass(['hidden','d-none']);
622 this.fireEvent('show', this);
627 * Hide a component - adds 'hidden' class
631 if(!this.getVisibilityEl()){
635 this.getVisibilityEl().addClass(['hidden','d-none']);
637 this.fireEvent('hide', this);
650 * @class Roo.bootstrap.Element
651 * @extends Roo.bootstrap.Component
652 * Bootstrap Element class
653 * @cfg {String} html contents of the element
654 * @cfg {String} tag tag of the element
655 * @cfg {String} cls class of the element
656 * @cfg {Boolean} preventDefault (true|false) default false
657 * @cfg {Boolean} clickable (true|false) default false
658 * @cfg {String} role default blank - set to button to force cursor pointer
662 * Create a new Element
663 * @param {Object} config The config object
666 Roo.bootstrap.Element = function(config){
667 Roo.bootstrap.Element.superclass.constructor.call(this, config);
673 * When a element is chick
674 * @param {Roo.bootstrap.Element} this
675 * @param {Roo.EventObject} e
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
688 preventDefault: false,
693 getAutoCreate : function(){
697 // cls: this.cls, double assign in parent class Component.js :: onRender
700 if (this.role !== false) {
701 cfg.role = this.role;
707 initEvents: function()
709 Roo.bootstrap.Element.superclass.initEvents.call(this);
712 this.el.on('click', this.onClick, this);
718 onClick : function(e)
720 if(this.preventDefault){
724 this.fireEvent('click', this, e); // why was this double click before?
732 getValue : function()
734 return this.el.dom.innerHTML;
737 setValue : function(value)
739 this.el.dom.innerHTML = value;
754 * @class Roo.bootstrap.DropTarget
755 * @extends Roo.bootstrap.Element
756 * Bootstrap DropTarget class
758 * @cfg {string} name dropable name
761 * Create a new Dropable Area
762 * @param {Object} config The config object
765 Roo.bootstrap.DropTarget = function(config){
766 Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
772 * When a element is chick
773 * @param {Roo.bootstrap.Element} this
774 * @param {Roo.EventObject} e
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element, {
783 getAutoCreate : function(){
788 initEvents: function()
790 Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791 this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
794 drop : this.dragDrop.createDelegate(this),
795 enter : this.dragEnter.createDelegate(this),
796 out : this.dragOut.createDelegate(this),
797 over : this.dragOver.createDelegate(this)
801 this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
804 dragDrop : function(source,e,data)
806 // user has to decide how to impliment this.
809 //this.fireEvent('drop', this, source, e ,data);
813 dragEnter : function(n, dd, e, data)
815 // probably want to resize the element to match the dropped element..
817 this.originalSize = this.el.getSize();
818 this.el.setSize( n.el.getSize());
819 this.dropZone.DDM.refreshCache(this.name);
820 Roo.log([n, dd, e, data]);
823 dragOut : function(value)
825 // resize back to normal
827 this.el.setSize(this.originalSize);
828 this.dropZone.resetConstraints();
831 dragOver : function()
848 * @class Roo.bootstrap.Body
849 * @extends Roo.bootstrap.Component
850 * Bootstrap Body class
854 * @param {Object} config The config object
857 Roo.bootstrap.Body = function(config){
859 config = config || {};
861 Roo.bootstrap.Body.superclass.constructor.call(this, config);
862 this.el = Roo.get(config.el ? config.el : document.body );
863 if (this.cls && this.cls.length) {
864 Roo.get(document.body).addClass(this.cls);
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
870 is_body : true,// just to make sure it's constructed?
875 onRender : function(ct, position)
877 /* Roo.log("Roo.bootstrap.Body - onRender");
878 if (this.cls && this.cls.length) {
879 Roo.get(document.body).addClass(this.cls);
898 * @class Roo.bootstrap.ButtonGroup
899 * @extends Roo.bootstrap.Component
900 * Bootstrap ButtonGroup class
901 * @cfg {String} size lg | sm | xs (default empty normal)
902 * @cfg {String} align vertical | justified (default none)
903 * @cfg {String} direction up | down (default down)
904 * @cfg {Boolean} toolbar false | true
905 * @cfg {Boolean} btn true | false
910 * @param {Object} config The config object
913 Roo.bootstrap.ButtonGroup = function(config){
914 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
925 getAutoCreate : function(){
931 cfg.html = this.html || cfg.html;
942 if (['vertical','justified'].indexOf(this.align)!==-1) {
943 cfg.cls = 'btn-group-' + this.align;
945 if (this.align == 'justified') {
946 console.log(this.items);
950 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951 cfg.cls += ' btn-group-' + this.size;
954 if (this.direction == 'up') {
955 cfg.cls += ' dropup' ;
961 * Add a button to the group (similar to NavItem API.)
963 addItem : function(cfg)
965 var cn = new Roo.bootstrap.Button(cfg);
967 cn.parentId = this.id;
968 cn.onRender(this.el, null);
982 * @class Roo.bootstrap.Button
983 * @extends Roo.bootstrap.Component
984 * Bootstrap Button class
985 * @cfg {String} html The button content
986 * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987 * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988 * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989 * @cfg {String} size (lg|sm|xs)
990 * @cfg {String} tag (a|input|submit)
991 * @cfg {String} href empty or href
992 * @cfg {Boolean} disabled default false;
993 * @cfg {Boolean} isClose default false;
994 * @cfg {String} glyphicon depricated - use fa
995 * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996 * @cfg {String} badge text for badge
997 * @cfg {String} theme (default|glow)
998 * @cfg {Boolean} inverse dark themed version
999 * @cfg {Boolean} toggle is it a slidy toggle button
1000 * @cfg {Boolean} pressed default null - if the button ahs active state
1001 * @cfg {String} ontext text for on slidy toggle state
1002 * @cfg {String} offtext text for off slidy toggle state
1003 * @cfg {Boolean} preventDefault default true (stop click event triggering the URL if it's a link.)
1004 * @cfg {Boolean} removeClass remove the standard class..
1005 * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href.
1006 * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1009 * Create a new button
1010 * @param {Object} config The config object
1014 Roo.bootstrap.Button = function(config){
1015 Roo.bootstrap.Button.superclass.constructor.call(this, config);
1021 * When a button is pressed
1022 * @param {Roo.bootstrap.Button} btn
1023 * @param {Roo.EventObject} e
1028 * When a button is double clicked
1029 * @param {Roo.bootstrap.Button} btn
1030 * @param {Roo.EventObject} e
1035 * After the button has been toggles
1036 * @param {Roo.bootstrap.Button} btn
1037 * @param {Roo.EventObject} e
1038 * @param {boolean} pressed (also available as button.pressed)
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
1065 preventDefault: true,
1074 getAutoCreate : function(){
1082 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084 this.tag = 'button';
1088 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090 if (this.toggle == true) {
1093 cls: 'slider-frame roo-button',
1097 'data-on-text':'ON',
1098 'data-off-text':'OFF',
1099 cls: 'slider-button',
1104 // why are we validating the weights?
1105 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106 cfg.cls += ' ' + this.weight;
1113 cfg.cls += ' close';
1115 cfg["aria-hidden"] = true;
1117 cfg.html = "×";
1123 if (this.theme==='default') {
1124 cfg.cls = 'btn roo-button';
1126 //if (this.parentType != 'Navbar') {
1127 this.weight = this.weight.length ? this.weight : 'default';
1129 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133 cfg.cls += ' btn-' + outline + weight;
1134 if (this.weight == 'default') {
1136 cfg.cls += ' btn-' + this.weight;
1139 } else if (this.theme==='glow') {
1142 cfg.cls = 'btn-glow roo-button';
1144 if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146 cfg.cls += ' ' + this.weight;
1152 this.cls += ' inverse';
1156 if (this.active || this.pressed === true) {
1157 cfg.cls += ' active';
1160 if (this.disabled) {
1161 cfg.disabled = 'disabled';
1165 Roo.log('changing to ul' );
1167 this.glyphicon = 'caret';
1168 if (Roo.bootstrap.version == 4) {
1169 this.fa = 'caret-down';
1174 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176 //gsRoo.log(this.parentType);
1177 if (this.parentType === 'Navbar' && !this.parent().bar) {
1178 Roo.log('changing to li?');
1187 href : this.href || '#'
1190 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
1191 cfg.cls += ' dropdown';
1198 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
1200 if (this.glyphicon) {
1201 cfg.html = ' ' + cfg.html;
1206 cls: 'glyphicon glyphicon-' + this.glyphicon
1211 cfg.html = ' ' + cfg.html;
1216 cls: 'fa fas fa-' + this.fa
1226 // cfg.cls='btn roo-button';
1230 var value = cfg.html;
1235 cls: 'glyphicon glyphicon-' + this.glyphicon,
1242 cls: 'fa fas fa-' + this.fa,
1247 var bw = this.badge_weight.length ? this.badge_weight :
1248 (this.weight.length ? this.weight : 'secondary');
1249 bw = bw == 'default' ? 'secondary' : bw;
1255 cls: 'badge badge-' + bw,
1264 cfg.cls += ' dropdown';
1265 cfg.html = typeof(cfg.html) != 'undefined' ?
1266 cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1269 if (cfg.tag !== 'a' && this.href !== '') {
1270 throw "Tag must be a to set href.";
1271 } else if (this.href.length > 0) {
1272 cfg.href = this.href;
1275 if(this.removeClass){
1280 cfg.target = this.target;
1285 initEvents: function() {
1286 // Roo.log('init events?');
1287 // Roo.log(this.el.dom);
1290 if (typeof (this.menu) != 'undefined') {
1291 this.menu.parentType = this.xtype;
1292 this.menu.triggerEl = this.el;
1293 this.addxtype(Roo.apply({}, this.menu));
1297 if (this.el.hasClass('roo-button')) {
1298 this.el.on('click', this.onClick, this);
1299 this.el.on('dblclick', this.onDblClick, this);
1301 this.el.select('.roo-button').on('click', this.onClick, this);
1302 this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1306 if(this.removeClass){
1307 this.el.on('click', this.onClick, this);
1310 if (this.group === true) {
1311 if (this.pressed === false || this.pressed === true) {
1314 this.pressed = false;
1315 this.setActive(this.pressed);
1320 this.el.enableDisplayMode();
1323 onClick : function(e)
1325 if (this.disabled) {
1329 Roo.log('button on click ');
1330 if(this.preventDefault){
1339 this.setActive(true);
1340 var pi = this.parent().items;
1341 for (var i = 0;i < pi.length;i++) {
1342 if (this == pi[i]) {
1345 if (pi[i].el.hasClass('roo-button')) {
1346 pi[i].setActive(false);
1349 this.fireEvent('click', this, e);
1353 if (this.pressed === true || this.pressed === false) {
1354 this.toggleActive(e);
1358 this.fireEvent('click', this, e);
1360 onDblClick: function(e)
1362 if (this.disabled) {
1365 if(this.preventDefault){
1368 this.fireEvent('dblclick', this, e);
1371 * Enables this button
1375 this.disabled = false;
1376 this.el.removeClass('disabled');
1377 this.el.dom.removeAttribute("disabled");
1381 * Disable this button
1383 disable : function()
1385 this.disabled = true;
1386 this.el.addClass('disabled');
1387 this.el.attr("disabled", "disabled")
1390 * sets the active state on/off,
1391 * @param {Boolean} state (optional) Force a particular state
1393 setActive : function(v) {
1395 this.el[v ? 'addClass' : 'removeClass']('active');
1399 * toggles the current active state
1401 toggleActive : function(e)
1403 this.setActive(!this.pressed); // this modifies pressed...
1404 this.fireEvent('toggle', this, e, this.pressed);
1407 * get the current active state
1408 * @return {boolean} true if it's active
1410 isActive : function()
1412 return this.el.hasClass('active');
1415 * set the text of the first selected button
1417 setText : function(str)
1419 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1422 * get the text of the first selected button
1424 getText : function()
1426 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1429 setWeight : function(str)
1431 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432 this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434 var outline = this.outline ? 'outline-' : '';
1435 if (str == 'default') {
1436 this.el.addClass('btn-default btn-outline-secondary');
1439 this.el.addClass('btn-' + outline + str);
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446 Roo.bootstrap.Button.weights = [
1466 * @class Roo.bootstrap.Column
1467 * @extends Roo.bootstrap.Component
1468 * Bootstrap Column class
1469 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1479 * @cfg {Boolean} hidden (true|false) hide the element
1480 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481 * @cfg {String} fa (ban|check|...) font awesome icon
1482 * @cfg {Number} fasize (1|2|....) font awsome size
1484 * @cfg {String} icon (info-sign|check|...) glyphicon name
1486 * @cfg {String} html content of column.
1489 * Create a new Column
1490 * @param {Object} config The config object
1493 Roo.bootstrap.Column = function(config){
1494 Roo.bootstrap.Column.superclass.constructor.call(this, config);
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
1515 getAutoCreate : function(){
1516 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1524 var sizes = ['xs','sm','md','lg'];
1525 sizes.map(function(size ,ix){
1526 //Roo.log( size + ':' + settings[size]);
1528 if (settings[size+'off'] !== false) {
1529 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1532 if (settings[size] === false) {
1536 if (!settings[size]) { // 0 = hidden
1537 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539 for (var i = ix; i > -1; i--) {
1540 cfg.cls += ' d-' + sizes[i] + '-none';
1546 cfg.cls += ' col-' + size + '-' + settings[size] + (
1547 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1553 cfg.cls += ' hidden';
1556 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557 cfg.cls +=' alert alert-' + this.alert;
1561 if (this.html.length) {
1562 cfg.html = this.html;
1566 if (this.fasize > 1) {
1567 fasize = ' fa-' + this.fasize + 'x';
1569 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1574 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + (cfg.html || '');
1593 * @class Roo.bootstrap.Container
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap Container class
1596 * @cfg {Boolean} jumbotron is it a jumbotron element
1597 * @cfg {String} html content of element
1598 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599 * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel - type - primary/success.....
1600 * @cfg {String} header content of header (for panel)
1601 * @cfg {String} footer content of footer (for panel)
1602 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603 * @cfg {String} tag (header|aside|section) type of HTML tag.
1604 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605 * @cfg {String} fa font awesome icon
1606 * @cfg {String} icon (info-sign|check|...) glyphicon name
1607 * @cfg {Boolean} hidden (true|false) hide the element
1608 * @cfg {Boolean} expandable (true|false) default false
1609 * @cfg {Boolean} expanded (true|false) default true
1610 * @cfg {String} rheader contet on the right of header
1611 * @cfg {Boolean} clickable (true|false) default false
1615 * Create a new Container
1616 * @param {Object} config The config object
1619 Roo.bootstrap.Container = function(config){
1620 Roo.bootstrap.Container.superclass.constructor.call(this, config);
1626 * After the panel has been expand
1628 * @param {Roo.bootstrap.Container} this
1633 * After the panel has been collapsed
1635 * @param {Roo.bootstrap.Container} this
1640 * When a element is chick
1641 * @param {Roo.bootstrap.Container} this
1642 * @param {Roo.EventObject} e
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
1666 getChildContainer : function() {
1672 if (this.panel.length) {
1673 return this.el.select('.panel-body',true).first();
1680 getAutoCreate : function(){
1683 tag : this.tag || 'div',
1687 if (this.jumbotron) {
1688 cfg.cls = 'jumbotron';
1693 // - this is applied by the parent..
1695 // cfg.cls = this.cls + '';
1698 if (this.sticky.length) {
1700 var bd = Roo.get(document.body);
1701 if (!bd.hasClass('bootstrap-sticky')) {
1702 bd.addClass('bootstrap-sticky');
1703 Roo.select('html',true).setStyle('height', '100%');
1706 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1710 if (this.well.length) {
1711 switch (this.well) {
1714 cfg.cls +=' well well-' +this.well;
1723 cfg.cls += ' hidden';
1727 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728 cfg.cls +=' alert alert-' + this.alert;
1733 if (this.panel.length) {
1734 cfg.cls += ' panel panel-' + this.panel;
1736 if (this.header.length) {
1740 if(this.expandable){
1742 cfg.cls = cfg.cls + ' expandable';
1746 cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus')
1754 cls : 'panel-title',
1755 html : (this.expandable ? ' ' : '') + this.header
1759 cls: 'panel-header-right',
1765 cls : 'panel-heading',
1766 style : this.expandable ? 'cursor: pointer' : '',
1774 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1779 if (this.footer.length) {
1781 cls : 'panel-footer',
1790 body.html = this.html || cfg.html;
1791 // prefix with the icons..
1793 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1796 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1801 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802 cfg.cls = 'container';
1808 initEvents: function()
1810 if(this.expandable){
1811 var headerEl = this.headerEl();
1814 headerEl.on('click', this.onToggleClick, this);
1819 this.el.on('click', this.onClick, this);
1824 onToggleClick : function()
1826 var headerEl = this.headerEl();
1842 if(this.fireEvent('expand', this)) {
1844 this.expanded = true;
1846 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848 this.el.select('.panel-body',true).first().removeClass('hide');
1850 var toggleEl = this.toggleEl();
1856 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1861 collapse : function()
1863 if(this.fireEvent('collapse', this)) {
1865 this.expanded = false;
1867 //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868 this.el.select('.panel-body',true).first().addClass('hide');
1870 var toggleEl = this.toggleEl();
1876 toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1880 toggleEl : function()
1882 if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1886 return this.el.select('.panel-heading .fa',true).first();
1889 headerEl : function()
1891 if(!this.el || !this.panel.length || !this.header.length){
1895 return this.el.select('.panel-heading',true).first()
1900 if(!this.el || !this.panel.length){
1904 return this.el.select('.panel-body',true).first()
1907 titleEl : function()
1909 if(!this.el || !this.panel.length || !this.header.length){
1913 return this.el.select('.panel-title',true).first();
1916 setTitle : function(v)
1918 var titleEl = this.titleEl();
1924 titleEl.dom.innerHTML = v;
1927 getTitle : function()
1930 var titleEl = this.titleEl();
1936 return titleEl.dom.innerHTML;
1939 setRightTitle : function(v)
1941 var t = this.el.select('.panel-header-right',true).first();
1947 t.dom.innerHTML = v;
1950 onClick : function(e)
1954 this.fireEvent('click', this, e);
1961 * This is BS4's Card element.. - similar to our containers probably..
1965 * @class Roo.bootstrap.Card
1966 * @extends Roo.bootstrap.Component
1967 * Bootstrap Card class
1970 * possible... may not be implemented..
1971 * @cfg {String} header_image src url of image.
1972 * @cfg {String|Object} header
1973 * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974 * @cfg {Number} header_weight (primary|secondary|success|info|warning|danger|light|dark)
1976 * @cfg {String} title
1977 * @cfg {String} subtitle
1978 * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979 * @cfg {String} footer
1981 * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983 * @cfg {String} margin (0|1|2|3|4|5|auto)
1984 * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985 * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986 * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987 * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988 * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989 * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991 * @cfg {String} padding (0|1|2|3|4|5)
1992 * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993 * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994 * @cfg {String} padding_left (0|1|2|3|4|5)
1995 * @cfg {String} padding_right (0|1|2|3|4|5)
1996 * @cfg {String} padding_x (0|1|2|3|4|5)
1997 * @cfg {String} padding_y (0|1|2|3|4|5)
1999 * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000 * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001 * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002 * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003 * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005 * @config {Boolean} dragable if this card can be dragged.
2006 * @config {String} drag_group group for drag
2007 * @config {Boolean} dropable if this card can recieve other cards being dropped onto it..
2008 * @config {String} drop_group group for drag
2010 * @config {Boolean} collapsable can the body be collapsed.
2011 * @config {Boolean} collapsed is the body collapsed when rendered...
2012 * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013 * @config {Boolean} rotated is the body rotated when rendered...
2016 * Create a new Container
2017 * @param {Object} config The config object
2020 Roo.bootstrap.Card = function(config){
2021 Roo.bootstrap.Card.superclass.constructor.call(this, config);
2027 * When a element a card is dropped
2028 * @param {Roo.bootstrap.Card} this
2031 * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032 * @param {String} position 'above' or 'below'
2033 * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2039 * When a element a card is rotate
2040 * @param {Roo.bootstrap.Card} this
2041 * @param {Roo.Element} n the node being dropped?
2042 * @param {Boolean} rotate status
2047 * When a card element is dragged over ready to drop (return false to block dropable)
2048 * @param {Roo.bootstrap.Card} this
2049 * @param {Object} data from dragdrop
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component, {
2062 margin: '', /// may be better in component?
2092 collapsable : false,
2101 childContainer : false,
2102 dropEl : false, /// the dom placeholde element that indicates drop location.
2103 containerEl: false, // body container
2104 bodyEl: false, // card-body
2105 headerContainerEl : false, //
2107 header_imageEl : false,
2110 layoutCls : function()
2114 Roo.log(this.margin_bottom.length);
2115 ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116 // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118 if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119 cls += ' m' + (v.length ? v[0] : '') + '-' + t['margin' + (v.length ? '_' : '') + v];
2121 if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122 cls += ' p' + (v.length ? v[0] : '') + '-' + t['padding' + (v.length ? '_' : '') + v];
2126 ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127 if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128 cls += ' d' + (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2132 // more generic support?
2140 // Roo.log("Call onRender: " + this.xtype);
2141 /* We are looking at something like this.
2143 <img src="..." class="card-img-top" alt="...">
2144 <div class="card-body">
2145 <h5 class="card-title">Card title</h5>
2146 <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148 >> this bit is really the body...
2149 <div> << we will ad dthis in hopefully it will not break shit.
2151 ** card text does not actually have any styling...
2153 <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>
2156 <a href="#" class="card-link">Card link</a>
2159 <div class="card-footer">
2160 <small class="text-muted">Last updated 3 mins ago</small>
2164 getAutoCreate : function(){
2172 if (this.weight.length && this.weight != 'light') {
2173 cfg.cls += ' text-white';
2175 cfg.cls += ' text-dark'; // need as it's nested..
2177 if (this.weight.length) {
2178 cfg.cls += ' bg-' + this.weight;
2181 cfg.cls += ' ' + this.layoutCls();
2184 var hdr_ctr = false;
2185 if (this.header.length) {
2187 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2196 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2202 if (this.collapsable) {
2205 cls : 'd-block user-select-none',
2209 cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2214 hdr.cn.push(hdr_ctr);
2219 cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2224 if (this.header_image.length) {
2227 cls : 'card-img-top',
2228 src: this.header_image // escape?
2233 cls : 'card-img-top d-none'
2239 cls : 'card-body' + (this.html === false ? ' d-none' : ''),
2243 if (this.collapsable || this.rotateable) {
2246 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2253 if (this.title.length) {
2257 src: this.title // escape?
2261 if (this.subtitle.length) {
2265 src: this.subtitle // escape?
2271 cls : 'roo-card-body-ctr'
2274 if (this.html.length) {
2280 // fixme ? handle objects?
2282 if (this.footer.length) {
2285 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2290 cfg.cn.push({cls : 'card-footer d-none'});
2299 getCardHeader : function()
2301 var ret = this.el.select('.card-header',true).first();
2302 if (ret.hasClass('d-none')) {
2303 ret.removeClass('d-none');
2308 getCardFooter : function()
2310 var ret = this.el.select('.card-footer',true).first();
2311 if (ret.hasClass('d-none')) {
2312 ret.removeClass('d-none');
2317 getCardImageTop : function()
2319 var ret = this.header_imageEl;
2320 if (ret.hasClass('d-none')) {
2321 ret.removeClass('d-none');
2327 getChildContainer : function()
2333 return this.el.select('.roo-card-body-ctr',true).first();
2336 initEvents: function()
2338 this.bodyEl = this.el.select('.card-body',true).first();
2339 this.containerEl = this.getChildContainer();
2341 this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342 containerScroll: true,
2343 ddGroup: this.drag_group || 'default_card_drag_group'
2345 this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347 if (this.dropable) {
2348 this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349 containerScroll: true,
2350 ddGroup: this.drop_group || 'default_card_drag_group'
2352 this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353 this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354 this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355 this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356 this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2359 if (this.collapsable) {
2360 this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362 if (this.rotateable) {
2363 this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365 this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367 this.footerEl = this.el.select('.card-footer',true).first();
2368 this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369 this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370 this.headerEl = this.el.select('.card-header',true).first();
2373 this.el.addClass('roo-card-rotated');
2374 this.fireEvent('rotate', this, true);
2376 this.header_imageEl = this.el.select('.card-img-top',true).first();
2377 this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2380 getDragData : function(e)
2382 var target = this.getEl();
2384 //this.handleSelection(e);
2389 nodes: this.getEl(),
2394 dragData.ddel = target.dom ; // the div element
2395 Roo.log(target.getWidth( ));
2396 dragData.ddel.style.width = target.getWidth() + 'px';
2403 * Part of the Roo.dd.DropZone interface. If no target node is found, the
2404 * whole Element becomes the target, and this causes the drop gesture to append.
2406 * Returns an object:
2409 position : 'below' or 'above'
2410 card : relateive to card OBJECT (or true for no cards listed)
2411 items_n : relative to nth item in list
2412 card_n : relative to nth card in list
2417 getTargetFromEvent : function(e, dragged_card_el)
2419 var target = e.getTarget();
2420 while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421 target = target.parentNode;
2432 //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433 // see if target is one of the 'cards'...
2436 //Roo.log(this.items.length);
2439 var last_card_n = 0;
2441 for (var i = 0;i< this.items.length;i++) {
2443 if (!this.items[i].el.hasClass('card')) {
2446 pos = this.getDropPoint(e, this.items[i].el.dom);
2448 cards_len = ret.cards.length;
2449 //Roo.log(this.items[i].el.dom.id);
2450 ret.cards.push(this.items[i]);
2452 if (ret.card_n < 0 && pos == 'above') {
2453 ret.position = cards_len > 0 ? 'below' : pos;
2454 ret.items_n = i > 0 ? i - 1 : 0;
2455 ret.card_n = cards_len > 0 ? cards_len - 1 : 0;
2456 ret.card = ret.cards[ret.card_n];
2459 if (!ret.cards.length) {
2461 ret.position = 'below';
2465 // could not find a card.. stick it at the end..
2466 if (ret.card_n < 0) {
2467 ret.card_n = last_card_n;
2468 ret.card = ret.cards[last_card_n];
2469 ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470 ret.position = 'below';
2473 if (this.items[ret.items_n].el == dragged_card_el) {
2477 if (ret.position == 'below') {
2478 var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480 if (card_after && card_after.el == dragged_card_el) {
2487 var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489 if (card_before && card_before.el == dragged_card_el) {
2496 onNodeEnter : function(n, dd, e, data){
2499 onNodeOver : function(n, dd, e, data)
2502 var target_info = this.getTargetFromEvent(e,data.source.el);
2503 if (target_info === false) {
2504 this.dropPlaceHolder('hide');
2507 Roo.log(['getTargetFromEvent', target_info ]);
2510 if (this.fireEvent('cardover', this, [ data ]) === false) {
2514 this.dropPlaceHolder('show', target_info,data);
2518 onNodeOut : function(n, dd, e, data){
2519 this.dropPlaceHolder('hide');
2522 onNodeDrop : function(n, dd, e, data)
2525 // call drop - return false if
2527 // this could actually fail - if the Network drops..
2528 // we will ignore this at present..- client should probably reload
2529 // the whole set of cards if stuff like that fails.
2532 var info = this.getTargetFromEvent(e,data.source.el);
2533 if (info === false) {
2536 this.dropPlaceHolder('hide');
2540 this.acceptCard(data.source, info.position, info.card, info.items_n);
2544 firstChildCard : function()
2546 for (var i = 0;i< this.items.length;i++) {
2548 if (!this.items[i].el.hasClass('card')) {
2551 return this.items[i];
2553 return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2558 * - card.acceptCard(move_card, info.position, info.card, info.items_n);
2560 acceptCard : function(move_card, position, next_to_card )
2562 if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2566 var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568 move_card.parent().removeCard(move_card);
2571 var dom = move_card.el.dom;
2572 dom.style.width = ''; // clear with - which is set by drag.
2574 if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575 var cardel = next_to_card.el.dom;
2577 if (position == 'above' ) {
2578 cardel.parentNode.insertBefore(dom, cardel);
2579 } else if (cardel.nextSibling) {
2580 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582 cardel.parentNode.append(dom);
2585 // card container???
2586 this.containerEl.dom.append(dom);
2589 //FIXME HANDLE card = true
2591 // add this to the correct place in items.
2593 // remove Card from items.
2596 if (this.items.length) {
2598 //Roo.log([info.items_n, info.position, this.items.length]);
2599 for (var i =0; i < this.items.length; i++) {
2600 if (i == to_items_n && position == 'above') {
2601 nitems.push(move_card);
2603 nitems.push(this.items[i]);
2604 if (i == to_items_n && position == 'below') {
2605 nitems.push(move_card);
2608 this.items = nitems;
2609 Roo.log(this.items);
2611 this.items.push(move_card);
2614 move_card.parentId = this.id;
2620 removeCard : function(c)
2622 this.items = this.items.filter(function(e) { return e != c });
2625 dom.parentNode.removeChild(dom);
2626 dom.style.width = ''; // clear with - which is set by drag.
2631 /** Decide whether to drop above or below a View node. */
2632 getDropPoint : function(e, n, dd)
2637 if (n == this.containerEl.dom) {
2640 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641 var c = t + (b - t) / 2;
2642 var y = Roo.lib.Event.getPageY(e);
2649 onToggleCollapse : function(e)
2651 if (this.collapsed) {
2652 this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653 this.collapsableEl.addClass('show');
2654 this.collapsed = false;
2657 this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658 this.collapsableEl.removeClass('show');
2659 this.collapsed = true;
2664 onToggleRotate : function(e)
2666 this.collapsableEl.removeClass('show');
2667 this.footerEl.removeClass('d-none');
2668 this.el.removeClass('roo-card-rotated');
2669 this.el.removeClass('d-none');
2672 this.collapsableEl.addClass('show');
2673 this.rotated = false;
2674 this.fireEvent('rotate', this, this.rotated);
2677 this.el.addClass('roo-card-rotated');
2678 this.footerEl.addClass('d-none');
2679 this.el.select('.roo-collapsable').removeClass('show');
2681 this.rotated = true;
2682 this.fireEvent('rotate', this, this.rotated);
2686 dropPlaceHolder: function (action, info, data)
2688 if (this.dropEl === false) {
2689 this.dropEl = Roo.DomHelper.append(this.containerEl, {
2693 this.dropEl.removeClass(['d-none', 'd-block']);
2694 if (action == 'hide') {
2696 this.dropEl.addClass('d-none');
2699 // FIXME - info.card == true!!!
2700 this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702 if (info.card !== true) {
2703 var cardel = info.card.el.dom;
2705 if (info.position == 'above') {
2706 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707 } else if (cardel.nextSibling) {
2708 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710 cardel.parentNode.append(this.dropEl.dom);
2713 // card container???
2714 this.containerEl.dom.append(this.dropEl.dom);
2717 this.dropEl.addClass('d-block roo-card-dropzone');
2719 this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2726 setHeaderText: function(html)
2729 if (this.headerContainerEl) {
2730 this.headerContainerEl.dom.innerHTML = html;
2733 onHeaderImageLoad : function(ev, he)
2735 if (!this.header_image_fit_square) {
2739 var hw = he.naturalHeight / he.naturalWidth;
2742 //var w = he.dom.naturalWidth;
2745 he.style.position = 'relative';
2747 var nw = (ww * (1/hw));
2748 Roo.get(he).setSize( ww * (1/hw), ww);
2749 he.style.left = ((ww - nw)/ 2) + 'px';
2750 he.style.position = 'relative';
2761 * Card header - holder for the card header elements.
2766 * @class Roo.bootstrap.CardHeader
2767 * @extends Roo.bootstrap.Element
2768 * Bootstrap CardHeader class
2770 * Create a new Card Header - that you can embed children into
2771 * @param {Object} config The config object
2774 Roo.bootstrap.CardHeader = function(config){
2775 Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element, {
2781 container_method : 'getCardHeader'
2794 * Card footer - holder for the card footer elements.
2799 * @class Roo.bootstrap.CardFooter
2800 * @extends Roo.bootstrap.Element
2801 * Bootstrap CardFooter class
2803 * Create a new Card Footer - that you can embed children into
2804 * @param {Object} config The config object
2807 Roo.bootstrap.CardFooter = function(config){
2808 Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element, {
2814 container_method : 'getCardFooter'
2827 * Card header - holder for the card header elements.
2832 * @class Roo.bootstrap.CardImageTop
2833 * @extends Roo.bootstrap.Element
2834 * Bootstrap CardImageTop class
2836 * Create a new Card Image Top container
2837 * @param {Object} config The config object
2840 Roo.bootstrap.CardImageTop = function(config){
2841 Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element, {
2847 container_method : 'getCardImageTop'
2862 * @class Roo.bootstrap.ButtonUploader
2863 * @extends Roo.bootstrap.Button
2864 * Bootstrap Button Uploader class - it's a button which when you add files to it
2867 * @cfg {Number} errorTimeout default 3000
2868 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
2869 * @cfg {Array} html The button text.
2870 * @cfg {Boolean} multiple (default true) Should the upload allow multiple files to be uploaded.
2873 * Create a new CardUploader
2874 * @param {Object} config The config object
2877 Roo.bootstrap.ButtonUploader = function(config){
2881 Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2887 * @event beforeselect
2888 * When button is pressed, before show upload files dialog is shown
2889 * @param {Roo.bootstrap.UploaderButton} this
2892 'beforeselect' : true,
2894 * @event fired when files have been selected,
2895 * When a the download link is clicked
2896 * @param {Roo.bootstrap.UploaderButton} this
2897 * @param {Array} Array of files that have been uploaded
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button, {
2907 errorTimeout : 3000,
2911 fileCollection : false,
2916 getAutoCreate : function()
2921 cls : 'd-none roo-card-upload-selector'
2924 if (this.multiple) {
2925 im.multiple = 'multiple';
2931 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2941 initEvents : function()
2944 Roo.bootstrap.Button.prototype.initEvents.call(this);
2950 this.urlAPI = (window.createObjectURL && window) ||
2951 (window.URL && URL.revokeObjectURL && URL) ||
2952 (window.webkitURL && webkitURL);
2957 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959 this.selectorEl.on('change', this.onFileSelected, this);
2966 onClick : function(e)
2970 if ( this.fireEvent('beforeselect', this) === false) {
2974 this.selectorEl.dom.click();
2978 onFileSelected : function(e)
2982 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2985 var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986 this.selectorEl.dom.value = '';// hopefully reset..
2988 this.fireEvent('uploaded', this, files );
2996 * addCard - add an Attachment to the uploader
2997 * @param data - the data about the image to upload
3001 title : "Title of file",
3002 is_uploaded : false,
3003 src : "http://.....",
3004 srcfile : { the File upload object },
3005 mimetype : file.type,
3008 .. any other data...
3033 * @class Roo.bootstrap.Img
3034 * @extends Roo.bootstrap.Component
3035 * Bootstrap Img class
3036 * @cfg {Boolean} imgResponsive false | true
3037 * @cfg {String} border rounded | circle | thumbnail
3038 * @cfg {String} src image source
3039 * @cfg {String} alt image alternative text
3040 * @cfg {String} href a tag href
3041 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042 * @cfg {String} xsUrl xs image source
3043 * @cfg {String} smUrl sm image source
3044 * @cfg {String} mdUrl md image source
3045 * @cfg {String} lgUrl lg image source
3046 * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3049 * Create a new Input
3050 * @param {Object} config The config object
3053 Roo.bootstrap.Img = function(config){
3054 Roo.bootstrap.Img.superclass.constructor.call(this, config);
3060 * The img click event for the img.
3061 * @param {Roo.EventObject} e
3066 * The when any image loads
3067 * @param {Roo.EventObject} e
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
3075 imgResponsive: true,
3084 backgroundContain : false,
3086 getAutoCreate : function()
3088 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089 return this.createSingleImg();
3094 cls: 'roo-image-responsive-group',
3099 Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101 if(!_this[size + 'Url']){
3107 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108 html: _this.html || cfg.html,
3109 src: _this[size + 'Url']
3112 img.cls += ' roo-image-responsive-' + size;
3114 var s = ['xs', 'sm', 'md', 'lg'];
3116 s.splice(s.indexOf(size), 1);
3118 Roo.each(s, function(ss){
3119 img.cls += ' hidden-' + ss;
3122 if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123 cfg.cls += ' img-' + _this.border;
3127 cfg.alt = _this.alt;
3140 a.target = _this.target;
3144 cfg.cn.push((_this.href) ? a : img);
3151 createSingleImg : function()
3155 cls: (this.imgResponsive) ? 'img-responsive' : '',
3157 src : Roo.BLANK_IMAGE_URL // just incase src get's set to undefined?!?
3160 if (this.backgroundContain) {
3161 cfg.cls += ' background-contain';
3164 cfg.html = this.html || cfg.html;
3166 if (this.backgroundContain) {
3167 cfg.style="background-image: url(" + this.src + ')';
3169 cfg.src = this.src || cfg.src;
3172 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173 cfg.cls += ' img-' + this.border;
3190 a.target = this.target;
3195 return (this.href) ? a : cfg;
3198 initEvents: function()
3201 this.el.on('click', this.onClick, this);
3203 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204 this.el.on('load', this.onImageLoad, this);
3206 // not sure if this works.. not tested
3207 this.el.select('img', true).on('load', this.onImageLoad, this);
3212 onClick : function(e)
3214 Roo.log('img onclick');
3215 this.fireEvent('click', this, e);
3217 onImageLoad: function(e)
3219 Roo.log('img load');
3220 this.fireEvent('load', this, e);
3224 * Sets the url of the image - used to update it
3225 * @param {String} url the url of the image
3228 setSrc : function(url)
3232 if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233 if (this.backgroundContain) {
3234 this.el.dom.style.backgroundImage = 'url(' + url + ')';
3236 this.el.dom.src = url;
3241 this.el.select('img', true).first().dom.src = url;
3257 * @class Roo.bootstrap.Link
3258 * @extends Roo.bootstrap.Component
3259 * Bootstrap Link Class
3260 * @cfg {String} alt image alternative text
3261 * @cfg {String} href a tag href
3262 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263 * @cfg {String} html the content of the link.
3264 * @cfg {String} anchor name for the anchor link
3265 * @cfg {String} fa - favicon
3267 * @cfg {Boolean} preventDefault (true | false) default false
3271 * Create a new Input
3272 * @param {Object} config The config object
3275 Roo.bootstrap.Link = function(config){
3276 Roo.bootstrap.Link.superclass.constructor.call(this, config);
3282 * The img click event for the img.
3283 * @param {Roo.EventObject} e
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
3293 preventDefault: false,
3299 getAutoCreate : function()
3301 var html = this.html || '';
3303 if (this.fa !== false) {
3304 html = '<i class="fa fa-' + this.fa + '"></i>';
3309 // anchor's do not require html/href...
3310 if (this.anchor === false) {
3312 cfg.href = this.href || '#';
3314 cfg.name = this.anchor;
3315 if (this.html !== false || this.fa !== false) {
3318 if (this.href !== false) {
3319 cfg.href = this.href;
3323 if(this.alt !== false){
3328 if(this.target !== false) {
3329 cfg.target = this.target;
3335 initEvents: function() {
3337 if(!this.href || this.preventDefault){
3338 this.el.on('click', this.onClick, this);
3342 onClick : function(e)
3344 if(this.preventDefault){
3347 //Roo.log('img onclick');
3348 this.fireEvent('click', this, e);
3361 * @class Roo.bootstrap.Header
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap Header class
3364 * @cfg {String} html content of header
3365 * @cfg {Number} level (1|2|3|4|5|6) default 1
3368 * Create a new Header
3369 * @param {Object} config The config object
3373 Roo.bootstrap.Header = function(config){
3374 Roo.bootstrap.Header.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3390 tag: 'h' + (1 *this.level),
3391 html: this.html || ''
3403 * Ext JS Library 1.1.1
3404 * Copyright(c) 2006-2007, Ext JS, LLC.
3406 * Originally Released Under LGPL - original licence link has changed is not relivant.
3409 * <script type="text/javascript">
3413 * @class Roo.bootstrap.MenuMgr
3414 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3417 Roo.bootstrap.MenuMgr = function(){
3418 var menus, active, groups = {}, attached = false, lastShow = new Date();
3420 // private - called when first menu is created
3423 active = new Roo.util.MixedCollection();
3424 Roo.get(document).addKeyListener(27, function(){
3425 if(active.length > 0){
3433 if(active && active.length > 0){
3434 var c = active.clone();
3444 if(active.length < 1){
3445 Roo.get(document).un("mouseup", onMouseDown);
3453 var last = active.last();
3454 lastShow = new Date();
3457 Roo.get(document).on("mouseup", onMouseDown);
3462 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463 m.parentMenu.activeChild = m;
3464 }else if(last && last.isVisible()){
3465 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3470 function onBeforeHide(m){
3472 m.activeChild.hide();
3474 if(m.autoHideTimer){
3475 clearTimeout(m.autoHideTimer);
3476 delete m.autoHideTimer;
3481 function onBeforeShow(m){
3482 var pm = m.parentMenu;
3483 if(!pm && !m.allowOtherMenus){
3485 }else if(pm && pm.activeChild && active != m){
3486 pm.activeChild.hide();
3490 // private this should really trigger on mouseup..
3491 function onMouseDown(e){
3492 Roo.log("on Mouse Up");
3494 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495 Roo.log("MenuManager hideAll");
3504 function onBeforeCheck(mi, state){
3506 var g = groups[mi.group];
3507 for(var i = 0, l = g.length; i < l; i++){
3509 g[i].setChecked(false);
3518 * Hides all menus that are currently visible
3520 hideAll : function(){
3525 register : function(menu){
3529 menus[menu.id] = menu;
3530 menu.on("beforehide", onBeforeHide);
3531 menu.on("hide", onHide);
3532 menu.on("beforeshow", onBeforeShow);
3533 menu.on("show", onShow);
3535 if(g && menu.events["checkchange"]){
3539 groups[g].push(menu);
3540 menu.on("checkchange", onCheck);
3545 * Returns a {@link Roo.menu.Menu} object
3546 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547 * be used to generate and return a new Menu instance.
3549 get : function(menu){
3550 if(typeof menu == "string"){ // menu id
3552 }else if(menu.events){ // menu instance
3555 /*else if(typeof menu.length == 'number'){ // array of menu items?
3556 return new Roo.bootstrap.Menu({items:menu});
3557 }else{ // otherwise, must be a config
3558 return new Roo.bootstrap.Menu(menu);
3565 unregister : function(menu){
3566 delete menus[menu.id];
3567 menu.un("beforehide", onBeforeHide);
3568 menu.un("hide", onHide);
3569 menu.un("beforeshow", onBeforeShow);
3570 menu.un("show", onShow);
3572 if(g && menu.events["checkchange"]){
3573 groups[g].remove(menu);
3574 menu.un("checkchange", onCheck);
3579 registerCheckable : function(menuItem){
3580 var g = menuItem.group;
3585 groups[g].push(menuItem);
3586 menuItem.on("beforecheckchange", onBeforeCheck);
3591 unregisterCheckable : function(menuItem){
3592 var g = menuItem.group;
3594 groups[g].remove(menuItem);
3595 menuItem.un("beforecheckchange", onBeforeCheck);
3607 * @class Roo.bootstrap.Menu
3608 * @extends Roo.bootstrap.Component
3609 * Bootstrap Menu class - container for MenuItems
3610 * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611 * @cfg {bool} hidden if the menu should be hidden when rendered.
3612 * @cfg {bool} stopEvent (true|false) Stop event after trigger press (default true)
3613 * @cfg {bool} isLink (true|false) the menu has link disable auto expand and collaspe (default false)
3614 * @cfg {bool} hideTrigger (true|false) default false - hide the carret for trigger.
3615 * @cfg {String} align default tl-bl? == below - how the menu should be aligned.
3619 * @param {Object} config The config object
3623 Roo.bootstrap.Menu = function(config){
3625 if (config.type == 'treeview') {
3626 // normally menu's are drawn attached to the document to handle layering etc..
3627 // however treeview (used by the docs menu is drawn into the parent element)
3628 this.container_method = 'getChildContainer';
3631 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632 if (this.registerMenu && this.type != 'treeview') {
3633 Roo.bootstrap.MenuMgr.register(this);
3640 * Fires before this menu is displayed (return false to block)
3641 * @param {Roo.menu.Menu} this
3646 * Fires before this menu is hidden (return false to block)
3647 * @param {Roo.menu.Menu} this
3652 * Fires after this menu is displayed
3653 * @param {Roo.menu.Menu} this
3658 * Fires after this menu is hidden
3659 * @param {Roo.menu.Menu} this
3664 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665 * @param {Roo.menu.Menu} this
3666 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667 * @param {Roo.EventObject} e
3672 * Fires when the mouse is hovering over this menu
3673 * @param {Roo.menu.Menu} this
3674 * @param {Roo.EventObject} e
3675 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3680 * Fires when the mouse exits this menu
3681 * @param {Roo.menu.Menu} this
3682 * @param {Roo.EventObject} e
3683 * @param {Roo.menu.Item} menuItem The menu item that was clicked
3688 * Fires when a menu item contained in this menu is clicked
3689 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690 * @param {Roo.EventObject} e
3694 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
3701 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
3704 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706 registerMenu : true,
3708 menuItems :false, // stores the menu items..
3718 container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720 hideTrigger : false,
3725 getChildContainer : function() {
3729 getAutoCreate : function(){
3731 //if (['right'].indexOf(this.align)!==-1) {
3732 // cfg.cn[1].cls += ' pull-right'
3737 cls : 'dropdown-menu shadow' ,
3738 style : 'z-index:1000'
3742 if (this.type === 'submenu') {
3743 cfg.cls = 'submenu active';
3745 if (this.type === 'treeview') {
3746 cfg.cls = 'treeview-menu';
3751 initEvents : function() {
3753 // Roo.log("ADD event");
3754 // Roo.log(this.triggerEl.dom);
3755 if (this.triggerEl) {
3757 this.triggerEl.on('click', this.onTriggerClick, this);
3759 this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761 if (!this.hideTrigger) {
3762 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763 // dropdown toggle on the 'a' in BS4?
3764 this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766 this.triggerEl.addClass('dropdown-toggle');
3772 this.el.on('touchstart' , this.onTouch, this);
3774 this.el.on('click' , this.onClick, this);
3776 this.el.on("mouseover", this.onMouseOver, this);
3777 this.el.on("mouseout", this.onMouseOut, this);
3781 findTargetItem : function(e)
3783 var t = e.getTarget(".dropdown-menu-item", this.el, true);
3787 //Roo.log(t); Roo.log(t.id);
3789 //Roo.log(this.menuitems);
3790 return this.menuitems.get(t.id);
3792 //return this.items.get(t.menuItemId);
3798 onTouch : function(e)
3800 Roo.log("menu.onTouch");
3801 //e.stopEvent(); this make the user popdown broken
3805 onClick : function(e)
3807 Roo.log("menu.onClick");
3809 var t = this.findTargetItem(e);
3810 if(!t || t.isContainer){
3815 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
3816 if(t == this.activeItem && t.shouldDeactivate(e)){
3817 this.activeItem.deactivate();
3818 delete this.activeItem;
3822 this.setActiveItem(t, true);
3830 Roo.log('pass click event');
3834 this.fireEvent("click", this, t, e);
3838 if(!t.href.length || t.href == '#'){
3839 (function() { _this.hide(); }).defer(100);
3844 onMouseOver : function(e){
3845 var t = this.findTargetItem(e);
3848 // if(t.canActivate && !t.disabled){
3849 // this.setActiveItem(t, true);
3853 this.fireEvent("mouseover", this, e, t);
3855 isVisible : function(){
3856 return !this.hidden;
3858 onMouseOut : function(e){
3859 var t = this.findTargetItem(e);
3862 // if(t == this.activeItem && t.shouldDeactivate(e)){
3863 // this.activeItem.deactivate();
3864 // delete this.activeItem;
3867 this.fireEvent("mouseout", this, e, t);
3872 * Displays this menu relative to another element
3873 * @param {String/HTMLElement/Roo.Element} element The element to align to
3874 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875 * the element (defaults to this.defaultAlign)
3876 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878 show : function(el, pos, parentMenu)
3880 if (false === this.fireEvent("beforeshow", this)) {
3881 Roo.log("show canceled");
3884 this.parentMenu = parentMenu;
3888 this.el.addClass('show'); // show otherwise we do not know how big we are..
3890 var xy = this.el.getAlignToXY(el, pos);
3892 // bl-tl << left align below
3893 // tl-bl << left align
3895 if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896 // if it goes to far to the right.. -> align left.
3897 xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3900 // was left align - go right?
3901 xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3904 // goes down the bottom
3905 if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907 var a = this.align.replace('?', '').split('-');
3908 xy = this.el.getAlignToXY(el, a[1] + '-' + a[0] + '?')
3912 this.showAt( xy , parentMenu, false);
3915 * Displays this menu at a specific xy position
3916 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919 showAt : function(xy, parentMenu, /* private: */_e){
3920 this.parentMenu = parentMenu;
3925 this.fireEvent("beforeshow", this);
3926 //xy = this.el.adjustForConstraints(xy);
3930 this.hideMenuItems();
3931 this.hidden = false;
3932 if (this.triggerEl) {
3933 this.triggerEl.addClass('open');
3936 this.el.addClass('show');
3940 // reassign x when hitting right
3942 // reassign y when hitting bottom
3944 // but the list may align on trigger left or trigger top... should it be a properity?
3946 if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3951 this.fireEvent("show", this);
3957 this.doFocus.defer(50, this);
3961 doFocus : function(){
3963 this.focusEl.focus();
3968 * Hides this menu and optionally all parent menus
3969 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971 hide : function(deep)
3973 if (false === this.fireEvent("beforehide", this)) {
3974 Roo.log("hide canceled");
3977 this.hideMenuItems();
3978 if(this.el && this.isVisible()){
3980 if(this.activeItem){
3981 this.activeItem.deactivate();
3982 this.activeItem = null;
3984 if (this.triggerEl) {
3985 this.triggerEl.removeClass('open');
3988 this.el.removeClass('show');
3990 this.fireEvent("hide", this);
3992 if(deep === true && this.parentMenu){
3993 this.parentMenu.hide(true);
3997 onTriggerClick : function(e)
3999 Roo.log('trigger click');
4001 var target = e.getTarget();
4003 Roo.log(target.nodeName.toLowerCase());
4005 if(target.nodeName.toLowerCase() === 'i'){
4011 onTriggerPress : function(e)
4013 Roo.log('trigger press');
4014 //Roo.log(e.getTarget());
4015 // Roo.log(this.triggerEl.dom);
4017 // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018 var pel = Roo.get(e.getTarget());
4019 if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020 Roo.log('is treeview or dropdown?');
4024 if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4028 if (this.isVisible()) {
4034 this.show(this.triggerEl, this.align, false);
4037 if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4044 hideMenuItems : function()
4046 Roo.log("hide Menu Items");
4051 this.el.select('.open',true).each(function(aa) {
4053 aa.removeClass('open');
4057 addxtypeChild : function (tree, cntr) {
4058 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060 this.menuitems.add(comp);
4072 this.getEl().dom.innerHTML = '';
4073 this.menuitems.clear();
4087 * @class Roo.bootstrap.MenuItem
4088 * @extends Roo.bootstrap.Component
4089 * Bootstrap MenuItem class
4090 * @cfg {String} html the menu label
4091 * @cfg {String} href the link
4092 * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093 * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094 * @cfg {Boolean} active used on sidebars to highlight active itesm
4095 * @cfg {String} fa favicon to show on left of menu item.
4096 * @cfg {Roo.bootsrap.Menu} menu the child menu.
4100 * Create a new MenuItem
4101 * @param {Object} config The config object
4105 Roo.bootstrap.MenuItem = function(config){
4106 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4111 * The raw click event for the entire grid.
4112 * @param {Roo.bootstrap.MenuItem} this
4113 * @param {Roo.EventObject} e
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
4123 preventDefault: false,
4124 isContainer : false,
4128 getAutoCreate : function(){
4130 if(this.isContainer){
4133 cls: 'dropdown-menu-item '
4143 cls : 'dropdown-item',
4148 if (this.fa !== false) {
4151 cls : 'fa fa-' + this.fa
4160 cls: 'dropdown-menu-item',
4163 if (this.parent().type == 'treeview') {
4164 cfg.cls = 'treeview-menu';
4167 cfg.cls += ' active';
4172 anc.href = this.href || cfg.cn[0].href ;
4173 ctag.html = this.html || cfg.cn[0].html ;
4177 initEvents: function()
4179 if (this.parent().type == 'treeview') {
4180 this.el.select('a').on('click', this.onClick, this);
4184 this.menu.parentType = this.xtype;
4185 this.menu.triggerEl = this.el;
4186 this.menu = this.addxtype(Roo.apply({}, this.menu));
4190 onClick : function(e)
4192 Roo.log('item on click ');
4194 if(this.preventDefault){
4197 //this.parent().hideMenuItems();
4199 this.fireEvent('click', this, e);
4218 * @class Roo.bootstrap.MenuSeparator
4219 * @extends Roo.bootstrap.Component
4220 * Bootstrap MenuSeparator class
4223 * Create a new MenuItem
4224 * @param {Object} config The config object
4228 Roo.bootstrap.MenuSeparator = function(config){
4229 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
4234 getAutoCreate : function(){
4253 * @class Roo.bootstrap.Modal
4254 * @extends Roo.bootstrap.Component
4255 * Bootstrap Modal class
4256 * @cfg {String} title Title of dialog
4257 * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258 * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method adn
4259 * @cfg {Boolean} specificTitle default false
4260 * @cfg {Array} buttons Array of buttons or standard button set..
4261 * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262 * @cfg {Boolean} animate default true
4263 * @cfg {Boolean} allow_close default true
4264 * @cfg {Boolean} fitwindow default false
4265 * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266 * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267 * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268 * @cfg {String} size (sm|lg|xl) default empty
4269 * @cfg {Number} max_width set the max width of modal
4270 * @cfg {Boolean} editableTitle can the title be edited
4275 * Create a new Modal Dialog
4276 * @param {Object} config The config object
4279 Roo.bootstrap.Modal = function(config){
4280 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4285 * The raw btnclick event for the button
4286 * @param {Roo.EventObject} e
4291 * Fire when dialog resize
4292 * @param {Roo.bootstrap.Modal} this
4293 * @param {Roo.EventObject} e
4297 * @event titlechanged
4298 * Fire when the editable title has been changed
4299 * @param {Roo.bootstrap.Modal} this
4300 * @param {Roo.EventObject} value
4302 "titlechanged" : true
4305 this.buttons = this.buttons || [];
4308 this.tmpl = Roo.factory(this.tmpl);
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
4315 title : 'test dialog',
4325 specificTitle: false,
4327 buttonPosition: 'right',
4349 editableTitle : false,
4351 onRender : function(ct, position)
4353 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4356 var cfg = Roo.apply({}, this.getAutoCreate());
4359 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361 //if (!cfg.name.length) {
4365 cfg.cls += ' ' + this.cls;
4368 cfg.style = this.style;
4370 this.el = Roo.get(document.body).createChild(cfg, position);
4372 //var type = this.el.dom.type;
4375 if(this.tabIndex !== undefined){
4376 this.el.dom.setAttribute('tabIndex', this.tabIndex);
4379 this.dialogEl = this.el.select('.modal-dialog',true).first();
4380 this.bodyEl = this.el.select('.modal-body',true).first();
4381 this.closeEl = this.el.select('.modal-header .close', true).first();
4382 this.headerEl = this.el.select('.modal-header',true).first();
4383 this.titleEl = this.el.select('.modal-title',true).first();
4384 this.footerEl = this.el.select('.modal-footer',true).first();
4386 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388 //this.el.addClass("x-dlg-modal");
4390 if (this.buttons.length) {
4391 Roo.each(this.buttons, function(bb) {
4392 var b = Roo.apply({}, bb);
4393 b.xns = b.xns || Roo.bootstrap;
4394 b.xtype = b.xtype || 'Button';
4395 if (typeof(b.listeners) == 'undefined') {
4396 b.listeners = { click : this.onButtonClick.createDelegate(this) };
4399 var btn = Roo.factory(b);
4401 btn.render(this.getButtonContainer());
4405 // render the children.
4408 if(typeof(this.items) != 'undefined'){
4409 var items = this.items;
4412 for(var i =0;i < items.length;i++) {
4413 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4417 this.items = nitems;
4419 // where are these used - they used to be body/close/footer
4423 //this.el.addClass([this.fieldClass, this.cls]);
4427 getAutoCreate : function()
4429 // we will default to modal-body-overflow - might need to remove or make optional later.
4431 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''),
4432 html : this.html || ''
4437 cls : 'modal-title',
4441 if(this.specificTitle){ // WTF is this?
4446 if (this.allow_close && Roo.bootstrap.version == 3) {
4456 if (this.editableTitle) {
4458 cls: 'form-control roo-editable-title d-none',
4464 if (this.allow_close && Roo.bootstrap.version == 4) {
4474 if(this.size.length){
4475 size = 'modal-' + this.size;
4478 var footer = Roo.bootstrap.version == 3 ?
4480 cls : 'modal-footer',
4484 cls: 'btn-' + this.buttonPosition
4489 { // BS4 uses mr-auto on left buttons....
4490 cls : 'modal-footer'
4501 cls: "modal-dialog " + size,
4504 cls : "modal-content",
4507 cls : 'modal-header',
4522 modal.cls += ' fade';
4528 getChildContainer : function() {
4533 getButtonContainer : function() {
4535 return Roo.bootstrap.version == 4 ?
4536 this.el.select('.modal-footer',true).first()
4537 : this.el.select('.modal-footer div',true).first();
4540 initEvents : function()
4542 if (this.allow_close) {
4543 this.closeEl.on('click', this.hide, this);
4545 Roo.EventManager.onWindowResize(this.resize, this, true);
4546 if (this.editableTitle) {
4547 this.headerEditEl = this.headerEl.select('.form-control',true).first();
4548 this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549 this.headerEditEl.on('keyup', function(e) {
4550 if([ e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551 this.toggleHeaderInput(false)
4554 this.headerEditEl.on('blur', function(e) {
4555 this.toggleHeaderInput(false)
4564 this.maskEl.setSize(
4565 Roo.lib.Dom.getViewWidth(true),
4566 Roo.lib.Dom.getViewHeight(true)
4569 if (this.fitwindow) {
4571 this.dialogEl.setStyle( { 'max-width' : '100%' });
4573 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4579 if(this.max_width !== 0) {
4581 var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4584 this.setSize(w, this.height);
4588 if(this.max_height) {
4589 this.setSize(w,Math.min(
4591 Roo.lib.Dom.getViewportHeight(true) - 60
4597 if(!this.fit_content) {
4598 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4602 this.setSize(w, Math.min(
4604 this.headerEl.getHeight() +
4605 this.footerEl.getHeight() +
4606 this.getChildHeight(this.bodyEl.dom.childNodes),
4607 Roo.lib.Dom.getViewportHeight(true) - 60)
4613 setSize : function(w,h)
4624 if (!this.rendered) {
4627 this.toggleHeaderInput(false);
4628 //this.el.setStyle('display', 'block');
4629 this.el.removeClass('hideing');
4630 this.el.dom.style.display='block';
4632 Roo.get(document.body).addClass('modal-open');
4634 if(this.animate){ // element has 'fade' - so stuff happens after .3s ?- not sure why the delay?
4637 this.el.addClass('show');
4638 this.el.addClass('in');
4641 this.el.addClass('show');
4642 this.el.addClass('in');
4645 // not sure how we can show data in here..
4647 // this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4650 Roo.get(document.body).addClass("x-body-masked");
4652 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
4653 this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654 this.maskEl.dom.style.display = 'block';
4655 this.maskEl.addClass('show');
4660 this.fireEvent('show', this);
4662 // set zindex here - otherwise it appears to be ignored...
4663 this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4666 this.items.forEach( function(e) {
4667 e.layout ? e.layout() : false;
4675 if(this.fireEvent("beforehide", this) !== false){
4677 this.maskEl.removeClass('show');
4679 this.maskEl.dom.style.display = '';
4680 Roo.get(document.body).removeClass("x-body-masked");
4681 this.el.removeClass('in');
4682 this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684 if(this.animate){ // why
4685 this.el.addClass('hideing');
4686 this.el.removeClass('show');
4688 if (!this.el.hasClass('hideing')) {
4689 return; // it's been shown again...
4692 this.el.dom.style.display='';
4694 Roo.get(document.body).removeClass('modal-open');
4695 this.el.removeClass('hideing');
4699 this.el.removeClass('show');
4700 this.el.dom.style.display='';
4701 Roo.get(document.body).removeClass('modal-open');
4704 this.fireEvent('hide', this);
4707 isVisible : function()
4710 return this.el.hasClass('show') && !this.el.hasClass('hideing');
4714 addButton : function(str, cb)
4718 var b = Roo.apply({}, { html : str } );
4719 b.xns = b.xns || Roo.bootstrap;
4720 b.xtype = b.xtype || 'Button';
4721 if (typeof(b.listeners) == 'undefined') {
4722 b.listeners = { click : cb.createDelegate(this) };
4725 var btn = Roo.factory(b);
4727 btn.render(this.getButtonContainer());
4733 setDefaultButton : function(btn)
4735 //this.el.select('.modal-footer').()
4738 resizeTo: function(w,h)
4740 this.dialogEl.setWidth(w);
4742 var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30
4744 this.bodyEl.setHeight(h - diff);
4746 this.fireEvent('resize', this);
4749 setContentSize : function(w, h)
4753 onButtonClick: function(btn,e)
4756 this.fireEvent('btnclick', btn.name, e);
4759 * Set the title of the Dialog
4760 * @param {String} str new Title
4762 setTitle: function(str) {
4763 this.titleEl.dom.innerHTML = str;
4767 * Set the body of the Dialog
4768 * @param {String} str new Title
4770 setBody: function(str) {
4771 this.bodyEl.dom.innerHTML = str;
4774 * Set the body of the Dialog using the template
4775 * @param {Obj} data - apply this data to the template and replace the body contents.
4777 applyBody: function(obj)
4780 Roo.log("Error - using apply Body without a template");
4783 this.tmpl.overwrite(this.bodyEl, obj);
4786 getChildHeight : function(child_nodes)
4790 child_nodes.length == 0
4795 var child_height = 0;
4797 for(var i = 0; i < child_nodes.length; i++) {
4800 * for modal with tabs...
4801 if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803 var layout_childs = child_nodes[i].childNodes;
4805 for(var j = 0; j < layout_childs.length; j++) {
4807 if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809 var layout_body_childs = layout_childs[j].childNodes;
4811 for(var k = 0; k < layout_body_childs.length; k++) {
4813 if(layout_body_childs[k].classList.contains('navbar')) {
4814 child_height += layout_body_childs[k].offsetHeight;
4818 if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824 if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825 child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4840 child_height += child_nodes[i].offsetHeight;
4841 // Roo.log(child_nodes[i].offsetHeight);
4844 return child_height;
4846 toggleHeaderInput : function(is_edit)
4848 if (!this.editableTitle) {
4849 return; // not editable.
4851 if (is_edit && this.is_header_editing) {
4852 return; // already editing..
4856 this.headerEditEl.dom.value = this.title;
4857 this.headerEditEl.removeClass('d-none');
4858 this.headerEditEl.dom.focus();
4859 this.titleEl.addClass('d-none');
4861 this.is_header_editing = true;
4864 // flip back to not editing.
4865 this.title = this.headerEditEl.dom.value;
4866 this.headerEditEl.addClass('d-none');
4867 this.titleEl.removeClass('d-none');
4868 this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869 this.is_header_editing = false;
4870 this.fireEvent('titlechanged', this, this.title);
4879 Roo.apply(Roo.bootstrap.Modal, {
4881 * Button config that displays a single OK button
4890 * Button config that displays Yes and No buttons
4906 * Button config that displays OK and Cancel buttons
4921 * Button config that displays Yes, No and Cancel buttons
4946 * messagebox - can be used as a replace
4950 * @class Roo.MessageBox
4951 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960 // process text value...
4964 // Show a dialog using config options:
4966 title:'Save Changes?',
4967 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968 buttons: Roo.Msg.YESNOCANCEL,
4975 Roo.bootstrap.MessageBox = function(){
4976 var dlg, opt, mask, waitTimer;
4977 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978 var buttons, activeTextEl, bwidth;
4982 var handleButton = function(button){
4984 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4988 var handleHide = function(){
4990 dlg.el.removeClass(opt.cls);
4993 // Roo.TaskMgr.stop(waitTimer);
4994 // waitTimer = null;
4999 var updateButtons = function(b){
5002 buttons["ok"].hide();
5003 buttons["cancel"].hide();
5004 buttons["yes"].hide();
5005 buttons["no"].hide();
5006 dlg.footerEl.hide();
5010 dlg.footerEl.show();
5011 for(var k in buttons){
5012 if(typeof buttons[k] != "function"){
5015 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016 width += buttons[k].el.getWidth()+15;
5026 var handleEsc = function(d, k, e){
5027 if(opt && opt.closable !== false){
5037 * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038 * @return {Roo.BasicDialog} The BasicDialog element
5040 getDialog : function(){
5042 dlg = new Roo.bootstrap.Modal( {
5045 //constraintoviewport:false,
5047 //collapsible : false,
5052 //buttonAlign:"center",
5053 closeClick : function(){
5054 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5057 handleButton("cancel");
5062 dlg.on("hide", handleHide);
5064 //dlg.addKeyListener(27, handleEsc);
5066 this.buttons = buttons;
5067 var bt = this.buttonText;
5068 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073 bodyEl = dlg.bodyEl.createChild({
5075 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076 '<textarea class="roo-mb-textarea"></textarea>' +
5077 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
5079 msgEl = bodyEl.dom.firstChild;
5080 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081 textboxEl.enableDisplayMode();
5082 textboxEl.addKeyListener([10,13], function(){
5083 if(dlg.isVisible() && opt && opt.buttons){
5086 }else if(opt.buttons.yes){
5087 handleButton("yes");
5091 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092 textareaEl.enableDisplayMode();
5093 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094 progressEl.enableDisplayMode();
5096 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097 var pf = progressEl.dom.firstChild;
5099 pp = Roo.get(pf.firstChild);
5100 pp.setHeight(pf.offsetHeight);
5108 * Updates the message box body text
5109 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110 * the XHTML-compliant non-breaking space character '&#160;')
5111 * @return {Roo.MessageBox} This message box
5113 updateText : function(text)
5115 if(!dlg.isVisible() && !opt.width){
5116 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119 msgEl.innerHTML = text || ' ';
5121 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124 Math.min(opt.width || cw , this.maxWidth),
5125 Math.max(opt.minWidth || this.minWidth, bwidth)
5128 activeTextEl.setWidth(w);
5130 if(dlg.isVisible()){
5131 dlg.fixedcenter = false;
5133 // to big, make it scroll. = But as usual stupid IE does not support
5136 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140 bodyEl.dom.style.height = '';
5141 bodyEl.dom.style.overflowY = '';
5144 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146 bodyEl.dom.style.overflowX = '';
5149 dlg.setContentSize(w, bodyEl.getHeight());
5150 if(dlg.isVisible()){
5151 dlg.fixedcenter = true;
5157 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
5158 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159 * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161 * @return {Roo.MessageBox} This message box
5163 updateProgress : function(value, text){
5165 this.updateText(text);
5168 if (pp) { // weird bug on my firefox - for some reason this is not defined
5169 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5176 * Returns true if the message box is currently displayed
5177 * @return {Boolean} True if the message box is visible, else false
5179 isVisible : function(){
5180 return dlg && dlg.isVisible();
5184 * Hides the message box if it is displayed
5187 if(this.isVisible()){
5193 * Displays a new message box, or reinitializes an existing message box, based on the config options
5194 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195 * The following config object properties are supported:
5197 Property Type Description
5198 ---------- --------------- ------------------------------------------------------------------------------------
5199 animEl String/Element An id or Element from which the message box should animate as it opens and
5200 closes (defaults to undefined)
5201 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202 cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable Boolean False to hide the top-right close button (defaults to true). Note that
5204 progress and wait dialogs will ignore this property and always hide the
5205 close button as they can only be closed programmatically.
5206 cls String A custom CSS class to apply to the message box element
5207 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
5208 displayed (defaults to 75)
5209 fn Function A callback function to execute after closing the dialog. The arguments to the
5210 function will be btn (the name of the button that was clicked, if applicable,
5211 e.g. "ok"), and text (the value of the active text field, if applicable).
5212 Progress and wait dialogs will ignore this option since they do not respond to
5213 user actions and can only be closed programmatically, so any required function
5214 should be called by the same code after it closes the dialog.
5215 icon String A CSS class that provides a background image to be used as an icon for
5216 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
5218 minWidth Number The minimum width in pixels of the message box (defaults to 100)
5219 modal Boolean False to allow user interaction with the page while the message box is
5220 displayed (defaults to true)
5221 msg String A string that will replace the existing message box body text (defaults
5222 to the XHTML-compliant non-breaking space character ' ')
5223 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
5224 progress Boolean True to display a progress bar (defaults to false)
5225 progressText String The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
5228 title String The title text
5229 value String The string value to set into the active textbox element if displayed
5230 wait Boolean True to display a progress bar (defaults to false)
5231 width Number The width of the dialog in pixels
5238 msg: 'Please enter your address:',
5240 buttons: Roo.MessageBox.OKCANCEL,
5243 animEl: 'addAddressBtn'
5246 * @param {Object} config Configuration options
5247 * @return {Roo.MessageBox} This message box
5249 show : function(options)
5252 // this causes nightmares if you show one dialog after another
5253 // especially on callbacks..
5255 if(this.isVisible()){
5258 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
5260 Roo.log("New Dialog Message:" + options.msg )
5261 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5265 var d = this.getDialog();
5267 d.setTitle(opt.title || " ");
5268 d.closeEl.setDisplayed(opt.closable !== false);
5269 activeTextEl = textboxEl;
5270 opt.prompt = opt.prompt || (opt.multiline ? true : false);
5275 textareaEl.setHeight(typeof opt.multiline == "number" ?
5276 opt.multiline : this.defaultTextHeight);
5277 activeTextEl = textareaEl;
5286 progressEl.setDisplayed(opt.progress === true);
5288 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290 this.updateProgress(0);
5291 activeTextEl.dom.value = opt.value || "";
5293 dlg.setDefaultButton(activeTextEl);
5295 var bs = opt.buttons;
5299 }else if(bs && bs.yes){
5300 db = buttons["yes"];
5302 dlg.setDefaultButton(db);
5304 bwidth = updateButtons(opt.buttons);
5305 this.updateText(opt.msg);
5307 d.el.addClass(opt.cls);
5309 d.proxyDrag = opt.proxyDrag === true;
5310 d.modal = opt.modal !== false;
5311 d.mask = opt.modal !== false ? mask : false;
5313 // force it to the end of the z-index stack so it gets a cursor in FF
5314 document.body.appendChild(dlg.el.dom);
5315 d.animateTarget = null;
5316 d.show(options.animEl);
5322 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
5323 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324 * and closing the message box when the process is complete.
5325 * @param {String} title The title bar text
5326 * @param {String} msg The message box body text
5327 * @return {Roo.MessageBox} This message box
5329 progress : function(title, msg){
5336 minWidth: this.minProgressWidth,
5343 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344 * If a callback function is passed it will be called after the user clicks the button, and the
5345 * id of the button that was clicked will be passed as the only parameter to the callback
5346 * (could also be the top-right close button).
5347 * @param {String} title The title bar text
5348 * @param {String} msg The message box body text
5349 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350 * @param {Object} scope (optional) The scope of the callback function
5351 * @return {Roo.MessageBox} This message box
5353 alert : function(title, msg, fn, scope)
5368 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
5369 * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370 * You are responsible for closing the message box when the process is complete.
5371 * @param {String} msg The message box body text
5372 * @param {String} title (optional) The title bar text
5373 * @return {Roo.MessageBox} This message box
5375 wait : function(msg, title){
5386 waitTimer = Roo.TaskMgr.start({
5388 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5396 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397 * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399 * @param {String} title The title bar text
5400 * @param {String} msg The message box body text
5401 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402 * @param {Object} scope (optional) The scope of the callback function
5403 * @return {Roo.MessageBox} This message box
5405 confirm : function(title, msg, fn, scope){
5409 buttons: this.YESNO,
5418 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
5420 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421 * (could also be the top-right close button) and the text that was entered will be passed as the two
5422 * parameters to the callback.
5423 * @param {String} title The title bar text
5424 * @param {String} msg The message box body text
5425 * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426 * @param {Object} scope (optional) The scope of the callback function
5427 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428 * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429 * @return {Roo.MessageBox} This message box
5431 prompt : function(title, msg, fn, scope, multiline){
5435 buttons: this.OKCANCEL,
5440 multiline: multiline,
5447 * Button config that displays a single OK button
5452 * Button config that displays Yes and No buttons
5455 YESNO : {yes:true, no:true},
5457 * Button config that displays OK and Cancel buttons
5460 OKCANCEL : {ok:true, cancel:true},
5462 * Button config that displays Yes, No and Cancel buttons
5465 YESNOCANCEL : {yes:true, no:true, cancel:true},
5468 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5471 defaultTextHeight : 75,
5473 * The maximum width in pixels of the message box (defaults to 600)
5478 * The minimum width in pixels of the message box (defaults to 100)
5483 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
5484 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5487 minProgressWidth : 250,
5489 * An object containing the default button text strings that can be overriden for localized language support.
5490 * Supported properties are: ok, cancel, yes and no.
5491 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5504 * Shorthand for {@link Roo.MessageBox}
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5516 * @class Roo.bootstrap.Navbar
5517 * @extends Roo.bootstrap.Component
5518 * Bootstrap Navbar class
5521 * Create a new Navbar
5522 * @param {Object} config The config object
5526 Roo.bootstrap.Navbar = function(config){
5527 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5531 * @event beforetoggle
5532 * Fire before toggle the menu
5533 * @param {Roo.EventObject} e
5535 "beforetoggle" : true
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
5548 getAutoCreate : function(){
5551 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5555 initEvents :function ()
5557 //Roo.log(this.el.select('.navbar-toggle',true));
5558 this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5565 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567 var size = this.el.getSize();
5568 this.maskEl.setSize(size.width, size.height);
5569 this.maskEl.enableDisplayMode("block");
5578 getChildContainer : function()
5580 if (this.el && this.el.select('.collapse').getCount()) {
5581 return this.el.select('.collapse',true).first();
5596 onToggle : function()
5599 if(this.fireEvent('beforetoggle', this) === false){
5602 var ce = this.el.select('.navbar-collapse',true).first();
5604 if (!ce.hasClass('show')) {
5614 * Expand the navbar pulldown
5616 expand : function ()
5619 var ce = this.el.select('.navbar-collapse',true).first();
5620 if (ce.hasClass('collapsing')) {
5623 ce.dom.style.height = '';
5625 ce.addClass('in'); // old...
5626 ce.removeClass('collapse');
5627 ce.addClass('show');
5628 var h = ce.getHeight();
5630 ce.removeClass('show');
5631 // at this point we should be able to see it..
5632 ce.addClass('collapsing');
5634 ce.setHeight(0); // resize it ...
5635 ce.on('transitionend', function() {
5636 //Roo.log('done transition');
5637 ce.removeClass('collapsing');
5638 ce.addClass('show');
5639 ce.removeClass('collapse');
5641 ce.dom.style.height = '';
5642 }, this, { single: true} );
5644 ce.dom.scrollTop = 0;
5647 * Collapse the navbar pulldown
5649 collapse : function()
5651 var ce = this.el.select('.navbar-collapse',true).first();
5653 if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654 // it's collapsed or collapsing..
5657 ce.removeClass('in'); // old...
5658 ce.setHeight(ce.getHeight());
5659 ce.removeClass('show');
5660 ce.addClass('collapsing');
5662 ce.on('transitionend', function() {
5663 ce.dom.style.height = '';
5664 ce.removeClass('collapsing');
5665 ce.addClass('collapse');
5666 }, this, { single: true} );
5686 * @class Roo.bootstrap.NavSimplebar
5687 * @extends Roo.bootstrap.Navbar
5688 * Bootstrap Sidebar class
5690 * @cfg {Boolean} inverse is inverted color
5692 * @cfg {String} type (nav | pills | tabs)
5693 * @cfg {Boolean} arrangement stacked | justified
5694 * @cfg {String} align (left | right) alignment
5696 * @cfg {Boolean} main (true|false) main nav bar? default false
5697 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699 * @cfg {String} tag (header|footer|nav|div) default is nav
5701 * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5705 * Create a new Sidebar
5706 * @param {Object} config The config object
5710 Roo.bootstrap.NavSimplebar = function(config){
5711 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
5730 getAutoCreate : function(){
5734 tag : this.tag || 'div',
5735 cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737 if (['light','white'].indexOf(this.weight) > -1) {
5738 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740 cfg.cls += ' bg-' + this.weight;
5743 cfg.cls += ' navbar-inverse';
5747 // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749 if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5758 cls: 'nav nav-' + this.xtype,
5764 this.type = this.type || 'nav';
5765 if (['tabs','pills'].indexOf(this.type) != -1) {
5766 cfg.cn[0].cls += ' nav-' + this.type
5770 if (this.type!=='nav') {
5771 Roo.log('nav type must be nav/tabs/pills')
5773 cfg.cn[0].cls += ' navbar-nav'
5779 if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780 cfg.cn[0].cls += ' nav-' + this.arrangement;
5784 if (this.align === 'right') {
5785 cfg.cn[0].cls += ' navbar-right';
5810 * navbar-expand-md fixed-top
5814 * @class Roo.bootstrap.NavHeaderbar
5815 * @extends Roo.bootstrap.NavSimplebar
5816 * Bootstrap Sidebar class
5818 * @cfg {String} brand what is brand
5819 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820 * @cfg {String} brand_href href of the brand
5821 * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button default true
5822 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823 * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824 * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5827 * Create a new Sidebar
5828 * @param {Object} config The config object
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
5844 desktopCenter : false,
5847 getAutoCreate : function(){
5850 tag: this.nav || 'nav',
5851 cls: 'navbar navbar-expand-md',
5857 if (this.desktopCenter) {
5858 cn.push({cls : 'container', cn : []});
5866 cls: 'navbar-toggle navbar-toggler',
5867 'data-toggle': 'collapse',
5872 html: 'Toggle navigation'
5876 cls: 'icon-bar navbar-toggler-icon'
5889 cn.push( Roo.bootstrap.version == 4 ? btn : {
5891 cls: 'navbar-header',
5900 cls: Roo.bootstrap.version == 4 ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5904 cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906 if (['light','white'].indexOf(this.weight) > -1) {
5907 cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909 cfg.cls += ' bg-' + this.weight;
5912 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913 cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915 // tag can override this..
5917 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
5920 if (this.brand !== '') {
5921 var cp = Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922 cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924 href: this.brand_href ? this.brand_href : '#',
5925 cls: 'navbar-brand',
5933 cfg.cls += ' main-nav';
5941 getHeaderChildContainer : function()
5943 if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944 return this.el.select('.navbar-header',true).first();
5947 return this.getChildContainer();
5950 getChildContainer : function()
5953 return this.el.select('.roo-navbar-collapse',true).first();
5958 initEvents : function()
5960 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962 if (this.autohide) {
5967 Roo.get(document).on('scroll',function(e) {
5968 var ns = Roo.get(document).getScroll().top;
5969 var os = prevScroll;
5973 ft.removeClass('slideDown');
5974 ft.addClass('slideUp');
5977 ft.removeClass('slideUp');
5978 ft.addClass('slideDown');
5999 * @class Roo.bootstrap.NavSidebar
6000 * @extends Roo.bootstrap.Navbar
6001 * Bootstrap Sidebar class
6004 * Create a new Sidebar
6005 * @param {Object} config The config object
6009 Roo.bootstrap.NavSidebar = function(config){
6010 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
6015 sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017 getAutoCreate : function(){
6022 cls: 'sidebar sidebar-nav'
6044 * @class Roo.bootstrap.NavGroup
6045 * @extends Roo.bootstrap.Component
6046 * Bootstrap NavGroup class
6047 * @cfg {String} align (left|right)
6048 * @cfg {Boolean} inverse
6049 * @cfg {String} type (nav|pills|tab) default nav
6050 * @cfg {String} navId - reference Id for navbar.
6051 * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6054 * Create a new nav group
6055 * @param {Object} config The config object
6058 Roo.bootstrap.NavGroup = function(config){
6059 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6062 Roo.bootstrap.NavGroup.register(this);
6066 * Fires when the active item changes
6067 * @param {Roo.bootstrap.NavGroup} this
6068 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
6088 getAutoCreate : function()
6090 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6096 if (Roo.bootstrap.version == 4) {
6097 if (['tabs','pills'].indexOf(this.type) != -1) {
6098 cfg.cls += ' nav-' + this.type;
6100 // trying to remove so header bar can right align top?
6101 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102 // do not use on header bar...
6103 cfg.cls += ' navbar-nav';
6108 if (['tabs','pills'].indexOf(this.type) != -1) {
6109 cfg.cls += ' nav-' + this.type
6111 if (this.type !== 'nav') {
6112 Roo.log('nav type must be nav/tabs/pills')
6114 cfg.cls += ' navbar-nav'
6118 if (this.parent() && this.parent().sidebar) {
6121 cls: 'dashboard-menu sidebar-menu'
6127 if (this.form === true) {
6130 cls: 'navbar-form form-inline'
6132 //nav navbar-right ml-md-auto
6133 if (this.align === 'right') {
6134 cfg.cls += ' navbar-right ml-md-auto';
6136 cfg.cls += ' navbar-left';
6140 if (this.align === 'right') {
6141 cfg.cls += ' navbar-right ml-md-auto';
6143 cfg.cls += ' mr-auto';
6147 cfg.cls += ' navbar-inverse';
6155 * sets the active Navigation item
6156 * @param {Roo.bootstrap.NavItem} the new current navitem
6158 setActiveItem : function(item)
6161 Roo.each(this.navItems, function(v){
6166 v.setActive(false, true);
6173 item.setActive(true, true);
6174 this.fireEvent('changed', this, item, prev);
6179 * gets the active Navigation item
6180 * @return {Roo.bootstrap.NavItem} the current navitem
6182 getActive : function()
6186 Roo.each(this.navItems, function(v){
6197 indexOfNav : function()
6201 Roo.each(this.navItems, function(v,i){
6212 * adds a Navigation item
6213 * @param {Roo.bootstrap.NavItem} the navitem to add
6215 addItem : function(cfg)
6217 if (this.form && Roo.bootstrap.version == 4) {
6220 var cn = new Roo.bootstrap.NavItem(cfg);
6222 cn.parentId = this.id;
6223 cn.onRender(this.el, null);
6227 * register a Navigation item
6228 * @param {Roo.bootstrap.NavItem} the navitem to add
6230 register : function(item)
6232 this.navItems.push( item);
6233 item.navId = this.navId;
6238 * clear all the Navigation item
6241 clearAll : function()
6244 this.el.dom.innerHTML = '';
6247 getNavItem: function(tabId)
6250 Roo.each(this.navItems, function(e) {
6251 if (e.tabId == tabId) {
6261 setActiveNext : function()
6263 var i = this.indexOfNav(this.getActive());
6264 if (i > this.navItems.length) {
6267 this.setActiveItem(this.navItems[i+1]);
6269 setActivePrev : function()
6271 var i = this.indexOfNav(this.getActive());
6275 this.setActiveItem(this.navItems[i-1]);
6277 clearWasActive : function(except) {
6278 Roo.each(this.navItems, function(e) {
6279 if (e.tabId != except.tabId && e.was_active) {
6280 e.was_active = false;
6287 getWasActive : function ()
6290 Roo.each(this.navItems, function(e) {
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6309 * register a Navigation Group
6310 * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312 register : function(navgrp)
6314 this.groups[navgrp.navId] = navgrp;
6318 * fetch a Navigation Group based on the navigation ID
6319 * @param {string} the navgroup to add
6320 * @returns {Roo.bootstrap.NavGroup} the navgroup
6322 get: function(navId) {
6323 if (typeof(this.groups[navId]) == 'undefined') {
6325 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327 return this.groups[navId] ;
6342 * @class Roo.bootstrap.NavItem
6343 * @extends Roo.bootstrap.Component
6344 * Bootstrap Navbar.NavItem class
6345 * @cfg {String} href link to
6346 * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347 * @cfg {Boolean} button_outline show and outlined button
6348 * @cfg {String} html content of button
6349 * @cfg {String} badge text inside badge
6350 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351 * @cfg {String} glyphicon DEPRICATED - use fa
6352 * @cfg {String} icon DEPRICATED - use fa
6353 * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354 * @cfg {Boolean} active Is item active
6355 * @cfg {Boolean} disabled Is item disabled
6356 * @cfg {String} linkcls Link Class
6357 * @cfg {Boolean} preventDefault (true | false) default false
6358 * @cfg {String} tabId the tab that this item activates.
6359 * @cfg {String} tagtype (a|span) render as a href or span?
6360 * @cfg {Boolean} animateRef (true|false) link to element default false
6363 * Create a new Navbar Item
6364 * @param {Object} config The config object
6366 Roo.bootstrap.NavItem = function(config){
6367 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6372 * The raw click event for the entire grid.
6373 * @param {Roo.EventObject} e
6378 * Fires when the active item active state changes
6379 * @param {Roo.bootstrap.NavItem} this
6380 * @param {boolean} state the new state
6386 * Fires when scroll to element
6387 * @param {Roo.bootstrap.NavItem} this
6388 * @param {Object} options
6389 * @param {Roo.EventObject} e
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
6406 preventDefault : false,
6414 button_outline : false,
6418 getAutoCreate : function(){
6425 cfg.cls = typeof(cfg.cls) == 'undefined' ? '' : cfg.cls;
6428 cfg.cls += ' active' ;
6430 if (this.disabled) {
6431 cfg.cls += ' disabled';
6435 if (this.button_weight.length) {
6436 cfg.tag = this.href ? 'a' : 'button';
6437 cfg.html = this.html || '';
6438 cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440 cfg.href = this.href;
6443 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445 cfg.cls += " nav-html";
6448 // menu .. should add dropdown-menu class - so no need for carat..
6450 if (this.badge !== '') {
6452 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6457 if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6461 href : this.href || "#",
6462 html: this.html || '',
6466 if (this.tagtype == 'a') {
6467 cfg.cn[0].cls = 'nav-link' + (this.active ? ' active' : '') + ' ' + this.linkcls;
6471 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472 } else if (this.fa) {
6473 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474 } else if(this.glyphicon) {
6475 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
6477 cfg.cn[0].cls += " nav-html";
6481 cfg.cn[0].html += " <span class='caret'></span>";
6485 if (this.badge !== '') {
6486 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6494 onRender : function(ct, position)
6496 // Roo.log("Call onRender: " + this.xtype);
6497 if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6501 var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502 this.navLink = this.el.select('.nav-link',true).first();
6503 this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6508 initEvents: function()
6510 if (typeof (this.menu) != 'undefined') {
6511 this.menu.parentType = this.xtype;
6512 this.menu.triggerEl = this.el;
6513 this.menu = this.addxtype(Roo.apply({}, this.menu));
6516 this.el.on('click', this.onClick, this);
6518 //if(this.tagtype == 'span'){
6519 // this.el.select('span',true).on('click', this.onClick, this);
6522 // at this point parent should be available..
6523 this.parent().register(this);
6526 onClick : function(e)
6528 if (e.getTarget('.dropdown-menu-item')) {
6529 // did you click on a menu itemm.... - then don't trigger onclick..
6534 this.preventDefault ||
6537 Roo.log("NavItem - prevent Default?");
6541 if (this.disabled) {
6545 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546 if (tg && tg.transition) {
6547 Roo.log("waiting for the transitionend");
6553 //Roo.log("fire event clicked");
6554 if(this.fireEvent('click', this, e) === false){
6558 if(this.tagtype == 'span'){
6562 //Roo.log(this.href);
6563 var ael = this.el.select('a',true).first();
6566 if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567 //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568 if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569 return; // ignore... - it's a 'hash' to another page.
6571 Roo.log("NavItem - prevent Default?");
6573 this.scrollToElement(e);
6577 var p = this.parent();
6579 if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580 if (typeof(p.setActiveItem) !== 'undefined') {
6581 p.setActiveItem(this);
6585 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586 if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587 // remove the collapsed menu expand...
6588 p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');
6592 isActive: function () {
6595 setActive : function(state, fire, is_was_active)
6597 if (this.active && !state && this.navId) {
6598 this.was_active = true;
6599 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601 nv.clearWasActive(this);
6605 this.active = state;
6608 this.el.removeClass('active');
6609 this.navLink ? this.navLink.removeClass('active') : false;
6610 } else if (!this.el.hasClass('active')) {
6612 this.el.addClass('active');
6613 if (Roo.bootstrap.version == 4 && this.navLink ) {
6614 this.navLink.addClass('active');
6619 this.fireEvent('changed', this, state);
6622 // show a panel if it's registered and related..
6624 if (!this.navId || !this.tabId || !state || is_was_active) {
6628 var tg = Roo.bootstrap.TabGroup.get(this.navId);
6632 var pan = tg.getPanelByName(this.tabId);
6636 // if we can not flip to new panel - go back to old nav highlight..
6637 if (false == tg.showPanel(pan)) {
6638 var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640 var onav = nv.getWasActive();
6642 onav.setActive(true, false, true);
6651 // this should not be here...
6652 setDisabled : function(state)
6654 this.disabled = state;
6656 this.el.removeClass('disabled');
6657 } else if (!this.el.hasClass('disabled')) {
6658 this.el.addClass('disabled');
6664 * Fetch the element to display the tooltip on.
6665 * @return {Roo.Element} defaults to this.el
6667 tooltipEl : function()
6669 return this.el; //this.tagtype == 'a' ? this.el : this.el.select('' + this.tagtype + '', true).first();
6672 scrollToElement : function(e)
6674 var c = document.body;
6677 * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679 if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680 c = document.documentElement;
6683 var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6689 var o = target.calcOffsetsTo(c);
6696 this.fireEvent('scrollto', this, options, e);
6698 Roo.get(c).scrollTo('top', options.value, true);
6703 * Set the HTML (text content) of the item
6704 * @param {string} html content for the nav item
6706 setHtml : function(html)
6709 this.htmlEl.dom.innerHTML = html;
6721 * <span> icon </span>
6722 * <span> text </span>
6723 * <span>badge </span>
6727 * @class Roo.bootstrap.NavSidebarItem
6728 * @extends Roo.bootstrap.NavItem
6729 * Bootstrap Navbar.NavSidebarItem class
6730 * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731 * {Boolean} open is the menu open
6732 * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733 * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734 * {String} buttonSize (sm|md|lg)the extra classes for the button
6735 * {Boolean} showArrow show arrow next to the text (default true)
6737 * Create a new Navbar Button
6738 * @param {Object} config The config object
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6746 * The raw click event for the entire grid.
6747 * @param {Roo.EventObject} e
6752 * Fires when the active item active state changes
6753 * @param {Roo.bootstrap.NavSidebarItem} this
6754 * @param {boolean} state the new state
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
6764 badgeWeight : 'default',
6770 buttonWeight : 'default',
6776 getAutoCreate : function(){
6781 href : this.href || '#',
6787 if(this.buttonView){
6790 href : this.href || '#',
6791 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6804 cfg.cls += ' active';
6807 if (this.disabled) {
6808 cfg.cls += ' disabled';
6811 cfg.cls += ' open x-open';
6814 if (this.glyphicon || this.icon) {
6815 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
6816 a.cn.push({ tag : 'i', cls : c }) ;
6819 if(!this.buttonView){
6822 html : this.html || ''
6829 if (this.badge !== '') {
6830 a.cn.push({ tag: 'span', cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge });
6836 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6839 a.cls += ' dropdown-toggle treeview' ;
6845 initEvents : function()
6847 if (typeof (this.menu) != 'undefined') {
6848 this.menu.parentType = this.xtype;
6849 this.menu.triggerEl = this.el;
6850 this.menu = this.addxtype(Roo.apply({}, this.menu));
6853 this.el.on('click', this.onClick, this);
6855 if(this.badge !== ''){
6856 this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6861 onClick : function(e)
6868 if(this.preventDefault){
6872 this.fireEvent('click', this, e);
6875 disable : function()
6877 this.setDisabled(true);
6882 this.setDisabled(false);
6885 setDisabled : function(state)
6887 if(this.disabled == state){
6891 this.disabled = state;
6894 this.el.addClass('disabled');
6898 this.el.removeClass('disabled');
6903 setActive : function(state)
6905 if(this.active == state){
6909 this.active = state;
6912 this.el.addClass('active');
6916 this.el.removeClass('active');
6921 isActive: function ()
6926 setBadge : function(str)
6932 this.badgeEl.dom.innerHTML = str;
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6951 * @class Roo.bootstrap.breadcrumb.Nav
6952 * @extends Roo.bootstrap.Component
6953 * Bootstrap Breadcrumb Nav Class
6955 * @children Roo.bootstrap.breadcrumb.Item
6958 * Create a new breadcrumb.Nav
6959 * @param {Object} config The config object
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964 Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component, {
6971 getAutoCreate : function()
6988 initEvents: function()
6990 this.olEl = this.el.select('ol',true).first();
6992 getChildContainer : function()
7008 * @class Roo.bootstrap.breadcrumb.Nav
7009 * @extends Roo.bootstrap.Component
7010 * Bootstrap Breadcrumb Nav Class
7012 * @children Roo.bootstrap.breadcrumb.Component
7013 * @cfg {String} html the content of the link.
7014 * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015 * @cfg {Boolean} active is it active
7019 * Create a new breadcrumb.Nav
7020 * @param {Object} config The config object
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024 Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7029 * The img click event for the img.
7030 * @param {Roo.EventObject} e
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component, {
7042 getAutoCreate : function()
7047 cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049 if (this.href !== false) {
7056 cfg.html = this.html;
7062 initEvents: function()
7065 this.el.select('a', true).first().on('click',this.onClick, this)
7069 onClick : function(e)
7072 this.fireEvent('click',this, e);
7085 * @class Roo.bootstrap.Row
7086 * @extends Roo.bootstrap.Component
7087 * Bootstrap Row class (contains columns...)
7091 * @param {Object} config The config object
7094 Roo.bootstrap.Row = function(config){
7095 Roo.bootstrap.Row.superclass.constructor.call(this, config);
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
7100 getAutoCreate : function(){
7119 * @class Roo.bootstrap.Pagination
7120 * @extends Roo.bootstrap.Component
7121 * Bootstrap Pagination class
7122 * @cfg {String} size xs | sm | md | lg
7123 * @cfg {Boolean} inverse false | true
7126 * Create a new Pagination
7127 * @param {Object} config The config object
7130 Roo.bootstrap.Pagination = function(config){
7131 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
7140 getAutoCreate : function(){
7146 cfg.cls += ' inverse';
7152 cfg.cls += " " + this.cls;
7170 * @class Roo.bootstrap.PaginationItem
7171 * @extends Roo.bootstrap.Component
7172 * Bootstrap PaginationItem class
7173 * @cfg {String} html text
7174 * @cfg {String} href the link
7175 * @cfg {Boolean} preventDefault (true | false) default true
7176 * @cfg {Boolean} active (true | false) default false
7177 * @cfg {Boolean} disabled default false
7181 * Create a new PaginationItem
7182 * @param {Object} config The config object
7186 Roo.bootstrap.PaginationItem = function(config){
7187 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7192 * The raw click event for the entire grid.
7193 * @param {Roo.EventObject} e
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
7203 preventDefault: true,
7208 getAutoCreate : function(){
7214 href : this.href ? this.href : '#',
7215 html : this.html ? this.html : ''
7225 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7229 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7235 initEvents: function() {
7237 this.el.on('click', this.onClick, this);
7240 onClick : function(e)
7242 Roo.log('PaginationItem on click ');
7243 if(this.preventDefault){
7251 this.fireEvent('click', this, e);
7267 * @class Roo.bootstrap.Slider
7268 * @extends Roo.bootstrap.Component
7269 * Bootstrap Slider class
7272 * Create a new Slider
7273 * @param {Object} config The config object
7276 Roo.bootstrap.Slider = function(config){
7277 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
7282 getAutoCreate : function(){
7286 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7290 cls: 'ui-slider-handle ui-state-default ui-corner-all'
7302 * Ext JS Library 1.1.1
7303 * Copyright(c) 2006-2007, Ext JS, LLC.
7305 * Originally Released Under LGPL - original licence link has changed is not relivant.
7308 * <script type="text/javascript">
7313 * @class Roo.grid.ColumnModel
7314 * @extends Roo.util.Observable
7315 * This is the default implementation of a ColumnModel used by the Grid. It defines
7316 * the columns in the grid.
7319 var colModel = new Roo.grid.ColumnModel([
7320 {header: "Ticker", width: 60, sortable: true, locked: true},
7321 {header: "Company Name", width: 150, sortable: true},
7322 {header: "Market Cap.", width: 100, sortable: true},
7323 {header: "$ Sales", width: 100, sortable: true, renderer: money},
7324 {header: "Employees", width: 100, sortable: true, resizable: false}
7329 * The config options listed for this class are options which may appear in each
7330 * individual column definition.
7331 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7333 * @param {Object} config An Array of column config objects. See this class's
7334 * config objects for details.
7336 Roo.grid.ColumnModel = function(config){
7338 * The config passed into the constructor
7340 this.config = []; //config;
7343 // if no id, create one
7344 // if the column does not have a dataIndex mapping,
7345 // map it to the order it is in the config
7346 for(var i = 0, len = config.length; i < len; i++){
7347 this.addColumn(config[i]);
7352 * The width of columns which have no width specified (defaults to 100)
7355 this.defaultWidth = 100;
7358 * Default sortable of columns which have no sortable specified (defaults to false)
7361 this.defaultSortable = false;
7365 * @event widthchange
7366 * Fires when the width of a column changes.
7367 * @param {ColumnModel} this
7368 * @param {Number} columnIndex The column index
7369 * @param {Number} newWidth The new width
7371 "widthchange": true,
7373 * @event headerchange
7374 * Fires when the text of a header changes.
7375 * @param {ColumnModel} this
7376 * @param {Number} columnIndex The column index
7377 * @param {Number} newText The new header text
7379 "headerchange": true,
7381 * @event hiddenchange
7382 * Fires when a column is hidden or "unhidden".
7383 * @param {ColumnModel} this
7384 * @param {Number} columnIndex The column index
7385 * @param {Boolean} hidden true if hidden, false otherwise
7387 "hiddenchange": true,
7389 * @event columnmoved
7390 * Fires when a column is moved.
7391 * @param {ColumnModel} this
7392 * @param {Number} oldIndex
7393 * @param {Number} newIndex
7395 "columnmoved" : true,
7397 * @event columlockchange
7398 * Fires when a column's locked state is changed
7399 * @param {ColumnModel} this
7400 * @param {Number} colIndex
7401 * @param {Boolean} locked true if locked
7403 "columnlockchange" : true
7405 Roo.grid.ColumnModel.superclass.constructor.call(this);
7407 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7409 * @cfg {String} header The header text to display in the Grid view.
7412 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7413 * {@link Roo.data.Record} definition from which to draw the column's value. If not
7414 * specified, the column's index is used as an index into the Record's data Array.
7417 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7418 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7421 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7422 * Defaults to the value of the {@link #defaultSortable} property.
7423 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7426 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
7429 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
7432 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7435 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7438 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7439 * given the cell's data value. See {@link #setRenderer}. If not specified, the
7440 * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7441 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7444 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
7447 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
7450 * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc). Defaults to undefined.
7453 * @cfg {String} cursor (Optional)
7456 * @cfg {String} tooltip (Optional)
7459 * @cfg {Number} xs (Optional)
7462 * @cfg {Number} sm (Optional)
7465 * @cfg {Number} md (Optional)
7468 * @cfg {Number} lg (Optional)
7471 * Returns the id of the column at the specified index.
7472 * @param {Number} index The column index
7473 * @return {String} the id
7475 getColumnId : function(index){
7476 return this.config[index].id;
7480 * Returns the column for a specified id.
7481 * @param {String} id The column id
7482 * @return {Object} the column
7484 getColumnById : function(id){
7485 return this.lookup[id];
7490 * Returns the column Object for a specified dataIndex.
7491 * @param {String} dataIndex The column dataIndex
7492 * @return {Object|Boolean} the column or false if not found
7494 getColumnByDataIndex: function(dataIndex){
7495 var index = this.findColumnIndex(dataIndex);
7496 return index > -1 ? this.config[index] : false;
7500 * Returns the index for a specified column id.
7501 * @param {String} id The column id
7502 * @return {Number} the index, or -1 if not found
7504 getIndexById : function(id){
7505 for(var i = 0, len = this.config.length; i < len; i++){
7506 if(this.config[i].id == id){
7514 * Returns the index for a specified column dataIndex.
7515 * @param {String} dataIndex The column dataIndex
7516 * @return {Number} the index, or -1 if not found
7519 findColumnIndex : function(dataIndex){
7520 for(var i = 0, len = this.config.length; i < len; i++){
7521 if(this.config[i].dataIndex == dataIndex){
7529 moveColumn : function(oldIndex, newIndex){
7530 var c = this.config[oldIndex];
7531 this.config.splice(oldIndex, 1);
7532 this.config.splice(newIndex, 0, c);
7533 this.dataMap = null;
7534 this.fireEvent("columnmoved", this, oldIndex, newIndex);
7537 isLocked : function(colIndex){
7538 return this.config[colIndex].locked === true;
7541 setLocked : function(colIndex, value, suppressEvent){
7542 if(this.isLocked(colIndex) == value){
7545 this.config[colIndex].locked = value;
7547 this.fireEvent("columnlockchange", this, colIndex, value);
7551 getTotalLockedWidth : function(){
7553 for(var i = 0; i < this.config.length; i++){
7554 if(this.isLocked(i) && !this.isHidden(i)){
7555 this.totalWidth += this.getColumnWidth(i);
7561 getLockedCount : function(){
7562 for(var i = 0, len = this.config.length; i < len; i++){
7563 if(!this.isLocked(i)){
7568 return this.config.length;
7572 * Returns the number of columns.
7575 getColumnCount : function(visibleOnly){
7576 if(visibleOnly === true){
7578 for(var i = 0, len = this.config.length; i < len; i++){
7579 if(!this.isHidden(i)){
7585 return this.config.length;
7589 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7590 * @param {Function} fn
7591 * @param {Object} scope (optional)
7592 * @return {Array} result
7594 getColumnsBy : function(fn, scope){
7596 for(var i = 0, len = this.config.length; i < len; i++){
7597 var c = this.config[i];
7598 if(fn.call(scope||this, c, i) === true){
7606 * Returns true if the specified column is sortable.
7607 * @param {Number} col The column index
7610 isSortable : function(col){
7611 if(typeof this.config[col].sortable == "undefined"){
7612 return this.defaultSortable;
7614 return this.config[col].sortable;
7618 * Returns the rendering (formatting) function defined for the column.
7619 * @param {Number} col The column index.
7620 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7622 getRenderer : function(col){
7623 if(!this.config[col].renderer){
7624 return Roo.grid.ColumnModel.defaultRenderer;
7626 return this.config[col].renderer;
7630 * Sets the rendering (formatting) function for a column.
7631 * @param {Number} col The column index
7632 * @param {Function} fn The function to use to process the cell's raw data
7633 * to return HTML markup for the grid view. The render function is called with
7634 * the following parameters:<ul>
7635 * <li>Data value.</li>
7636 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7637 * <li>css A CSS style string to apply to the table cell.</li>
7638 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7639 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7640 * <li>Row index</li>
7641 * <li>Column index</li>
7642 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7644 setRenderer : function(col, fn){
7645 this.config[col].renderer = fn;
7649 * Returns the width for the specified column.
7650 * @param {Number} col The column index
7653 getColumnWidth : function(col){
7654 return this.config[col].width * 1 || this.defaultWidth;
7658 * Sets the width for a column.
7659 * @param {Number} col The column index
7660 * @param {Number} width The new width
7662 setColumnWidth : function(col, width, suppressEvent){
7663 this.config[col].width = width;
7664 this.totalWidth = null;
7666 this.fireEvent("widthchange", this, col, width);
7671 * Returns the total width of all columns.
7672 * @param {Boolean} includeHidden True to include hidden column widths
7675 getTotalWidth : function(includeHidden){
7676 if(!this.totalWidth){
7677 this.totalWidth = 0;
7678 for(var i = 0, len = this.config.length; i < len; i++){
7679 if(includeHidden || !this.isHidden(i)){
7680 this.totalWidth += this.getColumnWidth(i);
7684 return this.totalWidth;
7688 * Returns the header for the specified column.
7689 * @param {Number} col The column index
7692 getColumnHeader : function(col){
7693 return this.config[col].header;
7697 * Sets the header for a column.
7698 * @param {Number} col The column index
7699 * @param {String} header The new header
7701 setColumnHeader : function(col, header){
7702 this.config[col].header = header;
7703 this.fireEvent("headerchange", this, col, header);
7707 * Returns the tooltip for the specified column.
7708 * @param {Number} col The column index
7711 getColumnTooltip : function(col){
7712 return this.config[col].tooltip;
7715 * Sets the tooltip for a column.
7716 * @param {Number} col The column index
7717 * @param {String} tooltip The new tooltip
7719 setColumnTooltip : function(col, tooltip){
7720 this.config[col].tooltip = tooltip;
7724 * Returns the dataIndex for the specified column.
7725 * @param {Number} col The column index
7728 getDataIndex : function(col){
7729 return this.config[col].dataIndex;
7733 * Sets the dataIndex for a column.
7734 * @param {Number} col The column index
7735 * @param {Number} dataIndex The new dataIndex
7737 setDataIndex : function(col, dataIndex){
7738 this.config[col].dataIndex = dataIndex;
7744 * Returns true if the cell is editable.
7745 * @param {Number} colIndex The column index
7746 * @param {Number} rowIndex The row index - this is nto actually used..?
7749 isCellEditable : function(colIndex, rowIndex){
7750 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7754 * Returns the editor defined for the cell/column.
7755 * return false or null to disable editing.
7756 * @param {Number} colIndex The column index
7757 * @param {Number} rowIndex The row index
7760 getCellEditor : function(colIndex, rowIndex){
7761 return this.config[colIndex].editor;
7765 * Sets if a column is editable.
7766 * @param {Number} col The column index
7767 * @param {Boolean} editable True if the column is editable
7769 setEditable : function(col, editable){
7770 this.config[col].editable = editable;
7775 * Returns true if the column is hidden.
7776 * @param {Number} colIndex The column index
7779 isHidden : function(colIndex){
7780 return this.config[colIndex].hidden;
7785 * Returns true if the column width cannot be changed
7787 isFixed : function(colIndex){
7788 return this.config[colIndex].fixed;
7792 * Returns true if the column can be resized
7795 isResizable : function(colIndex){
7796 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7799 * Sets if a column is hidden.
7800 * @param {Number} colIndex The column index
7801 * @param {Boolean} hidden True if the column is hidden
7803 setHidden : function(colIndex, hidden){
7804 this.config[colIndex].hidden = hidden;
7805 this.totalWidth = null;
7806 this.fireEvent("hiddenchange", this, colIndex, hidden);
7810 * Sets the editor for a column.
7811 * @param {Number} col The column index
7812 * @param {Object} editor The editor object
7814 setEditor : function(col, editor){
7815 this.config[col].editor = editor;
7818 * Add a column (experimental...) - defaults to adding to the end..
7819 * @param {Object} config
7821 addColumn : function(c)
7824 var i = this.config.length;
7827 if(typeof c.dataIndex == "undefined"){
7830 if(typeof c.renderer == "string"){
7831 c.renderer = Roo.util.Format[c.renderer];
7833 if(typeof c.id == "undefined"){
7836 if(c.editor && c.editor.xtype){
7837 c.editor = Roo.factory(c.editor, Roo.grid);
7839 if(c.editor && c.editor.isFormField){
7840 c.editor = new Roo.grid.GridEditor(c.editor);
7842 this.lookup[c.id] = c;
7847 Roo.grid.ColumnModel.defaultRenderer = function(value)
7849 if(typeof value == "object") {
7852 if(typeof value == "string" && value.length < 1){
7856 return String.format("{0}", value);
7859 // Alias for backwards compatibility
7860 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7863 * Ext JS Library 1.1.1
7864 * Copyright(c) 2006-2007, Ext JS, LLC.
7866 * Originally Released Under LGPL - original licence link has changed is not relivant.
7869 * <script type="text/javascript">
7873 * @class Roo.LoadMask
7874 * A simple utility class for generically masking elements while loading data. If the element being masked has
7875 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7876 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
7877 * element's UpdateManager load indicator and will be destroyed after the initial load.
7879 * Create a new LoadMask
7880 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7881 * @param {Object} config The config object
7883 Roo.LoadMask = function(el, config){
7884 this.el = Roo.get(el);
7885 Roo.apply(this, config);
7887 this.store.on('beforeload', this.onBeforeLoad, this);
7888 this.store.on('load', this.onLoad, this);
7889 this.store.on('loadexception', this.onLoadException, this);
7890 this.removeMask = false;
7892 var um = this.el.getUpdateManager();
7893 um.showLoadIndicator = false; // disable the default indicator
7894 um.on('beforeupdate', this.onBeforeLoad, this);
7895 um.on('update', this.onLoad, this);
7896 um.on('failure', this.onLoad, this);
7897 this.removeMask = true;
7901 Roo.LoadMask.prototype = {
7903 * @cfg {Boolean} removeMask
7904 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7905 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
7909 * The text to display in a centered loading message box (defaults to 'Loading...')
7913 * @cfg {String} msgCls
7914 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7916 msgCls : 'x-mask-loading',
7919 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7925 * Disables the mask to prevent it from being displayed
7927 disable : function(){
7928 this.disabled = true;
7932 * Enables the mask so that it can be displayed
7934 enable : function(){
7935 this.disabled = false;
7938 onLoadException : function()
7942 if (typeof(arguments[3]) != 'undefined') {
7943 Roo.MessageBox.alert("Error loading",arguments[3]);
7947 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7948 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7955 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7960 (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7964 onBeforeLoad : function(){
7966 (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7971 destroy : function(){
7973 this.store.un('beforeload', this.onBeforeLoad, this);
7974 this.store.un('load', this.onLoad, this);
7975 this.store.un('loadexception', this.onLoadException, this);
7977 var um = this.el.getUpdateManager();
7978 um.un('beforeupdate', this.onBeforeLoad, this);
7979 um.un('update', this.onLoad, this);
7980 um.un('failure', this.onLoad, this);
7991 * @class Roo.bootstrap.Table
7992 * @extends Roo.bootstrap.Component
7993 * Bootstrap Table class
7994 * @cfg {String} cls table class
7995 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7996 * @cfg {String} bgcolor Specifies the background color for a table
7997 * @cfg {Number} border Specifies whether the table cells should have borders or not
7998 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7999 * @cfg {Number} cellspacing Specifies the space between cells
8000 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
8001 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
8002 * @cfg {String} sortable Specifies that the table should be sortable
8003 * @cfg {String} summary Specifies a summary of the content of a table
8004 * @cfg {Number} width Specifies the width of a table
8005 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
8007 * @cfg {boolean} striped Should the rows be alternative striped
8008 * @cfg {boolean} bordered Add borders to the table
8009 * @cfg {boolean} hover Add hover highlighting
8010 * @cfg {boolean} condensed Format condensed
8011 * @cfg {boolean} responsive Format condensed
8012 * @cfg {Boolean} loadMask (true|false) default false
8013 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8014 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8015 * @cfg {Boolean} rowSelection (true|false) default false
8016 * @cfg {Boolean} cellSelection (true|false) default false
8017 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8018 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8019 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8020 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8024 * Create a new Table
8025 * @param {Object} config The config object
8028 Roo.bootstrap.Table = function(config){
8029 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8034 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8035 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8036 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8037 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8039 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8041 this.sm.grid = this;
8042 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8043 this.sm = this.selModel;
8044 this.sm.xmodule = this.xmodule || false;
8047 if (this.cm && typeof(this.cm.config) == 'undefined') {
8048 this.colModel = new Roo.grid.ColumnModel(this.cm);
8049 this.cm = this.colModel;
8050 this.cm.xmodule = this.xmodule || false;
8053 this.store= Roo.factory(this.store, Roo.data);
8054 this.ds = this.store;
8055 this.ds.xmodule = this.xmodule || false;
8058 if (this.footer && this.store) {
8059 this.footer.dataSource = this.ds;
8060 this.footer = Roo.factory(this.footer);
8067 * Fires when a cell is clicked
8068 * @param {Roo.bootstrap.Table} this
8069 * @param {Roo.Element} el
8070 * @param {Number} rowIndex
8071 * @param {Number} columnIndex
8072 * @param {Roo.EventObject} e
8076 * @event celldblclick
8077 * Fires when a cell is double clicked
8078 * @param {Roo.bootstrap.Table} this
8079 * @param {Roo.Element} el
8080 * @param {Number} rowIndex
8081 * @param {Number} columnIndex
8082 * @param {Roo.EventObject} e
8084 "celldblclick" : true,
8087 * Fires when a row is clicked
8088 * @param {Roo.bootstrap.Table} this
8089 * @param {Roo.Element} el
8090 * @param {Number} rowIndex
8091 * @param {Roo.EventObject} e
8095 * @event rowdblclick
8096 * Fires when a row is double clicked
8097 * @param {Roo.bootstrap.Table} this
8098 * @param {Roo.Element} el
8099 * @param {Number} rowIndex
8100 * @param {Roo.EventObject} e
8102 "rowdblclick" : true,
8105 * Fires when a mouseover occur
8106 * @param {Roo.bootstrap.Table} this
8107 * @param {Roo.Element} el
8108 * @param {Number} rowIndex
8109 * @param {Number} columnIndex
8110 * @param {Roo.EventObject} e
8115 * Fires when a mouseout occur
8116 * @param {Roo.bootstrap.Table} this
8117 * @param {Roo.Element} el
8118 * @param {Number} rowIndex
8119 * @param {Number} columnIndex
8120 * @param {Roo.EventObject} e
8125 * Fires when a row is rendered, so you can change add a style to it.
8126 * @param {Roo.bootstrap.Table} this
8127 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8131 * @event rowsrendered
8132 * Fires when all the rows have been rendered
8133 * @param {Roo.bootstrap.Table} this
8135 'rowsrendered' : true,
8137 * @event contextmenu
8138 * The raw contextmenu event for the entire grid.
8139 * @param {Roo.EventObject} e
8141 "contextmenu" : true,
8143 * @event rowcontextmenu
8144 * Fires when a row is right clicked
8145 * @param {Roo.bootstrap.Table} this
8146 * @param {Number} rowIndex
8147 * @param {Roo.EventObject} e
8149 "rowcontextmenu" : true,
8151 * @event cellcontextmenu
8152 * Fires when a cell is right clicked
8153 * @param {Roo.bootstrap.Table} this
8154 * @param {Number} rowIndex
8155 * @param {Number} cellIndex
8156 * @param {Roo.EventObject} e
8158 "cellcontextmenu" : true,
8160 * @event headercontextmenu
8161 * Fires when a header is right clicked
8162 * @param {Roo.bootstrap.Table} this
8163 * @param {Number} columnIndex
8164 * @param {Roo.EventObject} e
8166 "headercontextmenu" : true
8170 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8196 rowSelection : false,
8197 cellSelection : false,
8200 // Roo.Element - the tbody
8202 // Roo.Element - thead element
8205 container: false, // used by gridpanel...
8211 auto_hide_footer : false,
8213 getAutoCreate : function()
8215 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8222 if (this.scrollBody) {
8223 cfg.cls += ' table-body-fixed';
8226 cfg.cls += ' table-striped';
8230 cfg.cls += ' table-hover';
8232 if (this.bordered) {
8233 cfg.cls += ' table-bordered';
8235 if (this.condensed) {
8236 cfg.cls += ' table-condensed';
8238 if (this.responsive) {
8239 cfg.cls += ' table-responsive';
8243 cfg.cls+= ' ' +this.cls;
8246 // this lot should be simplifed...
8259 ].forEach(function(k) {
8267 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8270 if(this.store || this.cm){
8271 if(this.headerShow){
8272 cfg.cn.push(this.renderHeader());
8275 cfg.cn.push(this.renderBody());
8277 if(this.footerShow){
8278 cfg.cn.push(this.renderFooter());
8280 // where does this come from?
8281 //cfg.cls+= ' TableGrid';
8284 return { cn : [ cfg ] };
8287 initEvents : function()
8289 if(!this.store || !this.cm){
8292 if (this.selModel) {
8293 this.selModel.initEvents();
8297 //Roo.log('initEvents with ds!!!!');
8299 this.mainBody = this.el.select('tbody', true).first();
8300 this.mainHead = this.el.select('thead', true).first();
8301 this.mainFoot = this.el.select('tfoot', true).first();
8306 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8307 e.on('click', this.sort, this);
8310 this.mainBody.on("click", this.onClick, this);
8311 this.mainBody.on("dblclick", this.onDblClick, this);
8313 // why is this done????? = it breaks dialogs??
8314 //this.parent().el.setStyle('position', 'relative');
8318 this.footer.parentId = this.id;
8319 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8322 this.el.select('tfoot tr td').first().addClass('hide');
8327 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8330 this.store.on('load', this.onLoad, this);
8331 this.store.on('beforeload', this.onBeforeLoad, this);
8332 this.store.on('update', this.onUpdate, this);
8333 this.store.on('add', this.onAdd, this);
8334 this.store.on("clear", this.clear, this);
8336 this.el.on("contextmenu", this.onContextMenu, this);
8338 this.mainBody.on('scroll', this.onBodyScroll, this);
8340 this.cm.on("headerchange", this.onHeaderChange, this);
8342 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8346 onContextMenu : function(e, t)
8348 this.processEvent("contextmenu", e);
8351 processEvent : function(name, e)
8353 if (name != 'touchstart' ) {
8354 this.fireEvent(name, e);
8357 var t = e.getTarget();
8359 var cell = Roo.get(t);
8365 if(cell.findParent('tfoot', false, true)){
8369 if(cell.findParent('thead', false, true)){
8371 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8372 cell = Roo.get(t).findParent('th', false, true);
8374 Roo.log("failed to find th in thead?");
8375 Roo.log(e.getTarget());
8380 var cellIndex = cell.dom.cellIndex;
8382 var ename = name == 'touchstart' ? 'click' : name;
8383 this.fireEvent("header" + ename, this, cellIndex, e);
8388 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8389 cell = Roo.get(t).findParent('td', false, true);
8391 Roo.log("failed to find th in tbody?");
8392 Roo.log(e.getTarget());
8397 var row = cell.findParent('tr', false, true);
8398 var cellIndex = cell.dom.cellIndex;
8399 var rowIndex = row.dom.rowIndex - 1;
8403 this.fireEvent("row" + name, this, rowIndex, e);
8407 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8413 onMouseover : function(e, el)
8415 var cell = Roo.get(el);
8421 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8422 cell = cell.findParent('td', false, true);
8425 var row = cell.findParent('tr', false, true);
8426 var cellIndex = cell.dom.cellIndex;
8427 var rowIndex = row.dom.rowIndex - 1; // start from 0
8429 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8433 onMouseout : function(e, el)
8435 var cell = Roo.get(el);
8441 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8442 cell = cell.findParent('td', false, true);
8445 var row = cell.findParent('tr', false, true);
8446 var cellIndex = cell.dom.cellIndex;
8447 var rowIndex = row.dom.rowIndex - 1; // start from 0
8449 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8453 onClick : function(e, el)
8455 var cell = Roo.get(el);
8457 if(!cell || (!this.cellSelection && !this.rowSelection)){
8461 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8462 cell = cell.findParent('td', false, true);
8465 if(!cell || typeof(cell) == 'undefined'){
8469 var row = cell.findParent('tr', false, true);
8471 if(!row || typeof(row) == 'undefined'){
8475 var cellIndex = cell.dom.cellIndex;
8476 var rowIndex = this.getRowIndex(row);
8478 // why??? - should these not be based on SelectionModel?
8479 //if(this.cellSelection){
8480 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8483 //if(this.rowSelection){
8484 this.fireEvent('rowclick', this, row, rowIndex, e);
8489 onDblClick : function(e,el)
8491 var cell = Roo.get(el);
8493 if(!cell || (!this.cellSelection && !this.rowSelection)){
8497 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8498 cell = cell.findParent('td', false, true);
8501 if(!cell || typeof(cell) == 'undefined'){
8505 var row = cell.findParent('tr', false, true);
8507 if(!row || typeof(row) == 'undefined'){
8511 var cellIndex = cell.dom.cellIndex;
8512 var rowIndex = this.getRowIndex(row);
8514 if(this.cellSelection){
8515 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8518 if(this.rowSelection){
8519 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8523 sort : function(e,el)
8525 var col = Roo.get(el);
8527 if(!col.hasClass('sortable')){
8531 var sort = col.attr('sort');
8534 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8538 this.store.sortInfo = {field : sort, direction : dir};
8541 Roo.log("calling footer first");
8542 this.footer.onClick('first');
8545 this.store.load({ params : { start : 0 } });
8549 renderHeader : function()
8557 this.totalWidth = 0;
8559 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8561 var config = cm.config[i];
8565 cls : 'x-hcol-' + i,
8568 html: cm.getColumnHeader(i)
8571 var tooltip = cm.getColumnTooltip(i);
8573 c.tooltip = tooltip;
8579 if(typeof(config.sortable) != 'undefined' && config.sortable){
8581 c.html = '<i class="fa"></i>' + c.html;
8584 // could use BS4 hidden-..-down
8586 if(typeof(config.lgHeader) != 'undefined'){
8587 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8590 if(typeof(config.mdHeader) != 'undefined'){
8591 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8594 if(typeof(config.smHeader) != 'undefined'){
8595 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8598 if(typeof(config.xsHeader) != 'undefined'){
8599 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8606 if(typeof(config.tooltip) != 'undefined'){
8607 c.tooltip = config.tooltip;
8610 if(typeof(config.colspan) != 'undefined'){
8611 c.colspan = config.colspan;
8614 if(typeof(config.hidden) != 'undefined' && config.hidden){
8615 c.style += ' display:none;';
8618 if(typeof(config.dataIndex) != 'undefined'){
8619 c.sort = config.dataIndex;
8624 if(typeof(config.align) != 'undefined' && config.align.length){
8625 c.style += ' text-align:' + config.align + ';';
8628 if(typeof(config.width) != 'undefined'){
8629 c.style += ' width:' + config.width + 'px;';
8630 this.totalWidth += config.width;
8632 this.totalWidth += 100; // assume minimum of 100 per column?
8635 if(typeof(config.cls) != 'undefined'){
8636 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8639 ['xs','sm','md','lg'].map(function(size){
8641 if(typeof(config[size]) == 'undefined'){
8645 if (!config[size]) { // 0 = hidden
8646 // BS 4 '0' is treated as hide that column and below.
8647 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8651 c.cls += ' col-' + size + '-' + config[size] + (
8652 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8664 renderBody : function()
8674 colspan : this.cm.getColumnCount()
8684 renderFooter : function()
8694 colspan : this.cm.getColumnCount()
8708 // Roo.log('ds onload');
8713 var ds = this.store;
8715 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8716 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8717 if (_this.store.sortInfo) {
8719 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8720 e.select('i', true).addClass(['fa-arrow-up']);
8723 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8724 e.select('i', true).addClass(['fa-arrow-down']);
8729 var tbody = this.mainBody;
8731 if(ds.getCount() > 0){
8732 ds.data.each(function(d,rowIndex){
8733 var row = this.renderRow(cm, ds, rowIndex);
8735 tbody.createChild(row);
8739 if(row.cellObjects.length){
8740 Roo.each(row.cellObjects, function(r){
8741 _this.renderCellObject(r);
8748 var tfoot = this.el.select('tfoot', true).first();
8750 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8752 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8754 var total = this.ds.getTotalCount();
8756 if(this.footer.pageSize < total){
8757 this.mainFoot.show();
8761 Roo.each(this.el.select('tbody td', true).elements, function(e){
8762 e.on('mouseover', _this.onMouseover, _this);
8765 Roo.each(this.el.select('tbody td', true).elements, function(e){
8766 e.on('mouseout', _this.onMouseout, _this);
8768 this.fireEvent('rowsrendered', this);
8774 onUpdate : function(ds,record)
8776 this.refreshRow(record);
8780 onRemove : function(ds, record, index, isUpdate){
8781 if(isUpdate !== true){
8782 this.fireEvent("beforerowremoved", this, index, record);
8784 var bt = this.mainBody.dom;
8786 var rows = this.el.select('tbody > tr', true).elements;
8788 if(typeof(rows[index]) != 'undefined'){
8789 bt.removeChild(rows[index].dom);
8792 // if(bt.rows[index]){
8793 // bt.removeChild(bt.rows[index]);
8796 if(isUpdate !== true){
8797 //this.stripeRows(index);
8798 //this.syncRowHeights(index, index);
8800 this.fireEvent("rowremoved", this, index, record);
8804 onAdd : function(ds, records, rowIndex)
8806 //Roo.log('on Add called');
8807 // - note this does not handle multiple adding very well..
8808 var bt = this.mainBody.dom;
8809 for (var i =0 ; i < records.length;i++) {
8810 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8811 //Roo.log(records[i]);
8812 //Roo.log(this.store.getAt(rowIndex+i));
8813 this.insertRow(this.store, rowIndex + i, false);
8820 refreshRow : function(record){
8821 var ds = this.store, index;
8822 if(typeof record == 'number'){
8824 record = ds.getAt(index);
8826 index = ds.indexOf(record);
8828 return; // should not happen - but seems to
8831 this.insertRow(ds, index, true);
8833 this.onRemove(ds, record, index+1, true);
8835 //this.syncRowHeights(index, index);
8837 this.fireEvent("rowupdated", this, index, record);
8840 insertRow : function(dm, rowIndex, isUpdate){
8843 this.fireEvent("beforerowsinserted", this, rowIndex);
8845 //var s = this.getScrollState();
8846 var row = this.renderRow(this.cm, this.store, rowIndex);
8847 // insert before rowIndex..
8848 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8852 if(row.cellObjects.length){
8853 Roo.each(row.cellObjects, function(r){
8854 _this.renderCellObject(r);
8859 this.fireEvent("rowsinserted", this, rowIndex);
8860 //this.syncRowHeights(firstRow, lastRow);
8861 //this.stripeRows(firstRow);
8868 getRowDom : function(rowIndex)
8870 var rows = this.el.select('tbody > tr', true).elements;
8872 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8875 // returns the object tree for a tr..
8878 renderRow : function(cm, ds, rowIndex)
8880 var d = ds.getAt(rowIndex);
8884 cls : 'x-row-' + rowIndex,
8888 var cellObjects = [];
8890 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8891 var config = cm.config[i];
8893 var renderer = cm.getRenderer(i);
8897 if(typeof(renderer) !== 'undefined'){
8898 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8900 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8901 // and are rendered into the cells after the row is rendered - using the id for the element.
8903 if(typeof(value) === 'object'){
8913 rowIndex : rowIndex,
8918 this.fireEvent('rowclass', this, rowcfg);
8922 // this might end up displaying HTML?
8923 // this is too messy... - better to only do it on columsn you know are going to be too long
8924 //tooltip : (typeof(value) === 'object') ? '' : value,
8925 cls : rowcfg.rowClass + ' x-col-' + i,
8927 html: (typeof(value) === 'object') ? '' : value
8934 if(typeof(config.colspan) != 'undefined'){
8935 td.colspan = config.colspan;
8938 if(typeof(config.hidden) != 'undefined' && config.hidden){
8939 td.style += ' display:none;';
8942 if(typeof(config.align) != 'undefined' && config.align.length){
8943 td.style += ' text-align:' + config.align + ';';
8945 if(typeof(config.valign) != 'undefined' && config.valign.length){
8946 td.style += ' vertical-align:' + config.valign + ';';
8949 if(typeof(config.width) != 'undefined'){
8950 td.style += ' width:' + config.width + 'px;';
8953 if(typeof(config.cursor) != 'undefined'){
8954 td.style += ' cursor:' + config.cursor + ';';
8957 if(typeof(config.cls) != 'undefined'){
8958 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8961 ['xs','sm','md','lg'].map(function(size){
8963 if(typeof(config[size]) == 'undefined'){
8969 if (!config[size]) { // 0 = hidden
8970 // BS 4 '0' is treated as hide that column and below.
8971 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8975 td.cls += ' col-' + size + '-' + config[size] + (
8976 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8986 row.cellObjects = cellObjects;
8994 onBeforeLoad : function()
9003 this.el.select('tbody', true).first().dom.innerHTML = '';
9006 * Show or hide a row.
9007 * @param {Number} rowIndex to show or hide
9008 * @param {Boolean} state hide
9010 setRowVisibility : function(rowIndex, state)
9012 var bt = this.mainBody.dom;
9014 var rows = this.el.select('tbody > tr', true).elements;
9016 if(typeof(rows[rowIndex]) == 'undefined'){
9019 rows[rowIndex].dom.style.display = state ? '' : 'none';
9023 getSelectionModel : function(){
9025 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9027 return this.selModel;
9030 * Render the Roo.bootstrap object from renderder
9032 renderCellObject : function(r)
9036 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9038 var t = r.cfg.render(r.container);
9041 Roo.each(r.cfg.cn, function(c){
9043 container: t.getChildContainer(),
9046 _this.renderCellObject(child);
9051 getRowIndex : function(row)
9055 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9066 * Returns the grid's underlying element = used by panel.Grid
9067 * @return {Element} The element
9069 getGridEl : function(){
9073 * Forces a resize - used by panel.Grid
9074 * @return {Element} The element
9076 autoSize : function()
9078 //var ctr = Roo.get(this.container.dom.parentElement);
9079 var ctr = Roo.get(this.el.dom);
9081 var thd = this.getGridEl().select('thead',true).first();
9082 var tbd = this.getGridEl().select('tbody', true).first();
9083 var tfd = this.getGridEl().select('tfoot', true).first();
9085 var cw = ctr.getWidth();
9086 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9090 tbd.setWidth(ctr.getWidth());
9091 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9092 // this needs fixing for various usage - currently only hydra job advers I think..
9094 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9096 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9099 cw = Math.max(cw, this.totalWidth);
9100 this.getGridEl().select('tbody tr',true).setWidth(cw);
9102 // resize 'expandable coloumn?
9104 return; // we doe not have a view in this design..
9107 onBodyScroll: function()
9109 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9111 this.mainHead.setStyle({
9112 'position' : 'relative',
9113 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9119 var scrollHeight = this.mainBody.dom.scrollHeight;
9121 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9123 var height = this.mainBody.getHeight();
9125 if(scrollHeight - height == scrollTop) {
9127 var total = this.ds.getTotalCount();
9129 if(this.footer.cursor + this.footer.pageSize < total){
9131 this.footer.ds.load({
9133 start : this.footer.cursor + this.footer.pageSize,
9134 limit : this.footer.pageSize
9144 onHeaderChange : function()
9146 var header = this.renderHeader();
9147 var table = this.el.select('table', true).first();
9149 this.mainHead.remove();
9150 this.mainHead = table.createChild(header, this.mainBody, false);
9152 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9153 e.on('click', this.sort, this);
9159 onHiddenChange : function(colModel, colIndex, hidden)
9161 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9162 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9164 this.CSS.updateRule(thSelector, "display", "");
9165 this.CSS.updateRule(tdSelector, "display", "");
9168 this.CSS.updateRule(thSelector, "display", "none");
9169 this.CSS.updateRule(tdSelector, "display", "none");
9172 this.onHeaderChange();
9176 setColumnWidth: function(col_index, width)
9178 // width = "md-2 xs-2..."
9179 if(!this.colModel.config[col_index]) {
9183 var w = width.split(" ");
9185 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9187 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9190 for(var j = 0; j < w.length; j++) {
9196 var size_cls = w[j].split("-");
9198 if(!Number.isInteger(size_cls[1] * 1)) {
9202 if(!this.colModel.config[col_index][size_cls[0]]) {
9206 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9210 h_row[0].classList.replace(
9211 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9212 "col-"+size_cls[0]+"-"+size_cls[1]
9215 for(var i = 0; i < rows.length; i++) {
9217 var size_cls = w[j].split("-");
9219 if(!Number.isInteger(size_cls[1] * 1)) {
9223 if(!this.colModel.config[col_index][size_cls[0]]) {
9227 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9231 rows[i].classList.replace(
9232 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9233 "col-"+size_cls[0]+"-"+size_cls[1]
9237 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9252 * @class Roo.bootstrap.TableCell
9253 * @extends Roo.bootstrap.Component
9254 * Bootstrap TableCell class
9255 * @cfg {String} html cell contain text
9256 * @cfg {String} cls cell class
9257 * @cfg {String} tag cell tag (td|th) default td
9258 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9259 * @cfg {String} align Aligns the content in a cell
9260 * @cfg {String} axis Categorizes cells
9261 * @cfg {String} bgcolor Specifies the background color of a cell
9262 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9263 * @cfg {Number} colspan Specifies the number of columns a cell should span
9264 * @cfg {String} headers Specifies one or more header cells a cell is related to
9265 * @cfg {Number} height Sets the height of a cell
9266 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9267 * @cfg {Number} rowspan Sets the number of rows a cell should span
9268 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9269 * @cfg {String} valign Vertical aligns the content in a cell
9270 * @cfg {Number} width Specifies the width of a cell
9273 * Create a new TableCell
9274 * @param {Object} config The config object
9277 Roo.bootstrap.TableCell = function(config){
9278 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9281 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9301 getAutoCreate : function(){
9302 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9322 cfg.align=this.align
9328 cfg.bgcolor=this.bgcolor
9331 cfg.charoff=this.charoff
9334 cfg.colspan=this.colspan
9337 cfg.headers=this.headers
9340 cfg.height=this.height
9343 cfg.nowrap=this.nowrap
9346 cfg.rowspan=this.rowspan
9349 cfg.scope=this.scope
9352 cfg.valign=this.valign
9355 cfg.width=this.width
9374 * @class Roo.bootstrap.TableRow
9375 * @extends Roo.bootstrap.Component
9376 * Bootstrap TableRow class
9377 * @cfg {String} cls row class
9378 * @cfg {String} align Aligns the content in a table row
9379 * @cfg {String} bgcolor Specifies a background color for a table row
9380 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9381 * @cfg {String} valign Vertical aligns the content in a table row
9384 * Create a new TableRow
9385 * @param {Object} config The config object
9388 Roo.bootstrap.TableRow = function(config){
9389 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9392 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9400 getAutoCreate : function(){
9401 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9411 cfg.align = this.align;
9414 cfg.bgcolor = this.bgcolor;
9417 cfg.charoff = this.charoff;
9420 cfg.valign = this.valign;
9438 * @class Roo.bootstrap.TableBody
9439 * @extends Roo.bootstrap.Component
9440 * Bootstrap TableBody class
9441 * @cfg {String} cls element class
9442 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9443 * @cfg {String} align Aligns the content inside the element
9444 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9445 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9448 * Create a new TableBody
9449 * @param {Object} config The config object
9452 Roo.bootstrap.TableBody = function(config){
9453 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9456 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9464 getAutoCreate : function(){
9465 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9479 cfg.align = this.align;
9482 cfg.charoff = this.charoff;
9485 cfg.valign = this.valign;
9492 // initEvents : function()
9499 // this.store = Roo.factory(this.store, Roo.data);
9500 // this.store.on('load', this.onLoad, this);
9502 // this.store.load();
9506 // onLoad: function ()
9508 // this.fireEvent('load', this);
9518 * Ext JS Library 1.1.1
9519 * Copyright(c) 2006-2007, Ext JS, LLC.
9521 * Originally Released Under LGPL - original licence link has changed is not relivant.
9524 * <script type="text/javascript">
9527 // as we use this in bootstrap.
9528 Roo.namespace('Roo.form');
9530 * @class Roo.form.Action
9531 * Internal Class used to handle form actions
9533 * @param {Roo.form.BasicForm} el The form element or its id
9534 * @param {Object} config Configuration options
9539 // define the action interface
9540 Roo.form.Action = function(form, options){
9542 this.options = options || {};
9545 * Client Validation Failed
9548 Roo.form.Action.CLIENT_INVALID = 'client';
9550 * Server Validation Failed
9553 Roo.form.Action.SERVER_INVALID = 'server';
9555 * Connect to Server Failed
9558 Roo.form.Action.CONNECT_FAILURE = 'connect';
9560 * Reading Data from Server Failed
9563 Roo.form.Action.LOAD_FAILURE = 'load';
9565 Roo.form.Action.prototype = {
9567 failureType : undefined,
9568 response : undefined,
9572 run : function(options){
9577 success : function(response){
9582 handleResponse : function(response){
9586 // default connection failure
9587 failure : function(response){
9589 this.response = response;
9590 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9591 this.form.afterAction(this, false);
9594 processResponse : function(response){
9595 this.response = response;
9596 if(!response.responseText){
9599 this.result = this.handleResponse(response);
9603 // utility functions used internally
9604 getUrl : function(appendParams){
9605 var url = this.options.url || this.form.url || this.form.el.dom.action;
9607 var p = this.getParams();
9609 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9615 getMethod : function(){
9616 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9619 getParams : function(){
9620 var bp = this.form.baseParams;
9621 var p = this.options.params;
9623 if(typeof p == "object"){
9624 p = Roo.urlEncode(Roo.applyIf(p, bp));
9625 }else if(typeof p == 'string' && bp){
9626 p += '&' + Roo.urlEncode(bp);
9629 p = Roo.urlEncode(bp);
9634 createCallback : function(){
9636 success: this.success,
9637 failure: this.failure,
9639 timeout: (this.form.timeout*1000),
9640 upload: this.form.fileUpload ? this.success : undefined
9645 Roo.form.Action.Submit = function(form, options){
9646 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9649 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9652 haveProgress : false,
9653 uploadComplete : false,
9655 // uploadProgress indicator.
9656 uploadProgress : function()
9658 if (!this.form.progressUrl) {
9662 if (!this.haveProgress) {
9663 Roo.MessageBox.progress("Uploading", "Uploading");
9665 if (this.uploadComplete) {
9666 Roo.MessageBox.hide();
9670 this.haveProgress = true;
9672 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9674 var c = new Roo.data.Connection();
9676 url : this.form.progressUrl,
9681 success : function(req){
9682 //console.log(data);
9686 rdata = Roo.decode(req.responseText)
9688 Roo.log("Invalid data from server..");
9692 if (!rdata || !rdata.success) {
9694 Roo.MessageBox.alert(Roo.encode(rdata));
9697 var data = rdata.data;
9699 if (this.uploadComplete) {
9700 Roo.MessageBox.hide();
9705 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9706 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9709 this.uploadProgress.defer(2000,this);
9712 failure: function(data) {
9713 Roo.log('progress url failed ');
9724 // run get Values on the form, so it syncs any secondary forms.
9725 this.form.getValues();
9727 var o = this.options;
9728 var method = this.getMethod();
9729 var isPost = method == 'POST';
9730 if(o.clientValidation === false || this.form.isValid()){
9732 if (this.form.progressUrl) {
9733 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9734 (new Date() * 1) + '' + Math.random());
9739 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9740 form:this.form.el.dom,
9741 url:this.getUrl(!isPost),
9743 params:isPost ? this.getParams() : null,
9744 isUpload: this.form.fileUpload,
9745 formData : this.form.formData
9748 this.uploadProgress();
9750 }else if (o.clientValidation !== false){ // client validation failed
9751 this.failureType = Roo.form.Action.CLIENT_INVALID;
9752 this.form.afterAction(this, false);
9756 success : function(response)
9758 this.uploadComplete= true;
9759 if (this.haveProgress) {
9760 Roo.MessageBox.hide();
9764 var result = this.processResponse(response);
9765 if(result === true || result.success){
9766 this.form.afterAction(this, true);
9770 this.form.markInvalid(result.errors);
9771 this.failureType = Roo.form.Action.SERVER_INVALID;
9773 this.form.afterAction(this, false);
9775 failure : function(response)
9777 this.uploadComplete= true;
9778 if (this.haveProgress) {
9779 Roo.MessageBox.hide();
9782 this.response = response;
9783 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9784 this.form.afterAction(this, false);
9787 handleResponse : function(response){
9788 if(this.form.errorReader){
9789 var rs = this.form.errorReader.read(response);
9792 for(var i = 0, len = rs.records.length; i < len; i++) {
9793 var r = rs.records[i];
9797 if(errors.length < 1){
9801 success : rs.success,
9807 ret = Roo.decode(response.responseText);
9811 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9821 Roo.form.Action.Load = function(form, options){
9822 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9823 this.reader = this.form.reader;
9826 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9831 Roo.Ajax.request(Roo.apply(
9832 this.createCallback(), {
9833 method:this.getMethod(),
9834 url:this.getUrl(false),
9835 params:this.getParams()
9839 success : function(response){
9841 var result = this.processResponse(response);
9842 if(result === true || !result.success || !result.data){
9843 this.failureType = Roo.form.Action.LOAD_FAILURE;
9844 this.form.afterAction(this, false);
9847 this.form.clearInvalid();
9848 this.form.setValues(result.data);
9849 this.form.afterAction(this, true);
9852 handleResponse : function(response){
9853 if(this.form.reader){
9854 var rs = this.form.reader.read(response);
9855 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9857 success : rs.success,
9861 return Roo.decode(response.responseText);
9865 Roo.form.Action.ACTION_TYPES = {
9866 'load' : Roo.form.Action.Load,
9867 'submit' : Roo.form.Action.Submit
9876 * @class Roo.bootstrap.Form
9877 * @extends Roo.bootstrap.Component
9878 * Bootstrap Form class
9879 * @cfg {String} method GET | POST (default POST)
9880 * @cfg {String} labelAlign top | left (default top)
9881 * @cfg {String} align left | right - for navbars
9882 * @cfg {Boolean} loadMask load mask when submit (default true)
9887 * @param {Object} config The config object
9891 Roo.bootstrap.Form = function(config){
9893 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9895 Roo.bootstrap.Form.popover.apply();
9899 * @event clientvalidation
9900 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9901 * @param {Form} this
9902 * @param {Boolean} valid true if the form has passed client-side validation
9904 clientvalidation: true,
9906 * @event beforeaction
9907 * Fires before any action is performed. Return false to cancel the action.
9908 * @param {Form} this
9909 * @param {Action} action The action to be performed
9913 * @event actionfailed
9914 * Fires when an action fails.
9915 * @param {Form} this
9916 * @param {Action} action The action that failed
9918 actionfailed : true,
9920 * @event actioncomplete
9921 * Fires when an action is completed.
9922 * @param {Form} this
9923 * @param {Action} action The action that completed
9925 actioncomplete : true
9929 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9932 * @cfg {String} method
9933 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9938 * The URL to use for form actions if one isn't supplied in the action options.
9941 * @cfg {Boolean} fileUpload
9942 * Set to true if this form is a file upload.
9946 * @cfg {Object} baseParams
9947 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9951 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9955 * @cfg {Sting} align (left|right) for navbar forms
9960 activeAction : null,
9963 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9964 * element by passing it or its id or mask the form itself by passing in true.
9967 waitMsgTarget : false,
9972 * @cfg {Boolean} errorMask (true|false) default false
9977 * @cfg {Number} maskOffset Default 100
9982 * @cfg {Boolean} maskBody
9986 getAutoCreate : function(){
9990 method : this.method || 'POST',
9991 id : this.id || Roo.id(),
9994 if (this.parent().xtype.match(/^Nav/)) {
9995 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9999 if (this.labelAlign == 'left' ) {
10000 cfg.cls += ' form-horizontal';
10006 initEvents : function()
10008 this.el.on('submit', this.onSubmit, this);
10009 // this was added as random key presses on the form where triggering form submit.
10010 this.el.on('keypress', function(e) {
10011 if (e.getCharCode() != 13) {
10014 // we might need to allow it for textareas.. and some other items.
10015 // check e.getTarget().
10017 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10021 Roo.log("keypress blocked");
10023 e.preventDefault();
10029 onSubmit : function(e){
10034 * Returns true if client-side validation on the form is successful.
10037 isValid : function(){
10038 var items = this.getItems();
10040 var target = false;
10042 items.each(function(f){
10048 Roo.log('invalid field: ' + f.name);
10052 if(!target && f.el.isVisible(true)){
10058 if(this.errorMask && !valid){
10059 Roo.bootstrap.Form.popover.mask(this, target);
10066 * Returns true if any fields in this form have changed since their original load.
10069 isDirty : function(){
10071 var items = this.getItems();
10072 items.each(function(f){
10082 * Performs a predefined action (submit or load) or custom actions you define on this form.
10083 * @param {String} actionName The name of the action type
10084 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10085 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10086 * accept other config options):
10088 Property Type Description
10089 ---------------- --------------- ----------------------------------------------------------------------------------
10090 url String The url for the action (defaults to the form's url)
10091 method String The form method to use (defaults to the form's method, or POST if not defined)
10092 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10093 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10094 validate the form on the client (defaults to false)
10096 * @return {BasicForm} this
10098 doAction : function(action, options){
10099 if(typeof action == 'string'){
10100 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10102 if(this.fireEvent('beforeaction', this, action) !== false){
10103 this.beforeAction(action);
10104 action.run.defer(100, action);
10110 beforeAction : function(action){
10111 var o = action.options;
10116 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10118 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10121 // not really supported yet.. ??
10123 //if(this.waitMsgTarget === true){
10124 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10125 //}else if(this.waitMsgTarget){
10126 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10127 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10129 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10135 afterAction : function(action, success){
10136 this.activeAction = null;
10137 var o = action.options;
10142 Roo.get(document.body).unmask();
10148 //if(this.waitMsgTarget === true){
10149 // this.el.unmask();
10150 //}else if(this.waitMsgTarget){
10151 // this.waitMsgTarget.unmask();
10153 // Roo.MessageBox.updateProgress(1);
10154 // Roo.MessageBox.hide();
10161 Roo.callback(o.success, o.scope, [this, action]);
10162 this.fireEvent('actioncomplete', this, action);
10166 // failure condition..
10167 // we have a scenario where updates need confirming.
10168 // eg. if a locking scenario exists..
10169 // we look for { errors : { needs_confirm : true }} in the response.
10171 (typeof(action.result) != 'undefined') &&
10172 (typeof(action.result.errors) != 'undefined') &&
10173 (typeof(action.result.errors.needs_confirm) != 'undefined')
10176 Roo.log("not supported yet");
10179 Roo.MessageBox.confirm(
10180 "Change requires confirmation",
10181 action.result.errorMsg,
10186 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10196 Roo.callback(o.failure, o.scope, [this, action]);
10197 // show an error message if no failed handler is set..
10198 if (!this.hasListener('actionfailed')) {
10199 Roo.log("need to add dialog support");
10201 Roo.MessageBox.alert("Error",
10202 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10203 action.result.errorMsg :
10204 "Saving Failed, please check your entries or try again"
10209 this.fireEvent('actionfailed', this, action);
10214 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10215 * @param {String} id The value to search for
10218 findField : function(id){
10219 var items = this.getItems();
10220 var field = items.get(id);
10222 items.each(function(f){
10223 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10230 return field || null;
10233 * Mark fields in this form invalid in bulk.
10234 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10235 * @return {BasicForm} this
10237 markInvalid : function(errors){
10238 if(errors instanceof Array){
10239 for(var i = 0, len = errors.length; i < len; i++){
10240 var fieldError = errors[i];
10241 var f = this.findField(fieldError.id);
10243 f.markInvalid(fieldError.msg);
10249 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10250 field.markInvalid(errors[id]);
10254 //Roo.each(this.childForms || [], function (f) {
10255 // f.markInvalid(errors);
10262 * Set values for fields in this form in bulk.
10263 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10264 * @return {BasicForm} this
10266 setValues : function(values){
10267 if(values instanceof Array){ // array of objects
10268 for(var i = 0, len = values.length; i < len; i++){
10270 var f = this.findField(v.id);
10272 f.setValue(v.value);
10273 if(this.trackResetOnLoad){
10274 f.originalValue = f.getValue();
10278 }else{ // object hash
10281 if(typeof values[id] != 'function' && (field = this.findField(id))){
10283 if (field.setFromData &&
10284 field.valueField &&
10285 field.displayField &&
10286 // combos' with local stores can
10287 // be queried via setValue()
10288 // to set their value..
10289 (field.store && !field.store.isLocal)
10293 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10294 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10295 field.setFromData(sd);
10297 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10299 field.setFromData(values);
10302 field.setValue(values[id]);
10306 if(this.trackResetOnLoad){
10307 field.originalValue = field.getValue();
10313 //Roo.each(this.childForms || [], function (f) {
10314 // f.setValues(values);
10321 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10322 * they are returned as an array.
10323 * @param {Boolean} asString
10326 getValues : function(asString){
10327 //if (this.childForms) {
10328 // copy values from the child forms
10329 // Roo.each(this.childForms, function (f) {
10330 // this.setValues(f.getValues());
10336 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10337 if(asString === true){
10340 return Roo.urlDecode(fs);
10344 * Returns the fields in this form as an object with key/value pairs.
10345 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10348 getFieldValues : function(with_hidden)
10350 var items = this.getItems();
10352 items.each(function(f){
10354 if (!f.getName()) {
10358 var v = f.getValue();
10360 if (f.inputType =='radio') {
10361 if (typeof(ret[f.getName()]) == 'undefined') {
10362 ret[f.getName()] = ''; // empty..
10365 if (!f.el.dom.checked) {
10369 v = f.el.dom.value;
10373 if(f.xtype == 'MoneyField'){
10374 ret[f.currencyName] = f.getCurrency();
10377 // not sure if this supported any more..
10378 if ((typeof(v) == 'object') && f.getRawValue) {
10379 v = f.getRawValue() ; // dates..
10381 // combo boxes where name != hiddenName...
10382 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10383 ret[f.name] = f.getRawValue();
10385 ret[f.getName()] = v;
10392 * Clears all invalid messages in this form.
10393 * @return {BasicForm} this
10395 clearInvalid : function(){
10396 var items = this.getItems();
10398 items.each(function(f){
10406 * Resets this form.
10407 * @return {BasicForm} this
10409 reset : function(){
10410 var items = this.getItems();
10411 items.each(function(f){
10415 Roo.each(this.childForms || [], function (f) {
10423 getItems : function()
10425 var r=new Roo.util.MixedCollection(false, function(o){
10426 return o.id || (o.id = Roo.id());
10428 var iter = function(el) {
10435 Roo.each(el.items,function(e) {
10444 hideFields : function(items)
10446 Roo.each(items, function(i){
10448 var f = this.findField(i);
10459 showFields : function(items)
10461 Roo.each(items, function(i){
10463 var f = this.findField(i);
10476 Roo.apply(Roo.bootstrap.Form, {
10492 intervalID : false,
10498 if(this.isApplied){
10503 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10504 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10505 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10506 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10509 this.maskEl.top.enableDisplayMode("block");
10510 this.maskEl.left.enableDisplayMode("block");
10511 this.maskEl.bottom.enableDisplayMode("block");
10512 this.maskEl.right.enableDisplayMode("block");
10514 this.toolTip = new Roo.bootstrap.Tooltip({
10515 cls : 'roo-form-error-popover',
10517 'left' : ['r-l', [-2,0], 'right'],
10518 'right' : ['l-r', [2,0], 'left'],
10519 'bottom' : ['tl-bl', [0,2], 'top'],
10520 'top' : [ 'bl-tl', [0,-2], 'bottom']
10524 this.toolTip.render(Roo.get(document.body));
10526 this.toolTip.el.enableDisplayMode("block");
10528 Roo.get(document.body).on('click', function(){
10532 Roo.get(document.body).on('touchstart', function(){
10536 this.isApplied = true
10539 mask : function(form, target)
10543 this.target = target;
10545 if(!this.form.errorMask || !target.el){
10549 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10551 Roo.log(scrollable);
10553 var ot = this.target.el.calcOffsetsTo(scrollable);
10555 var scrollTo = ot[1] - this.form.maskOffset;
10557 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10559 scrollable.scrollTo('top', scrollTo);
10561 var box = this.target.el.getBox();
10563 var zIndex = Roo.bootstrap.Modal.zIndex++;
10566 this.maskEl.top.setStyle('position', 'absolute');
10567 this.maskEl.top.setStyle('z-index', zIndex);
10568 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10569 this.maskEl.top.setLeft(0);
10570 this.maskEl.top.setTop(0);
10571 this.maskEl.top.show();
10573 this.maskEl.left.setStyle('position', 'absolute');
10574 this.maskEl.left.setStyle('z-index', zIndex);
10575 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10576 this.maskEl.left.setLeft(0);
10577 this.maskEl.left.setTop(box.y - this.padding);
10578 this.maskEl.left.show();
10580 this.maskEl.bottom.setStyle('position', 'absolute');
10581 this.maskEl.bottom.setStyle('z-index', zIndex);
10582 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10583 this.maskEl.bottom.setLeft(0);
10584 this.maskEl.bottom.setTop(box.bottom + this.padding);
10585 this.maskEl.bottom.show();
10587 this.maskEl.right.setStyle('position', 'absolute');
10588 this.maskEl.right.setStyle('z-index', zIndex);
10589 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10590 this.maskEl.right.setLeft(box.right + this.padding);
10591 this.maskEl.right.setTop(box.y - this.padding);
10592 this.maskEl.right.show();
10594 this.toolTip.bindEl = this.target.el;
10596 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10598 var tip = this.target.blankText;
10600 if(this.target.getValue() !== '' ) {
10602 if (this.target.invalidText.length) {
10603 tip = this.target.invalidText;
10604 } else if (this.target.regexText.length){
10605 tip = this.target.regexText;
10609 this.toolTip.show(tip);
10611 this.intervalID = window.setInterval(function() {
10612 Roo.bootstrap.Form.popover.unmask();
10615 window.onwheel = function(){ return false;};
10617 (function(){ this.isMasked = true; }).defer(500, this);
10621 unmask : function()
10623 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10627 this.maskEl.top.setStyle('position', 'absolute');
10628 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10629 this.maskEl.top.hide();
10631 this.maskEl.left.setStyle('position', 'absolute');
10632 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10633 this.maskEl.left.hide();
10635 this.maskEl.bottom.setStyle('position', 'absolute');
10636 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10637 this.maskEl.bottom.hide();
10639 this.maskEl.right.setStyle('position', 'absolute');
10640 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10641 this.maskEl.right.hide();
10643 this.toolTip.hide();
10645 this.toolTip.el.hide();
10647 window.onwheel = function(){ return true;};
10649 if(this.intervalID){
10650 window.clearInterval(this.intervalID);
10651 this.intervalID = false;
10654 this.isMasked = false;
10664 * Ext JS Library 1.1.1
10665 * Copyright(c) 2006-2007, Ext JS, LLC.
10667 * Originally Released Under LGPL - original licence link has changed is not relivant.
10670 * <script type="text/javascript">
10673 * @class Roo.form.VTypes
10674 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10677 Roo.form.VTypes = function(){
10678 // closure these in so they are only created once.
10679 var alpha = /^[a-zA-Z_]+$/;
10680 var alphanum = /^[a-zA-Z0-9_]+$/;
10681 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10682 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10684 // All these messages and functions are configurable
10687 * The function used to validate email addresses
10688 * @param {String} value The email address
10690 'email' : function(v){
10691 return email.test(v);
10694 * The error text to display when the email validation function returns false
10697 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10699 * The keystroke filter mask to be applied on email input
10702 'emailMask' : /[a-z0-9_\.\-@]/i,
10705 * The function used to validate URLs
10706 * @param {String} value The URL
10708 'url' : function(v){
10709 return url.test(v);
10712 * The error text to display when the url validation function returns false
10715 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10718 * The function used to validate alpha values
10719 * @param {String} value The value
10721 'alpha' : function(v){
10722 return alpha.test(v);
10725 * The error text to display when the alpha validation function returns false
10728 'alphaText' : 'This field should only contain letters and _',
10730 * The keystroke filter mask to be applied on alpha input
10733 'alphaMask' : /[a-z_]/i,
10736 * The function used to validate alphanumeric values
10737 * @param {String} value The value
10739 'alphanum' : function(v){
10740 return alphanum.test(v);
10743 * The error text to display when the alphanumeric validation function returns false
10746 'alphanumText' : 'This field should only contain letters, numbers and _',
10748 * The keystroke filter mask to be applied on alphanumeric input
10751 'alphanumMask' : /[a-z0-9_]/i
10761 * @class Roo.bootstrap.Input
10762 * @extends Roo.bootstrap.Component
10763 * Bootstrap Input class
10764 * @cfg {Boolean} disabled is it disabled
10765 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10766 * @cfg {String} name name of the input
10767 * @cfg {string} fieldLabel - the label associated
10768 * @cfg {string} placeholder - placeholder to put in text.
10769 * @cfg {string} before - input group add on before
10770 * @cfg {string} after - input group add on after
10771 * @cfg {string} size - (lg|sm) or leave empty..
10772 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10773 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10774 * @cfg {Number} md colspan out of 12 for computer-sized screens
10775 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10776 * @cfg {string} value default value of the input
10777 * @cfg {Number} labelWidth set the width of label
10778 * @cfg {Number} labellg set the width of label (1-12)
10779 * @cfg {Number} labelmd set the width of label (1-12)
10780 * @cfg {Number} labelsm set the width of label (1-12)
10781 * @cfg {Number} labelxs set the width of label (1-12)
10782 * @cfg {String} labelAlign (top|left)
10783 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10784 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10785 * @cfg {String} indicatorpos (left|right) default left
10786 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10787 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10788 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10790 * @cfg {String} align (left|center|right) Default left
10791 * @cfg {Boolean} forceFeedback (true|false) Default false
10794 * Create a new Input
10795 * @param {Object} config The config object
10798 Roo.bootstrap.Input = function(config){
10800 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10805 * Fires when this field receives input focus.
10806 * @param {Roo.form.Field} this
10811 * Fires when this field loses input focus.
10812 * @param {Roo.form.Field} this
10816 * @event specialkey
10817 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10818 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10819 * @param {Roo.form.Field} this
10820 * @param {Roo.EventObject} e The event object
10825 * Fires just before the field blurs if the field value has changed.
10826 * @param {Roo.form.Field} this
10827 * @param {Mixed} newValue The new value
10828 * @param {Mixed} oldValue The original value
10833 * Fires after the field has been marked as invalid.
10834 * @param {Roo.form.Field} this
10835 * @param {String} msg The validation message
10840 * Fires after the field has been validated with no errors.
10841 * @param {Roo.form.Field} this
10846 * Fires after the key up
10847 * @param {Roo.form.Field} this
10848 * @param {Roo.EventObject} e The event Object
10853 * Fires after the user pastes into input
10854 * @param {Roo.form.Field} this
10855 * @param {Roo.EventObject} e The event Object
10861 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10863 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10864 automatic validation (defaults to "keyup").
10866 validationEvent : "keyup",
10868 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10870 validateOnBlur : true,
10872 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10874 validationDelay : 250,
10876 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10878 focusClass : "x-form-focus", // not needed???
10882 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10884 invalidClass : "has-warning",
10887 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10889 validClass : "has-success",
10892 * @cfg {Boolean} hasFeedback (true|false) default true
10894 hasFeedback : true,
10897 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10899 invalidFeedbackClass : "glyphicon-warning-sign",
10902 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10904 validFeedbackClass : "glyphicon-ok",
10907 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10909 selectOnFocus : false,
10912 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10916 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10921 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10923 disableKeyFilter : false,
10926 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10930 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10934 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10936 blankText : "Please complete this mandatory field",
10939 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10943 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10945 maxLength : Number.MAX_VALUE,
10947 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10949 minLengthText : "The minimum length for this field is {0}",
10951 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10953 maxLengthText : "The maximum length for this field is {0}",
10957 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10958 * If available, this function will be called only after the basic validators all return true, and will be passed the
10959 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10963 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10964 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10965 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10969 * @cfg {String} regexText -- Depricated - use Invalid Text
10974 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10980 autocomplete: false,
10984 inputType : 'text',
10987 placeholder: false,
10992 preventMark: false,
10993 isFormField : true,
10996 labelAlign : false,
10999 formatedValue : false,
11000 forceFeedback : false,
11002 indicatorpos : 'left',
11012 parentLabelAlign : function()
11015 while (parent.parent()) {
11016 parent = parent.parent();
11017 if (typeof(parent.labelAlign) !='undefined') {
11018 return parent.labelAlign;
11025 getAutoCreate : function()
11027 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11033 if(this.inputType != 'hidden'){
11034 cfg.cls = 'form-group' //input-group
11040 type : this.inputType,
11041 value : this.value,
11042 cls : 'form-control',
11043 placeholder : this.placeholder || '',
11044 autocomplete : this.autocomplete || 'new-password'
11046 if (this.inputType == 'file') {
11047 input.style = 'overflow:hidden'; // why not in CSS?
11050 if(this.capture.length){
11051 input.capture = this.capture;
11054 if(this.accept.length){
11055 input.accept = this.accept + "/*";
11059 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11062 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11063 input.maxLength = this.maxLength;
11066 if (this.disabled) {
11067 input.disabled=true;
11070 if (this.readOnly) {
11071 input.readonly=true;
11075 input.name = this.name;
11079 input.cls += ' input-' + this.size;
11083 ['xs','sm','md','lg'].map(function(size){
11084 if (settings[size]) {
11085 cfg.cls += ' col-' + size + '-' + settings[size];
11089 var inputblock = input;
11093 cls: 'glyphicon form-control-feedback'
11096 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11099 cls : 'has-feedback',
11107 if (this.before || this.after) {
11110 cls : 'input-group',
11114 if (this.before && typeof(this.before) == 'string') {
11116 inputblock.cn.push({
11118 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11122 if (this.before && typeof(this.before) == 'object') {
11123 this.before = Roo.factory(this.before);
11125 inputblock.cn.push({
11127 cls : 'roo-input-before input-group-prepend input-group-' +
11128 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11132 inputblock.cn.push(input);
11134 if (this.after && typeof(this.after) == 'string') {
11135 inputblock.cn.push({
11137 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11141 if (this.after && typeof(this.after) == 'object') {
11142 this.after = Roo.factory(this.after);
11144 inputblock.cn.push({
11146 cls : 'roo-input-after input-group-append input-group-' +
11147 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11151 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11152 inputblock.cls += ' has-feedback';
11153 inputblock.cn.push(feedback);
11158 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11159 tooltip : 'This field is required'
11161 if (this.allowBlank ) {
11162 indicator.style = this.allowBlank ? ' display:none' : '';
11164 if (align ==='left' && this.fieldLabel.length) {
11166 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11173 cls : 'control-label col-form-label',
11174 html : this.fieldLabel
11185 var labelCfg = cfg.cn[1];
11186 var contentCfg = cfg.cn[2];
11188 if(this.indicatorpos == 'right'){
11193 cls : 'control-label col-form-label',
11197 html : this.fieldLabel
11211 labelCfg = cfg.cn[0];
11212 contentCfg = cfg.cn[1];
11216 if(this.labelWidth > 12){
11217 labelCfg.style = "width: " + this.labelWidth + 'px';
11220 if(this.labelWidth < 13 && this.labelmd == 0){
11221 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11224 if(this.labellg > 0){
11225 labelCfg.cls += ' col-lg-' + this.labellg;
11226 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11229 if(this.labelmd > 0){
11230 labelCfg.cls += ' col-md-' + this.labelmd;
11231 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11234 if(this.labelsm > 0){
11235 labelCfg.cls += ' col-sm-' + this.labelsm;
11236 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11239 if(this.labelxs > 0){
11240 labelCfg.cls += ' col-xs-' + this.labelxs;
11241 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11245 } else if ( this.fieldLabel.length) {
11252 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11253 tooltip : 'This field is required',
11254 style : this.allowBlank ? ' display:none' : ''
11258 //cls : 'input-group-addon',
11259 html : this.fieldLabel
11267 if(this.indicatorpos == 'right'){
11272 //cls : 'input-group-addon',
11273 html : this.fieldLabel
11278 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11279 tooltip : 'This field is required',
11280 style : this.allowBlank ? ' display:none' : ''
11300 if (this.parentType === 'Navbar' && this.parent().bar) {
11301 cfg.cls += ' navbar-form';
11304 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11305 // on BS4 we do this only if not form
11306 cfg.cls += ' navbar-form';
11314 * return the real input element.
11316 inputEl: function ()
11318 return this.el.select('input.form-control',true).first();
11321 tooltipEl : function()
11323 return this.inputEl();
11326 indicatorEl : function()
11328 if (Roo.bootstrap.version == 4) {
11329 return false; // not enabled in v4 yet.
11332 var indicator = this.el.select('i.roo-required-indicator',true).first();
11342 setDisabled : function(v)
11344 var i = this.inputEl().dom;
11346 i.removeAttribute('disabled');
11350 i.setAttribute('disabled','true');
11352 initEvents : function()
11355 this.inputEl().on("keydown" , this.fireKey, this);
11356 this.inputEl().on("focus", this.onFocus, this);
11357 this.inputEl().on("blur", this.onBlur, this);
11359 this.inputEl().relayEvent('keyup', this);
11360 this.inputEl().relayEvent('paste', this);
11362 this.indicator = this.indicatorEl();
11364 if(this.indicator){
11365 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11368 // reference to original value for reset
11369 this.originalValue = this.getValue();
11370 //Roo.form.TextField.superclass.initEvents.call(this);
11371 if(this.validationEvent == 'keyup'){
11372 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11373 this.inputEl().on('keyup', this.filterValidation, this);
11375 else if(this.validationEvent !== false){
11376 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11379 if(this.selectOnFocus){
11380 this.on("focus", this.preFocus, this);
11383 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11384 this.inputEl().on("keypress", this.filterKeys, this);
11386 this.inputEl().relayEvent('keypress', this);
11389 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11390 this.el.on("click", this.autoSize, this);
11393 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11394 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11397 if (typeof(this.before) == 'object') {
11398 this.before.render(this.el.select('.roo-input-before',true).first());
11400 if (typeof(this.after) == 'object') {
11401 this.after.render(this.el.select('.roo-input-after',true).first());
11404 this.inputEl().on('change', this.onChange, this);
11407 filterValidation : function(e){
11408 if(!e.isNavKeyPress()){
11409 this.validationTask.delay(this.validationDelay);
11413 * Validates the field value
11414 * @return {Boolean} True if the value is valid, else false
11416 validate : function(){
11417 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11418 if(this.disabled || this.validateValue(this.getRawValue())){
11423 this.markInvalid();
11429 * Validates a value according to the field's validation rules and marks the field as invalid
11430 * if the validation fails
11431 * @param {Mixed} value The value to validate
11432 * @return {Boolean} True if the value is valid, else false
11434 validateValue : function(value)
11436 if(this.getVisibilityEl().hasClass('hidden')){
11440 if(value.length < 1) { // if it's blank
11441 if(this.allowBlank){
11447 if(value.length < this.minLength){
11450 if(value.length > this.maxLength){
11454 var vt = Roo.form.VTypes;
11455 if(!vt[this.vtype](value, this)){
11459 if(typeof this.validator == "function"){
11460 var msg = this.validator(value);
11464 if (typeof(msg) == 'string') {
11465 this.invalidText = msg;
11469 if(this.regex && !this.regex.test(value)){
11477 fireKey : function(e){
11478 //Roo.log('field ' + e.getKey());
11479 if(e.isNavKeyPress()){
11480 this.fireEvent("specialkey", this, e);
11483 focus : function (selectText){
11485 this.inputEl().focus();
11486 if(selectText === true){
11487 this.inputEl().dom.select();
11493 onFocus : function(){
11494 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11495 // this.el.addClass(this.focusClass);
11497 if(!this.hasFocus){
11498 this.hasFocus = true;
11499 this.startValue = this.getValue();
11500 this.fireEvent("focus", this);
11504 beforeBlur : Roo.emptyFn,
11508 onBlur : function(){
11510 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11511 //this.el.removeClass(this.focusClass);
11513 this.hasFocus = false;
11514 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11517 var v = this.getValue();
11518 if(String(v) !== String(this.startValue)){
11519 this.fireEvent('change', this, v, this.startValue);
11521 this.fireEvent("blur", this);
11524 onChange : function(e)
11526 var v = this.getValue();
11527 if(String(v) !== String(this.startValue)){
11528 this.fireEvent('change', this, v, this.startValue);
11534 * Resets the current field value to the originally loaded value and clears any validation messages
11536 reset : function(){
11537 this.setValue(this.originalValue);
11541 * Returns the name of the field
11542 * @return {Mixed} name The name field
11544 getName: function(){
11548 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11549 * @return {Mixed} value The field value
11551 getValue : function(){
11553 var v = this.inputEl().getValue();
11558 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11559 * @return {Mixed} value The field value
11561 getRawValue : function(){
11562 var v = this.inputEl().getValue();
11568 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11569 * @param {Mixed} value The value to set
11571 setRawValue : function(v){
11572 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11575 selectText : function(start, end){
11576 var v = this.getRawValue();
11578 start = start === undefined ? 0 : start;
11579 end = end === undefined ? v.length : end;
11580 var d = this.inputEl().dom;
11581 if(d.setSelectionRange){
11582 d.setSelectionRange(start, end);
11583 }else if(d.createTextRange){
11584 var range = d.createTextRange();
11585 range.moveStart("character", start);
11586 range.moveEnd("character", v.length-end);
11593 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11594 * @param {Mixed} value The value to set
11596 setValue : function(v){
11599 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11605 processValue : function(value){
11606 if(this.stripCharsRe){
11607 var newValue = value.replace(this.stripCharsRe, '');
11608 if(newValue !== value){
11609 this.setRawValue(newValue);
11616 preFocus : function(){
11618 if(this.selectOnFocus){
11619 this.inputEl().dom.select();
11622 filterKeys : function(e){
11623 var k = e.getKey();
11624 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11627 var c = e.getCharCode(), cc = String.fromCharCode(c);
11628 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11631 if(!this.maskRe.test(cc)){
11636 * Clear any invalid styles/messages for this field
11638 clearInvalid : function(){
11640 if(!this.el || this.preventMark){ // not rendered
11645 this.el.removeClass([this.invalidClass, 'is-invalid']);
11647 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11649 var feedback = this.el.select('.form-control-feedback', true).first();
11652 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11657 if(this.indicator){
11658 this.indicator.removeClass('visible');
11659 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11662 this.fireEvent('valid', this);
11666 * Mark this field as valid
11668 markValid : function()
11670 if(!this.el || this.preventMark){ // not rendered...
11674 this.el.removeClass([this.invalidClass, this.validClass]);
11675 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11677 var feedback = this.el.select('.form-control-feedback', true).first();
11680 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11683 if(this.indicator){
11684 this.indicator.removeClass('visible');
11685 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11693 if(this.allowBlank && !this.getRawValue().length){
11696 if (Roo.bootstrap.version == 3) {
11697 this.el.addClass(this.validClass);
11699 this.inputEl().addClass('is-valid');
11702 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11704 var feedback = this.el.select('.form-control-feedback', true).first();
11707 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11708 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11713 this.fireEvent('valid', this);
11717 * Mark this field as invalid
11718 * @param {String} msg The validation message
11720 markInvalid : function(msg)
11722 if(!this.el || this.preventMark){ // not rendered
11726 this.el.removeClass([this.invalidClass, this.validClass]);
11727 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11729 var feedback = this.el.select('.form-control-feedback', true).first();
11732 this.el.select('.form-control-feedback', true).first().removeClass(
11733 [this.invalidFeedbackClass, this.validFeedbackClass]);
11740 if(this.allowBlank && !this.getRawValue().length){
11744 if(this.indicator){
11745 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11746 this.indicator.addClass('visible');
11748 if (Roo.bootstrap.version == 3) {
11749 this.el.addClass(this.invalidClass);
11751 this.inputEl().addClass('is-invalid');
11756 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11758 var feedback = this.el.select('.form-control-feedback', true).first();
11761 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11763 if(this.getValue().length || this.forceFeedback){
11764 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11771 this.fireEvent('invalid', this, msg);
11774 SafariOnKeyDown : function(event)
11776 // this is a workaround for a password hang bug on chrome/ webkit.
11777 if (this.inputEl().dom.type != 'password') {
11781 var isSelectAll = false;
11783 if(this.inputEl().dom.selectionEnd > 0){
11784 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11786 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11787 event.preventDefault();
11792 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11794 event.preventDefault();
11795 // this is very hacky as keydown always get's upper case.
11797 var cc = String.fromCharCode(event.getCharCode());
11798 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11802 adjustWidth : function(tag, w){
11803 tag = tag.toLowerCase();
11804 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11805 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11806 if(tag == 'input'){
11809 if(tag == 'textarea'){
11812 }else if(Roo.isOpera){
11813 if(tag == 'input'){
11816 if(tag == 'textarea'){
11824 setFieldLabel : function(v)
11826 if(!this.rendered){
11830 if(this.indicatorEl()){
11831 var ar = this.el.select('label > span',true);
11833 if (ar.elements.length) {
11834 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11835 this.fieldLabel = v;
11839 var br = this.el.select('label',true);
11841 if(br.elements.length) {
11842 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11843 this.fieldLabel = v;
11847 Roo.log('Cannot Found any of label > span || label in input');
11851 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11852 this.fieldLabel = v;
11867 * @class Roo.bootstrap.TextArea
11868 * @extends Roo.bootstrap.Input
11869 * Bootstrap TextArea class
11870 * @cfg {Number} cols Specifies the visible width of a text area
11871 * @cfg {Number} rows Specifies the visible number of lines in a text area
11872 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11873 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11874 * @cfg {string} html text
11877 * Create a new TextArea
11878 * @param {Object} config The config object
11881 Roo.bootstrap.TextArea = function(config){
11882 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11886 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11896 getAutoCreate : function(){
11898 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11904 if(this.inputType != 'hidden'){
11905 cfg.cls = 'form-group' //input-group
11913 value : this.value || '',
11914 html: this.html || '',
11915 cls : 'form-control',
11916 placeholder : this.placeholder || ''
11920 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11921 input.maxLength = this.maxLength;
11925 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11929 input.cols = this.cols;
11932 if (this.readOnly) {
11933 input.readonly = true;
11937 input.name = this.name;
11941 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11945 ['xs','sm','md','lg'].map(function(size){
11946 if (settings[size]) {
11947 cfg.cls += ' col-' + size + '-' + settings[size];
11951 var inputblock = input;
11953 if(this.hasFeedback && !this.allowBlank){
11957 cls: 'glyphicon form-control-feedback'
11961 cls : 'has-feedback',
11970 if (this.before || this.after) {
11973 cls : 'input-group',
11977 inputblock.cn.push({
11979 cls : 'input-group-addon',
11984 inputblock.cn.push(input);
11986 if(this.hasFeedback && !this.allowBlank){
11987 inputblock.cls += ' has-feedback';
11988 inputblock.cn.push(feedback);
11992 inputblock.cn.push({
11994 cls : 'input-group-addon',
12001 if (align ==='left' && this.fieldLabel.length) {
12006 cls : 'control-label',
12007 html : this.fieldLabel
12018 if(this.labelWidth > 12){
12019 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12022 if(this.labelWidth < 13 && this.labelmd == 0){
12023 this.labelmd = this.labelWidth;
12026 if(this.labellg > 0){
12027 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12028 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12031 if(this.labelmd > 0){
12032 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12033 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12036 if(this.labelsm > 0){
12037 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12038 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12041 if(this.labelxs > 0){
12042 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12043 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12046 } else if ( this.fieldLabel.length) {
12051 //cls : 'input-group-addon',
12052 html : this.fieldLabel
12070 if (this.disabled) {
12071 input.disabled=true;
12078 * return the real textarea element.
12080 inputEl: function ()
12082 return this.el.select('textarea.form-control',true).first();
12086 * Clear any invalid styles/messages for this field
12088 clearInvalid : function()
12091 if(!this.el || this.preventMark){ // not rendered
12095 var label = this.el.select('label', true).first();
12096 var icon = this.el.select('i.fa-star', true).first();
12101 this.el.removeClass( this.validClass);
12102 this.inputEl().removeClass('is-invalid');
12104 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12106 var feedback = this.el.select('.form-control-feedback', true).first();
12109 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12114 this.fireEvent('valid', this);
12118 * Mark this field as valid
12120 markValid : function()
12122 if(!this.el || this.preventMark){ // not rendered
12126 this.el.removeClass([this.invalidClass, this.validClass]);
12127 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12129 var feedback = this.el.select('.form-control-feedback', true).first();
12132 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12135 if(this.disabled || this.allowBlank){
12139 var label = this.el.select('label', true).first();
12140 var icon = this.el.select('i.fa-star', true).first();
12145 if (Roo.bootstrap.version == 3) {
12146 this.el.addClass(this.validClass);
12148 this.inputEl().addClass('is-valid');
12152 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12154 var feedback = this.el.select('.form-control-feedback', true).first();
12157 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12158 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12163 this.fireEvent('valid', this);
12167 * Mark this field as invalid
12168 * @param {String} msg The validation message
12170 markInvalid : function(msg)
12172 if(!this.el || this.preventMark){ // not rendered
12176 this.el.removeClass([this.invalidClass, this.validClass]);
12177 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12179 var feedback = this.el.select('.form-control-feedback', true).first();
12182 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12185 if(this.disabled || this.allowBlank){
12189 var label = this.el.select('label', true).first();
12190 var icon = this.el.select('i.fa-star', true).first();
12192 if(!this.getValue().length && label && !icon){
12193 this.el.createChild({
12195 cls : 'text-danger fa fa-lg fa-star',
12196 tooltip : 'This field is required',
12197 style : 'margin-right:5px;'
12201 if (Roo.bootstrap.version == 3) {
12202 this.el.addClass(this.invalidClass);
12204 this.inputEl().addClass('is-invalid');
12207 // fixme ... this may be depricated need to test..
12208 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12210 var feedback = this.el.select('.form-control-feedback', true).first();
12213 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12215 if(this.getValue().length || this.forceFeedback){
12216 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12223 this.fireEvent('invalid', this, msg);
12231 * trigger field - base class for combo..
12236 * @class Roo.bootstrap.TriggerField
12237 * @extends Roo.bootstrap.Input
12238 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12239 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12240 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12241 * for which you can provide a custom implementation. For example:
12243 var trigger = new Roo.bootstrap.TriggerField();
12244 trigger.onTriggerClick = myTriggerFn;
12245 trigger.applyTo('my-field');
12248 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12249 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12250 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12251 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12252 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12255 * Create a new TriggerField.
12256 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12257 * to the base TextField)
12259 Roo.bootstrap.TriggerField = function(config){
12260 this.mimicing = false;
12261 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12264 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12266 * @cfg {String} triggerClass A CSS class to apply to the trigger
12269 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12274 * @cfg {Boolean} removable (true|false) special filter default false
12278 /** @cfg {Boolean} grow @hide */
12279 /** @cfg {Number} growMin @hide */
12280 /** @cfg {Number} growMax @hide */
12286 autoSize: Roo.emptyFn,
12290 deferHeight : true,
12293 actionMode : 'wrap',
12298 getAutoCreate : function(){
12300 var align = this.labelAlign || this.parentLabelAlign();
12305 cls: 'form-group' //input-group
12312 type : this.inputType,
12313 cls : 'form-control',
12314 autocomplete: 'new-password',
12315 placeholder : this.placeholder || ''
12319 input.name = this.name;
12322 input.cls += ' input-' + this.size;
12325 if (this.disabled) {
12326 input.disabled=true;
12329 var inputblock = input;
12331 if(this.hasFeedback && !this.allowBlank){
12335 cls: 'glyphicon form-control-feedback'
12338 if(this.removable && !this.editable ){
12340 cls : 'has-feedback',
12346 cls : 'roo-combo-removable-btn close'
12353 cls : 'has-feedback',
12362 if(this.removable && !this.editable ){
12364 cls : 'roo-removable',
12370 cls : 'roo-combo-removable-btn close'
12377 if (this.before || this.after) {
12380 cls : 'input-group',
12384 inputblock.cn.push({
12386 cls : 'input-group-addon input-group-prepend input-group-text',
12391 inputblock.cn.push(input);
12393 if(this.hasFeedback && !this.allowBlank){
12394 inputblock.cls += ' has-feedback';
12395 inputblock.cn.push(feedback);
12399 inputblock.cn.push({
12401 cls : 'input-group-addon input-group-append input-group-text',
12410 var ibwrap = inputblock;
12415 cls: 'roo-select2-choices',
12419 cls: 'roo-select2-search-field',
12431 cls: 'roo-select2-container input-group',
12436 cls: 'form-hidden-field'
12442 if(!this.multiple && this.showToggleBtn){
12448 if (this.caret != false) {
12451 cls: 'fa fa-' + this.caret
12458 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12460 Roo.bootstrap.version == 3 ? caret : '',
12463 cls: 'combobox-clear',
12477 combobox.cls += ' roo-select2-container-multi';
12481 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12482 tooltip : 'This field is required'
12484 if (Roo.bootstrap.version == 4) {
12487 style : 'display:none'
12492 if (align ==='left' && this.fieldLabel.length) {
12494 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12501 cls : 'control-label',
12502 html : this.fieldLabel
12514 var labelCfg = cfg.cn[1];
12515 var contentCfg = cfg.cn[2];
12517 if(this.indicatorpos == 'right'){
12522 cls : 'control-label',
12526 html : this.fieldLabel
12540 labelCfg = cfg.cn[0];
12541 contentCfg = cfg.cn[1];
12544 if(this.labelWidth > 12){
12545 labelCfg.style = "width: " + this.labelWidth + 'px';
12548 if(this.labelWidth < 13 && this.labelmd == 0){
12549 this.labelmd = this.labelWidth;
12552 if(this.labellg > 0){
12553 labelCfg.cls += ' col-lg-' + this.labellg;
12554 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12557 if(this.labelmd > 0){
12558 labelCfg.cls += ' col-md-' + this.labelmd;
12559 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12562 if(this.labelsm > 0){
12563 labelCfg.cls += ' col-sm-' + this.labelsm;
12564 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12567 if(this.labelxs > 0){
12568 labelCfg.cls += ' col-xs-' + this.labelxs;
12569 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12572 } else if ( this.fieldLabel.length) {
12573 // Roo.log(" label");
12578 //cls : 'input-group-addon',
12579 html : this.fieldLabel
12587 if(this.indicatorpos == 'right'){
12595 html : this.fieldLabel
12609 // Roo.log(" no label && no align");
12616 ['xs','sm','md','lg'].map(function(size){
12617 if (settings[size]) {
12618 cfg.cls += ' col-' + size + '-' + settings[size];
12629 onResize : function(w, h){
12630 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12631 // if(typeof w == 'number'){
12632 // var x = w - this.trigger.getWidth();
12633 // this.inputEl().setWidth(this.adjustWidth('input', x));
12634 // this.trigger.setStyle('left', x+'px');
12639 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12642 getResizeEl : function(){
12643 return this.inputEl();
12647 getPositionEl : function(){
12648 return this.inputEl();
12652 alignErrorIcon : function(){
12653 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12657 initEvents : function(){
12661 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12662 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12663 if(!this.multiple && this.showToggleBtn){
12664 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12665 if(this.hideTrigger){
12666 this.trigger.setDisplayed(false);
12668 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12672 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12675 if(this.removable && !this.editable && !this.tickable){
12676 var close = this.closeTriggerEl();
12679 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12680 close.on('click', this.removeBtnClick, this, close);
12684 //this.trigger.addClassOnOver('x-form-trigger-over');
12685 //this.trigger.addClassOnClick('x-form-trigger-click');
12688 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12692 closeTriggerEl : function()
12694 var close = this.el.select('.roo-combo-removable-btn', true).first();
12695 return close ? close : false;
12698 removeBtnClick : function(e, h, el)
12700 e.preventDefault();
12702 if(this.fireEvent("remove", this) !== false){
12704 this.fireEvent("afterremove", this)
12708 createList : function()
12710 this.list = Roo.get(document.body).createChild({
12711 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12712 cls: 'typeahead typeahead-long dropdown-menu shadow',
12713 style: 'display:none'
12716 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12721 initTrigger : function(){
12726 onDestroy : function(){
12728 this.trigger.removeAllListeners();
12729 // this.trigger.remove();
12732 // this.wrap.remove();
12734 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12738 onFocus : function(){
12739 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12741 if(!this.mimicing){
12742 this.wrap.addClass('x-trigger-wrap-focus');
12743 this.mimicing = true;
12744 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12745 if(this.monitorTab){
12746 this.el.on("keydown", this.checkTab, this);
12753 checkTab : function(e){
12754 if(e.getKey() == e.TAB){
12755 this.triggerBlur();
12760 onBlur : function(){
12765 mimicBlur : function(e, t){
12767 if(!this.wrap.contains(t) && this.validateBlur()){
12768 this.triggerBlur();
12774 triggerBlur : function(){
12775 this.mimicing = false;
12776 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12777 if(this.monitorTab){
12778 this.el.un("keydown", this.checkTab, this);
12780 //this.wrap.removeClass('x-trigger-wrap-focus');
12781 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12785 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12786 validateBlur : function(e, t){
12791 onDisable : function(){
12792 this.inputEl().dom.disabled = true;
12793 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12795 // this.wrap.addClass('x-item-disabled');
12800 onEnable : function(){
12801 this.inputEl().dom.disabled = false;
12802 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12804 // this.el.removeClass('x-item-disabled');
12809 onShow : function(){
12810 var ae = this.getActionEl();
12813 ae.dom.style.display = '';
12814 ae.dom.style.visibility = 'visible';
12820 onHide : function(){
12821 var ae = this.getActionEl();
12822 ae.dom.style.display = 'none';
12826 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12827 * by an implementing function.
12829 * @param {EventObject} e
12831 onTriggerClick : Roo.emptyFn
12839 * @class Roo.bootstrap.CardUploader
12840 * @extends Roo.bootstrap.Button
12841 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12842 * @cfg {Number} errorTimeout default 3000
12843 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12844 * @cfg {Array} html The button text.
12848 * Create a new CardUploader
12849 * @param {Object} config The config object
12852 Roo.bootstrap.CardUploader = function(config){
12856 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12859 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12867 * When a image is clicked on - and needs to display a slideshow or similar..
12868 * @param {Roo.bootstrap.Card} this
12869 * @param {Object} The image information data
12875 * When a the download link is clicked
12876 * @param {Roo.bootstrap.Card} this
12877 * @param {Object} The image information data contains
12884 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12887 errorTimeout : 3000,
12891 fileCollection : false,
12894 getAutoCreate : function()
12898 cls :'form-group' ,
12903 //cls : 'input-group-addon',
12904 html : this.fieldLabel
12912 value : this.value,
12913 cls : 'd-none form-control'
12918 multiple : 'multiple',
12920 cls : 'd-none roo-card-upload-selector'
12924 cls : 'roo-card-uploader-button-container w-100 mb-2'
12927 cls : 'card-columns roo-card-uploader-container'
12937 getChildContainer : function() /// what children are added to.
12939 return this.containerEl;
12942 getButtonContainer : function() /// what children are added to.
12944 return this.el.select(".roo-card-uploader-button-container").first();
12947 initEvents : function()
12950 Roo.bootstrap.Input.prototype.initEvents.call(this);
12954 xns: Roo.bootstrap,
12957 container_method : 'getButtonContainer' ,
12958 html : this.html, // fix changable?
12961 'click' : function(btn, e) {
12970 this.urlAPI = (window.createObjectURL && window) ||
12971 (window.URL && URL.revokeObjectURL && URL) ||
12972 (window.webkitURL && webkitURL);
12977 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12979 this.selectorEl.on('change', this.onFileSelected, this);
12982 this.images.forEach(function(img) {
12985 this.images = false;
12987 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12993 onClick : function(e)
12995 e.preventDefault();
12997 this.selectorEl.dom.click();
13001 onFileSelected : function(e)
13003 e.preventDefault();
13005 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13009 Roo.each(this.selectorEl.dom.files, function(file){
13010 this.addFile(file);
13019 addFile : function(file)
13022 if(typeof(file) === 'string'){
13023 throw "Add file by name?"; // should not happen
13027 if(!file || !this.urlAPI){
13037 var url = _this.urlAPI.createObjectURL( file);
13040 id : Roo.bootstrap.CardUploader.ID--,
13041 is_uploaded : false,
13045 mimetype : file.type,
13053 * addCard - add an Attachment to the uploader
13054 * @param data - the data about the image to upload
13058 title : "Title of file",
13059 is_uploaded : false,
13060 src : "http://.....",
13061 srcfile : { the File upload object },
13062 mimetype : file.type,
13065 .. any other data...
13071 addCard : function (data)
13073 // hidden input element?
13074 // if the file is not an image...
13075 //then we need to use something other that and header_image
13080 xns : Roo.bootstrap,
13081 xtype : 'CardFooter',
13084 xns : Roo.bootstrap,
13090 xns : Roo.bootstrap,
13092 html : String.format("<small>{0}</small>", data.title),
13093 cls : 'col-10 text-left',
13098 click : function() {
13100 t.fireEvent( "download", t, data );
13106 xns : Roo.bootstrap,
13108 style: 'max-height: 28px; ',
13114 click : function() {
13115 t.removeCard(data.id)
13127 var cn = this.addxtype(
13130 xns : Roo.bootstrap,
13133 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13134 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13135 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13140 initEvents : function() {
13141 Roo.bootstrap.Card.prototype.initEvents.call(this);
13143 this.imgEl = this.el.select('.card-img-top').first();
13145 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13146 this.imgEl.set({ 'pointer' : 'cursor' });
13149 this.getCardFooter().addClass('p-1');
13156 // dont' really need ot update items.
13157 // this.items.push(cn);
13158 this.fileCollection.add(cn);
13160 if (!data.srcfile) {
13161 this.updateInput();
13166 var reader = new FileReader();
13167 reader.addEventListener("load", function() {
13168 data.srcdata = reader.result;
13171 reader.readAsDataURL(data.srcfile);
13176 removeCard : function(id)
13179 var card = this.fileCollection.get(id);
13180 card.data.is_deleted = 1;
13181 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13182 //this.fileCollection.remove(card);
13183 //this.items = this.items.filter(function(e) { return e != card });
13184 // dont' really need ot update items.
13185 card.el.dom.parentNode.removeChild(card.el.dom);
13186 this.updateInput();
13192 this.fileCollection.each(function(card) {
13193 if (card.el.dom && card.el.dom.parentNode) {
13194 card.el.dom.parentNode.removeChild(card.el.dom);
13197 this.fileCollection.clear();
13198 this.updateInput();
13201 updateInput : function()
13204 this.fileCollection.each(function(e) {
13208 this.inputEl().dom.value = JSON.stringify(data);
13218 Roo.bootstrap.CardUploader.ID = -1;/*
13220 * Ext JS Library 1.1.1
13221 * Copyright(c) 2006-2007, Ext JS, LLC.
13223 * Originally Released Under LGPL - original licence link has changed is not relivant.
13226 * <script type="text/javascript">
13231 * @class Roo.data.SortTypes
13233 * Defines the default sorting (casting?) comparison functions used when sorting data.
13235 Roo.data.SortTypes = {
13237 * Default sort that does nothing
13238 * @param {Mixed} s The value being converted
13239 * @return {Mixed} The comparison value
13241 none : function(s){
13246 * The regular expression used to strip tags
13250 stripTagsRE : /<\/?[^>]+>/gi,
13253 * Strips all HTML tags to sort on text only
13254 * @param {Mixed} s The value being converted
13255 * @return {String} The comparison value
13257 asText : function(s){
13258 return String(s).replace(this.stripTagsRE, "");
13262 * Strips all HTML tags to sort on text only - Case insensitive
13263 * @param {Mixed} s The value being converted
13264 * @return {String} The comparison value
13266 asUCText : function(s){
13267 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13271 * Case insensitive string
13272 * @param {Mixed} s The value being converted
13273 * @return {String} The comparison value
13275 asUCString : function(s) {
13276 return String(s).toUpperCase();
13281 * @param {Mixed} s The value being converted
13282 * @return {Number} The comparison value
13284 asDate : function(s) {
13288 if(s instanceof Date){
13289 return s.getTime();
13291 return Date.parse(String(s));
13296 * @param {Mixed} s The value being converted
13297 * @return {Float} The comparison value
13299 asFloat : function(s) {
13300 var val = parseFloat(String(s).replace(/,/g, ""));
13309 * @param {Mixed} s The value being converted
13310 * @return {Number} The comparison value
13312 asInt : function(s) {
13313 var val = parseInt(String(s).replace(/,/g, ""));
13321 * Ext JS Library 1.1.1
13322 * Copyright(c) 2006-2007, Ext JS, LLC.
13324 * Originally Released Under LGPL - original licence link has changed is not relivant.
13327 * <script type="text/javascript">
13331 * @class Roo.data.Record
13332 * Instances of this class encapsulate both record <em>definition</em> information, and record
13333 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13334 * to access Records cached in an {@link Roo.data.Store} object.<br>
13336 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13337 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13340 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13342 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13343 * {@link #create}. The parameters are the same.
13344 * @param {Array} data An associative Array of data values keyed by the field name.
13345 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13346 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13347 * not specified an integer id is generated.
13349 Roo.data.Record = function(data, id){
13350 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13355 * Generate a constructor for a specific record layout.
13356 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13357 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13358 * Each field definition object may contain the following properties: <ul>
13359 * <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,
13360 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13361 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13362 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13363 * is being used, then this is a string containing the javascript expression to reference the data relative to
13364 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13365 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13366 * this may be omitted.</p></li>
13367 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13368 * <ul><li>auto (Default, implies no conversion)</li>
13373 * <li>date</li></ul></p></li>
13374 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13375 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13376 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13377 * by the Reader into an object that will be stored in the Record. It is passed the
13378 * following parameters:<ul>
13379 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13381 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13383 * <br>usage:<br><pre><code>
13384 var TopicRecord = Roo.data.Record.create(
13385 {name: 'title', mapping: 'topic_title'},
13386 {name: 'author', mapping: 'username'},
13387 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13388 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13389 {name: 'lastPoster', mapping: 'user2'},
13390 {name: 'excerpt', mapping: 'post_text'}
13393 var myNewRecord = new TopicRecord({
13394 title: 'Do my job please',
13397 lastPost: new Date(),
13398 lastPoster: 'Animal',
13399 excerpt: 'No way dude!'
13401 myStore.add(myNewRecord);
13406 Roo.data.Record.create = function(o){
13407 var f = function(){
13408 f.superclass.constructor.apply(this, arguments);
13410 Roo.extend(f, Roo.data.Record);
13411 var p = f.prototype;
13412 p.fields = new Roo.util.MixedCollection(false, function(field){
13415 for(var i = 0, len = o.length; i < len; i++){
13416 p.fields.add(new Roo.data.Field(o[i]));
13418 f.getField = function(name){
13419 return p.fields.get(name);
13424 Roo.data.Record.AUTO_ID = 1000;
13425 Roo.data.Record.EDIT = 'edit';
13426 Roo.data.Record.REJECT = 'reject';
13427 Roo.data.Record.COMMIT = 'commit';
13429 Roo.data.Record.prototype = {
13431 * Readonly flag - true if this record has been modified.
13440 join : function(store){
13441 this.store = store;
13445 * Set the named field to the specified value.
13446 * @param {String} name The name of the field to set.
13447 * @param {Object} value The value to set the field to.
13449 set : function(name, value){
13450 if(this.data[name] == value){
13454 if(!this.modified){
13455 this.modified = {};
13457 if(typeof this.modified[name] == 'undefined'){
13458 this.modified[name] = this.data[name];
13460 this.data[name] = value;
13461 if(!this.editing && this.store){
13462 this.store.afterEdit(this);
13467 * Get the value of the named field.
13468 * @param {String} name The name of the field to get the value of.
13469 * @return {Object} The value of the field.
13471 get : function(name){
13472 return this.data[name];
13476 beginEdit : function(){
13477 this.editing = true;
13478 this.modified = {};
13482 cancelEdit : function(){
13483 this.editing = false;
13484 delete this.modified;
13488 endEdit : function(){
13489 this.editing = false;
13490 if(this.dirty && this.store){
13491 this.store.afterEdit(this);
13496 * Usually called by the {@link Roo.data.Store} which owns the Record.
13497 * Rejects all changes made to the Record since either creation, or the last commit operation.
13498 * Modified fields are reverted to their original values.
13500 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13501 * of reject operations.
13503 reject : function(){
13504 var m = this.modified;
13506 if(typeof m[n] != "function"){
13507 this.data[n] = m[n];
13510 this.dirty = false;
13511 delete this.modified;
13512 this.editing = false;
13514 this.store.afterReject(this);
13519 * Usually called by the {@link Roo.data.Store} which owns the Record.
13520 * Commits all changes made to the Record since either creation, or the last commit operation.
13522 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13523 * of commit operations.
13525 commit : function(){
13526 this.dirty = false;
13527 delete this.modified;
13528 this.editing = false;
13530 this.store.afterCommit(this);
13535 hasError : function(){
13536 return this.error != null;
13540 clearError : function(){
13545 * Creates a copy of this record.
13546 * @param {String} id (optional) A new record id if you don't want to use this record's id
13549 copy : function(newId) {
13550 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13554 * Ext JS Library 1.1.1
13555 * Copyright(c) 2006-2007, Ext JS, LLC.
13557 * Originally Released Under LGPL - original licence link has changed is not relivant.
13560 * <script type="text/javascript">
13566 * @class Roo.data.Store
13567 * @extends Roo.util.Observable
13568 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13569 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13571 * 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
13572 * has no knowledge of the format of the data returned by the Proxy.<br>
13574 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13575 * instances from the data object. These records are cached and made available through accessor functions.
13577 * Creates a new Store.
13578 * @param {Object} config A config object containing the objects needed for the Store to access data,
13579 * and read the data into Records.
13581 Roo.data.Store = function(config){
13582 this.data = new Roo.util.MixedCollection(false);
13583 this.data.getKey = function(o){
13586 this.baseParams = {};
13588 this.paramNames = {
13593 "multisort" : "_multisort"
13596 if(config && config.data){
13597 this.inlineData = config.data;
13598 delete config.data;
13601 Roo.apply(this, config);
13603 if(this.reader){ // reader passed
13604 this.reader = Roo.factory(this.reader, Roo.data);
13605 this.reader.xmodule = this.xmodule || false;
13606 if(!this.recordType){
13607 this.recordType = this.reader.recordType;
13609 if(this.reader.onMetaChange){
13610 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13614 if(this.recordType){
13615 this.fields = this.recordType.prototype.fields;
13617 this.modified = [];
13621 * @event datachanged
13622 * Fires when the data cache has changed, and a widget which is using this Store
13623 * as a Record cache should refresh its view.
13624 * @param {Store} this
13626 datachanged : true,
13628 * @event metachange
13629 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13630 * @param {Store} this
13631 * @param {Object} meta The JSON metadata
13636 * Fires when Records have been added to the Store
13637 * @param {Store} this
13638 * @param {Roo.data.Record[]} records The array of Records added
13639 * @param {Number} index The index at which the record(s) were added
13644 * Fires when a Record has been removed from the Store
13645 * @param {Store} this
13646 * @param {Roo.data.Record} record The Record that was removed
13647 * @param {Number} index The index at which the record was removed
13652 * Fires when a Record has been updated
13653 * @param {Store} this
13654 * @param {Roo.data.Record} record The Record that was updated
13655 * @param {String} operation The update operation being performed. Value may be one of:
13657 Roo.data.Record.EDIT
13658 Roo.data.Record.REJECT
13659 Roo.data.Record.COMMIT
13665 * Fires when the data cache has been cleared.
13666 * @param {Store} this
13670 * @event beforeload
13671 * Fires before a request is made for a new data object. If the beforeload handler returns false
13672 * the load action will be canceled.
13673 * @param {Store} this
13674 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13678 * @event beforeloadadd
13679 * Fires after a new set of Records has been loaded.
13680 * @param {Store} this
13681 * @param {Roo.data.Record[]} records The Records that were loaded
13682 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13684 beforeloadadd : true,
13687 * Fires after a new set of Records has been loaded, before they are added to the store.
13688 * @param {Store} this
13689 * @param {Roo.data.Record[]} records The Records that were loaded
13690 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13691 * @params {Object} return from reader
13695 * @event loadexception
13696 * Fires if an exception occurs in the Proxy during loading.
13697 * Called with the signature of the Proxy's "loadexception" event.
13698 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13701 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13702 * @param {Object} load options
13703 * @param {Object} jsonData from your request (normally this contains the Exception)
13705 loadexception : true
13709 this.proxy = Roo.factory(this.proxy, Roo.data);
13710 this.proxy.xmodule = this.xmodule || false;
13711 this.relayEvents(this.proxy, ["loadexception"]);
13713 this.sortToggle = {};
13714 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13716 Roo.data.Store.superclass.constructor.call(this);
13718 if(this.inlineData){
13719 this.loadData(this.inlineData);
13720 delete this.inlineData;
13724 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13726 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13727 * without a remote query - used by combo/forms at present.
13731 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13734 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13737 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13738 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13741 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13742 * on any HTTP request
13745 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13748 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13752 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13753 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13755 remoteSort : false,
13758 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13759 * loaded or when a record is removed. (defaults to false).
13761 pruneModifiedRecords : false,
13764 lastOptions : null,
13767 * Add Records to the Store and fires the add event.
13768 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13770 add : function(records){
13771 records = [].concat(records);
13772 for(var i = 0, len = records.length; i < len; i++){
13773 records[i].join(this);
13775 var index = this.data.length;
13776 this.data.addAll(records);
13777 this.fireEvent("add", this, records, index);
13781 * Remove a Record from the Store and fires the remove event.
13782 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13784 remove : function(record){
13785 var index = this.data.indexOf(record);
13786 this.data.removeAt(index);
13788 if(this.pruneModifiedRecords){
13789 this.modified.remove(record);
13791 this.fireEvent("remove", this, record, index);
13795 * Remove all Records from the Store and fires the clear event.
13797 removeAll : function(){
13799 if(this.pruneModifiedRecords){
13800 this.modified = [];
13802 this.fireEvent("clear", this);
13806 * Inserts Records to the Store at the given index and fires the add event.
13807 * @param {Number} index The start index at which to insert the passed Records.
13808 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13810 insert : function(index, records){
13811 records = [].concat(records);
13812 for(var i = 0, len = records.length; i < len; i++){
13813 this.data.insert(index, records[i]);
13814 records[i].join(this);
13816 this.fireEvent("add", this, records, index);
13820 * Get the index within the cache of the passed Record.
13821 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13822 * @return {Number} The index of the passed Record. Returns -1 if not found.
13824 indexOf : function(record){
13825 return this.data.indexOf(record);
13829 * Get the index within the cache of the Record with the passed id.
13830 * @param {String} id The id of the Record to find.
13831 * @return {Number} The index of the Record. Returns -1 if not found.
13833 indexOfId : function(id){
13834 return this.data.indexOfKey(id);
13838 * Get the Record with the specified id.
13839 * @param {String} id The id of the Record to find.
13840 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13842 getById : function(id){
13843 return this.data.key(id);
13847 * Get the Record at the specified index.
13848 * @param {Number} index The index of the Record to find.
13849 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13851 getAt : function(index){
13852 return this.data.itemAt(index);
13856 * Returns a range of Records between specified indices.
13857 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13858 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13859 * @return {Roo.data.Record[]} An array of Records
13861 getRange : function(start, end){
13862 return this.data.getRange(start, end);
13866 storeOptions : function(o){
13867 o = Roo.apply({}, o);
13870 this.lastOptions = o;
13874 * Loads the Record cache from the configured Proxy using the configured Reader.
13876 * If using remote paging, then the first load call must specify the <em>start</em>
13877 * and <em>limit</em> properties in the options.params property to establish the initial
13878 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13880 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13881 * and this call will return before the new data has been loaded. Perform any post-processing
13882 * in a callback function, or in a "load" event handler.</strong>
13884 * @param {Object} options An object containing properties which control loading options:<ul>
13885 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13886 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13887 * passed the following arguments:<ul>
13888 * <li>r : Roo.data.Record[]</li>
13889 * <li>options: Options object from the load call</li>
13890 * <li>success: Boolean success indicator</li></ul></li>
13891 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13892 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13895 load : function(options){
13896 options = options || {};
13897 if(this.fireEvent("beforeload", this, options) !== false){
13898 this.storeOptions(options);
13899 var p = Roo.apply(options.params || {}, this.baseParams);
13900 // if meta was not loaded from remote source.. try requesting it.
13901 if (!this.reader.metaFromRemote) {
13902 p._requestMeta = 1;
13904 if(this.sortInfo && this.remoteSort){
13905 var pn = this.paramNames;
13906 p[pn["sort"]] = this.sortInfo.field;
13907 p[pn["dir"]] = this.sortInfo.direction;
13909 if (this.multiSort) {
13910 var pn = this.paramNames;
13911 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13914 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13919 * Reloads the Record cache from the configured Proxy using the configured Reader and
13920 * the options from the last load operation performed.
13921 * @param {Object} options (optional) An object containing properties which may override the options
13922 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13923 * the most recently used options are reused).
13925 reload : function(options){
13926 this.load(Roo.applyIf(options||{}, this.lastOptions));
13930 // Called as a callback by the Reader during a load operation.
13931 loadRecords : function(o, options, success){
13932 if(!o || success === false){
13933 if(success !== false){
13934 this.fireEvent("load", this, [], options, o);
13936 if(options.callback){
13937 options.callback.call(options.scope || this, [], options, false);
13941 // if data returned failure - throw an exception.
13942 if (o.success === false) {
13943 // show a message if no listener is registered.
13944 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13945 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13947 // loadmask wil be hooked into this..
13948 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13951 var r = o.records, t = o.totalRecords || r.length;
13953 this.fireEvent("beforeloadadd", this, r, options, o);
13955 if(!options || options.add !== true){
13956 if(this.pruneModifiedRecords){
13957 this.modified = [];
13959 for(var i = 0, len = r.length; i < len; i++){
13963 this.data = this.snapshot;
13964 delete this.snapshot;
13967 this.data.addAll(r);
13968 this.totalLength = t;
13970 this.fireEvent("datachanged", this);
13972 this.totalLength = Math.max(t, this.data.length+r.length);
13976 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13978 var e = new Roo.data.Record({});
13980 e.set(this.parent.displayField, this.parent.emptyTitle);
13981 e.set(this.parent.valueField, '');
13986 this.fireEvent("load", this, r, options, o);
13987 if(options.callback){
13988 options.callback.call(options.scope || this, r, options, true);
13994 * Loads data from a passed data block. A Reader which understands the format of the data
13995 * must have been configured in the constructor.
13996 * @param {Object} data The data block from which to read the Records. The format of the data expected
13997 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13998 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14000 loadData : function(o, append){
14001 var r = this.reader.readRecords(o);
14002 this.loadRecords(r, {add: append}, true);
14006 * using 'cn' the nested child reader read the child array into it's child stores.
14007 * @param {Object} rec The record with a 'children array
14009 loadDataFromChildren : function(rec)
14011 this.loadData(this.reader.toLoadData(rec));
14016 * Gets the number of cached records.
14018 * <em>If using paging, this may not be the total size of the dataset. If the data object
14019 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14020 * the data set size</em>
14022 getCount : function(){
14023 return this.data.length || 0;
14027 * Gets the total number of records in the dataset as returned by the server.
14029 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14030 * the dataset size</em>
14032 getTotalCount : function(){
14033 return this.totalLength || 0;
14037 * Returns the sort state of the Store as an object with two properties:
14039 field {String} The name of the field by which the Records are sorted
14040 direction {String} The sort order, "ASC" or "DESC"
14043 getSortState : function(){
14044 return this.sortInfo;
14048 applySort : function(){
14049 if(this.sortInfo && !this.remoteSort){
14050 var s = this.sortInfo, f = s.field;
14051 var st = this.fields.get(f).sortType;
14052 var fn = function(r1, r2){
14053 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14054 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14056 this.data.sort(s.direction, fn);
14057 if(this.snapshot && this.snapshot != this.data){
14058 this.snapshot.sort(s.direction, fn);
14064 * Sets the default sort column and order to be used by the next load operation.
14065 * @param {String} fieldName The name of the field to sort by.
14066 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14068 setDefaultSort : function(field, dir){
14069 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14073 * Sort the Records.
14074 * If remote sorting is used, the sort is performed on the server, and the cache is
14075 * reloaded. If local sorting is used, the cache is sorted internally.
14076 * @param {String} fieldName The name of the field to sort by.
14077 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14079 sort : function(fieldName, dir){
14080 var f = this.fields.get(fieldName);
14082 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14084 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14085 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14090 this.sortToggle[f.name] = dir;
14091 this.sortInfo = {field: f.name, direction: dir};
14092 if(!this.remoteSort){
14094 this.fireEvent("datachanged", this);
14096 this.load(this.lastOptions);
14101 * Calls the specified function for each of the Records in the cache.
14102 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14103 * Returning <em>false</em> aborts and exits the iteration.
14104 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14106 each : function(fn, scope){
14107 this.data.each(fn, scope);
14111 * Gets all records modified since the last commit. Modified records are persisted across load operations
14112 * (e.g., during paging).
14113 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14115 getModifiedRecords : function(){
14116 return this.modified;
14120 createFilterFn : function(property, value, anyMatch){
14121 if(!value.exec){ // not a regex
14122 value = String(value);
14123 if(value.length == 0){
14126 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14128 return function(r){
14129 return value.test(r.data[property]);
14134 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14135 * @param {String} property A field on your records
14136 * @param {Number} start The record index to start at (defaults to 0)
14137 * @param {Number} end The last record index to include (defaults to length - 1)
14138 * @return {Number} The sum
14140 sum : function(property, start, end){
14141 var rs = this.data.items, v = 0;
14142 start = start || 0;
14143 end = (end || end === 0) ? end : rs.length-1;
14145 for(var i = start; i <= end; i++){
14146 v += (rs[i].data[property] || 0);
14152 * Filter the records by a specified property.
14153 * @param {String} field A field on your records
14154 * @param {String/RegExp} value Either a string that the field
14155 * should start with or a RegExp to test against the field
14156 * @param {Boolean} anyMatch True to match any part not just the beginning
14158 filter : function(property, value, anyMatch){
14159 var fn = this.createFilterFn(property, value, anyMatch);
14160 return fn ? this.filterBy(fn) : this.clearFilter();
14164 * Filter by a function. The specified function will be called with each
14165 * record in this data source. If the function returns true the record is included,
14166 * otherwise it is filtered.
14167 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14168 * @param {Object} scope (optional) The scope of the function (defaults to this)
14170 filterBy : function(fn, scope){
14171 this.snapshot = this.snapshot || this.data;
14172 this.data = this.queryBy(fn, scope||this);
14173 this.fireEvent("datachanged", this);
14177 * Query the records by a specified property.
14178 * @param {String} field A field on your records
14179 * @param {String/RegExp} value Either a string that the field
14180 * should start with or a RegExp to test against the field
14181 * @param {Boolean} anyMatch True to match any part not just the beginning
14182 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14184 query : function(property, value, anyMatch){
14185 var fn = this.createFilterFn(property, value, anyMatch);
14186 return fn ? this.queryBy(fn) : this.data.clone();
14190 * Query by a function. The specified function will be called with each
14191 * record in this data source. If the function returns true the record is included
14193 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14194 * @param {Object} scope (optional) The scope of the function (defaults to this)
14195 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14197 queryBy : function(fn, scope){
14198 var data = this.snapshot || this.data;
14199 return data.filterBy(fn, scope||this);
14203 * Collects unique values for a particular dataIndex from this store.
14204 * @param {String} dataIndex The property to collect
14205 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14206 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14207 * @return {Array} An array of the unique values
14209 collect : function(dataIndex, allowNull, bypassFilter){
14210 var d = (bypassFilter === true && this.snapshot) ?
14211 this.snapshot.items : this.data.items;
14212 var v, sv, r = [], l = {};
14213 for(var i = 0, len = d.length; i < len; i++){
14214 v = d[i].data[dataIndex];
14216 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14225 * Revert to a view of the Record cache with no filtering applied.
14226 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14228 clearFilter : function(suppressEvent){
14229 if(this.snapshot && this.snapshot != this.data){
14230 this.data = this.snapshot;
14231 delete this.snapshot;
14232 if(suppressEvent !== true){
14233 this.fireEvent("datachanged", this);
14239 afterEdit : function(record){
14240 if(this.modified.indexOf(record) == -1){
14241 this.modified.push(record);
14243 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14247 afterReject : function(record){
14248 this.modified.remove(record);
14249 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14253 afterCommit : function(record){
14254 this.modified.remove(record);
14255 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14259 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14260 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14262 commitChanges : function(){
14263 var m = this.modified.slice(0);
14264 this.modified = [];
14265 for(var i = 0, len = m.length; i < len; i++){
14271 * Cancel outstanding changes on all changed records.
14273 rejectChanges : function(){
14274 var m = this.modified.slice(0);
14275 this.modified = [];
14276 for(var i = 0, len = m.length; i < len; i++){
14281 onMetaChange : function(meta, rtype, o){
14282 this.recordType = rtype;
14283 this.fields = rtype.prototype.fields;
14284 delete this.snapshot;
14285 this.sortInfo = meta.sortInfo || this.sortInfo;
14286 this.modified = [];
14287 this.fireEvent('metachange', this, this.reader.meta);
14290 moveIndex : function(data, type)
14292 var index = this.indexOf(data);
14294 var newIndex = index + type;
14298 this.insert(newIndex, data);
14303 * Ext JS Library 1.1.1
14304 * Copyright(c) 2006-2007, Ext JS, LLC.
14306 * Originally Released Under LGPL - original licence link has changed is not relivant.
14309 * <script type="text/javascript">
14313 * @class Roo.data.SimpleStore
14314 * @extends Roo.data.Store
14315 * Small helper class to make creating Stores from Array data easier.
14316 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14317 * @cfg {Array} fields An array of field definition objects, or field name strings.
14318 * @cfg {Object} an existing reader (eg. copied from another store)
14319 * @cfg {Array} data The multi-dimensional array of data
14321 * @param {Object} config
14323 Roo.data.SimpleStore = function(config)
14325 Roo.data.SimpleStore.superclass.constructor.call(this, {
14327 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14330 Roo.data.Record.create(config.fields)
14332 proxy : new Roo.data.MemoryProxy(config.data)
14336 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14338 * Ext JS Library 1.1.1
14339 * Copyright(c) 2006-2007, Ext JS, LLC.
14341 * Originally Released Under LGPL - original licence link has changed is not relivant.
14344 * <script type="text/javascript">
14349 * @extends Roo.data.Store
14350 * @class Roo.data.JsonStore
14351 * Small helper class to make creating Stores for JSON data easier. <br/>
14353 var store = new Roo.data.JsonStore({
14354 url: 'get-images.php',
14356 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14359 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14360 * JsonReader and HttpProxy (unless inline data is provided).</b>
14361 * @cfg {Array} fields An array of field definition objects, or field name strings.
14363 * @param {Object} config
14365 Roo.data.JsonStore = function(c){
14366 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14367 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14368 reader: new Roo.data.JsonReader(c, c.fields)
14371 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14373 * Ext JS Library 1.1.1
14374 * Copyright(c) 2006-2007, Ext JS, LLC.
14376 * Originally Released Under LGPL - original licence link has changed is not relivant.
14379 * <script type="text/javascript">
14383 Roo.data.Field = function(config){
14384 if(typeof config == "string"){
14385 config = {name: config};
14387 Roo.apply(this, config);
14390 this.type = "auto";
14393 var st = Roo.data.SortTypes;
14394 // named sortTypes are supported, here we look them up
14395 if(typeof this.sortType == "string"){
14396 this.sortType = st[this.sortType];
14399 // set default sortType for strings and dates
14400 if(!this.sortType){
14403 this.sortType = st.asUCString;
14406 this.sortType = st.asDate;
14409 this.sortType = st.none;
14414 var stripRe = /[\$,%]/g;
14416 // prebuilt conversion function for this field, instead of
14417 // switching every time we're reading a value
14419 var cv, dateFormat = this.dateFormat;
14424 cv = function(v){ return v; };
14427 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14431 return v !== undefined && v !== null && v !== '' ?
14432 parseInt(String(v).replace(stripRe, ""), 10) : '';
14437 return v !== undefined && v !== null && v !== '' ?
14438 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14443 cv = function(v){ return v === true || v === "true" || v == 1; };
14450 if(v instanceof Date){
14454 if(dateFormat == "timestamp"){
14455 return new Date(v*1000);
14457 return Date.parseDate(v, dateFormat);
14459 var parsed = Date.parse(v);
14460 return parsed ? new Date(parsed) : null;
14469 Roo.data.Field.prototype = {
14477 * Ext JS Library 1.1.1
14478 * Copyright(c) 2006-2007, Ext JS, LLC.
14480 * Originally Released Under LGPL - original licence link has changed is not relivant.
14483 * <script type="text/javascript">
14486 // Base class for reading structured data from a data source. This class is intended to be
14487 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14490 * @class Roo.data.DataReader
14491 * Base class for reading structured data from a data source. This class is intended to be
14492 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14495 Roo.data.DataReader = function(meta, recordType){
14499 this.recordType = recordType instanceof Array ?
14500 Roo.data.Record.create(recordType) : recordType;
14503 Roo.data.DataReader.prototype = {
14506 readerType : 'Data',
14508 * Create an empty record
14509 * @param {Object} data (optional) - overlay some values
14510 * @return {Roo.data.Record} record created.
14512 newRow : function(d) {
14514 this.recordType.prototype.fields.each(function(c) {
14516 case 'int' : da[c.name] = 0; break;
14517 case 'date' : da[c.name] = new Date(); break;
14518 case 'float' : da[c.name] = 0.0; break;
14519 case 'boolean' : da[c.name] = false; break;
14520 default : da[c.name] = ""; break;
14524 return new this.recordType(Roo.apply(da, d));
14530 * Ext JS Library 1.1.1
14531 * Copyright(c) 2006-2007, Ext JS, LLC.
14533 * Originally Released Under LGPL - original licence link has changed is not relivant.
14536 * <script type="text/javascript">
14540 * @class Roo.data.DataProxy
14541 * @extends Roo.data.Observable
14542 * This class is an abstract base class for implementations which provide retrieval of
14543 * unformatted data objects.<br>
14545 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14546 * (of the appropriate type which knows how to parse the data object) to provide a block of
14547 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14549 * Custom implementations must implement the load method as described in
14550 * {@link Roo.data.HttpProxy#load}.
14552 Roo.data.DataProxy = function(){
14555 * @event beforeload
14556 * Fires before a network request is made to retrieve a data object.
14557 * @param {Object} This DataProxy object.
14558 * @param {Object} params The params parameter to the load function.
14563 * Fires before the load method's callback is called.
14564 * @param {Object} This DataProxy object.
14565 * @param {Object} o The data object.
14566 * @param {Object} arg The callback argument object passed to the load function.
14570 * @event loadexception
14571 * Fires if an Exception occurs during data retrieval.
14572 * @param {Object} This DataProxy object.
14573 * @param {Object} o The data object.
14574 * @param {Object} arg The callback argument object passed to the load function.
14575 * @param {Object} e The Exception.
14577 loadexception : true
14579 Roo.data.DataProxy.superclass.constructor.call(this);
14582 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14585 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14589 * Ext JS Library 1.1.1
14590 * Copyright(c) 2006-2007, Ext JS, LLC.
14592 * Originally Released Under LGPL - original licence link has changed is not relivant.
14595 * <script type="text/javascript">
14598 * @class Roo.data.MemoryProxy
14599 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14600 * to the Reader when its load method is called.
14602 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14604 Roo.data.MemoryProxy = function(data){
14608 Roo.data.MemoryProxy.superclass.constructor.call(this);
14612 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14615 * Load data from the requested source (in this case an in-memory
14616 * data object passed to the constructor), read the data object into
14617 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14618 * process that block using the passed callback.
14619 * @param {Object} params This parameter is not used by the MemoryProxy class.
14620 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14621 * object into a block of Roo.data.Records.
14622 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14623 * The function must be passed <ul>
14624 * <li>The Record block object</li>
14625 * <li>The "arg" argument from the load function</li>
14626 * <li>A boolean success indicator</li>
14628 * @param {Object} scope The scope in which to call the callback
14629 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14631 load : function(params, reader, callback, scope, arg){
14632 params = params || {};
14635 result = reader.readRecords(params.data ? params.data :this.data);
14637 this.fireEvent("loadexception", this, arg, null, e);
14638 callback.call(scope, null, arg, false);
14641 callback.call(scope, result, arg, true);
14645 update : function(params, records){
14650 * Ext JS Library 1.1.1
14651 * Copyright(c) 2006-2007, Ext JS, LLC.
14653 * Originally Released Under LGPL - original licence link has changed is not relivant.
14656 * <script type="text/javascript">
14659 * @class Roo.data.HttpProxy
14660 * @extends Roo.data.DataProxy
14661 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14662 * configured to reference a certain URL.<br><br>
14664 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14665 * from which the running page was served.<br><br>
14667 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14669 * Be aware that to enable the browser to parse an XML document, the server must set
14670 * the Content-Type header in the HTTP response to "text/xml".
14672 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14673 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14674 * will be used to make the request.
14676 Roo.data.HttpProxy = function(conn){
14677 Roo.data.HttpProxy.superclass.constructor.call(this);
14678 // is conn a conn config or a real conn?
14680 this.useAjax = !conn || !conn.events;
14684 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14685 // thse are take from connection...
14688 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14691 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14692 * extra parameters to each request made by this object. (defaults to undefined)
14695 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14696 * to each request made by this object. (defaults to undefined)
14699 * @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)
14702 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14705 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14711 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14715 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14716 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14717 * a finer-grained basis than the DataProxy events.
14719 getConnection : function(){
14720 return this.useAjax ? Roo.Ajax : this.conn;
14724 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14725 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14726 * process that block using the passed callback.
14727 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14728 * for the request to the remote server.
14729 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14730 * object into a block of Roo.data.Records.
14731 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14732 * The function must be passed <ul>
14733 * <li>The Record block object</li>
14734 * <li>The "arg" argument from the load function</li>
14735 * <li>A boolean success indicator</li>
14737 * @param {Object} scope The scope in which to call the callback
14738 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14740 load : function(params, reader, callback, scope, arg){
14741 if(this.fireEvent("beforeload", this, params) !== false){
14743 params : params || {},
14745 callback : callback,
14750 callback : this.loadResponse,
14754 Roo.applyIf(o, this.conn);
14755 if(this.activeRequest){
14756 Roo.Ajax.abort(this.activeRequest);
14758 this.activeRequest = Roo.Ajax.request(o);
14760 this.conn.request(o);
14763 callback.call(scope||this, null, arg, false);
14768 loadResponse : function(o, success, response){
14769 delete this.activeRequest;
14771 this.fireEvent("loadexception", this, o, response);
14772 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14777 result = o.reader.read(response);
14779 this.fireEvent("loadexception", this, o, response, e);
14780 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14784 this.fireEvent("load", this, o, o.request.arg);
14785 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14789 update : function(dataSet){
14794 updateResponse : function(dataSet){
14799 * Ext JS Library 1.1.1
14800 * Copyright(c) 2006-2007, Ext JS, LLC.
14802 * Originally Released Under LGPL - original licence link has changed is not relivant.
14805 * <script type="text/javascript">
14809 * @class Roo.data.ScriptTagProxy
14810 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14811 * other than the originating domain of the running page.<br><br>
14813 * <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
14814 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14816 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14817 * source code that is used as the source inside a <script> tag.<br><br>
14819 * In order for the browser to process the returned data, the server must wrap the data object
14820 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14821 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14822 * depending on whether the callback name was passed:
14825 boolean scriptTag = false;
14826 String cb = request.getParameter("callback");
14829 response.setContentType("text/javascript");
14831 response.setContentType("application/x-json");
14833 Writer out = response.getWriter();
14835 out.write(cb + "(");
14837 out.print(dataBlock.toJsonString());
14844 * @param {Object} config A configuration object.
14846 Roo.data.ScriptTagProxy = function(config){
14847 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14848 Roo.apply(this, config);
14849 this.head = document.getElementsByTagName("head")[0];
14852 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14854 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14856 * @cfg {String} url The URL from which to request the data object.
14859 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14863 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14864 * the server the name of the callback function set up by the load call to process the returned data object.
14865 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14866 * javascript output which calls this named function passing the data object as its only parameter.
14868 callbackParam : "callback",
14870 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14871 * name to the request.
14876 * Load data from the configured URL, read the data object into
14877 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14878 * process that block using the passed callback.
14879 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14880 * for the request to the remote server.
14881 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14882 * object into a block of Roo.data.Records.
14883 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14884 * The function must be passed <ul>
14885 * <li>The Record block object</li>
14886 * <li>The "arg" argument from the load function</li>
14887 * <li>A boolean success indicator</li>
14889 * @param {Object} scope The scope in which to call the callback
14890 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14892 load : function(params, reader, callback, scope, arg){
14893 if(this.fireEvent("beforeload", this, params) !== false){
14895 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14897 var url = this.url;
14898 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14900 url += "&_dc=" + (new Date().getTime());
14902 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14905 cb : "stcCallback"+transId,
14906 scriptId : "stcScript"+transId,
14910 callback : callback,
14916 window[trans.cb] = function(o){
14917 conn.handleResponse(o, trans);
14920 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14922 if(this.autoAbort !== false){
14926 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14928 var script = document.createElement("script");
14929 script.setAttribute("src", url);
14930 script.setAttribute("type", "text/javascript");
14931 script.setAttribute("id", trans.scriptId);
14932 this.head.appendChild(script);
14934 this.trans = trans;
14936 callback.call(scope||this, null, arg, false);
14941 isLoading : function(){
14942 return this.trans ? true : false;
14946 * Abort the current server request.
14948 abort : function(){
14949 if(this.isLoading()){
14950 this.destroyTrans(this.trans);
14955 destroyTrans : function(trans, isLoaded){
14956 this.head.removeChild(document.getElementById(trans.scriptId));
14957 clearTimeout(trans.timeoutId);
14959 window[trans.cb] = undefined;
14961 delete window[trans.cb];
14964 // if hasn't been loaded, wait for load to remove it to prevent script error
14965 window[trans.cb] = function(){
14966 window[trans.cb] = undefined;
14968 delete window[trans.cb];
14975 handleResponse : function(o, trans){
14976 this.trans = false;
14977 this.destroyTrans(trans, true);
14980 result = trans.reader.readRecords(o);
14982 this.fireEvent("loadexception", this, o, trans.arg, e);
14983 trans.callback.call(trans.scope||window, null, trans.arg, false);
14986 this.fireEvent("load", this, o, trans.arg);
14987 trans.callback.call(trans.scope||window, result, trans.arg, true);
14991 handleFailure : function(trans){
14992 this.trans = false;
14993 this.destroyTrans(trans, false);
14994 this.fireEvent("loadexception", this, null, trans.arg);
14995 trans.callback.call(trans.scope||window, null, trans.arg, false);
14999 * Ext JS Library 1.1.1
15000 * Copyright(c) 2006-2007, Ext JS, LLC.
15002 * Originally Released Under LGPL - original licence link has changed is not relivant.
15005 * <script type="text/javascript">
15009 * @class Roo.data.JsonReader
15010 * @extends Roo.data.DataReader
15011 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15012 * based on mappings in a provided Roo.data.Record constructor.
15014 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15015 * in the reply previously.
15020 var RecordDef = Roo.data.Record.create([
15021 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15022 {name: 'occupation'} // This field will use "occupation" as the mapping.
15024 var myReader = new Roo.data.JsonReader({
15025 totalProperty: "results", // The property which contains the total dataset size (optional)
15026 root: "rows", // The property which contains an Array of row objects
15027 id: "id" // The property within each row object that provides an ID for the record (optional)
15031 * This would consume a JSON file like this:
15033 { 'results': 2, 'rows': [
15034 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15035 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15038 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15039 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15040 * paged from the remote server.
15041 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15042 * @cfg {String} root name of the property which contains the Array of row objects.
15043 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15044 * @cfg {Array} fields Array of field definition objects
15046 * Create a new JsonReader
15047 * @param {Object} meta Metadata configuration options
15048 * @param {Object} recordType Either an Array of field definition objects,
15049 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15051 Roo.data.JsonReader = function(meta, recordType){
15054 // set some defaults:
15055 Roo.applyIf(meta, {
15056 totalProperty: 'total',
15057 successProperty : 'success',
15062 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15064 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15066 readerType : 'Json',
15069 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15070 * Used by Store query builder to append _requestMeta to params.
15073 metaFromRemote : false,
15075 * This method is only used by a DataProxy which has retrieved data from a remote server.
15076 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15077 * @return {Object} data A data block which is used by an Roo.data.Store object as
15078 * a cache of Roo.data.Records.
15080 read : function(response){
15081 var json = response.responseText;
15083 var o = /* eval:var:o */ eval("("+json+")");
15085 throw {message: "JsonReader.read: Json object not found"};
15091 this.metaFromRemote = true;
15092 this.meta = o.metaData;
15093 this.recordType = Roo.data.Record.create(o.metaData.fields);
15094 this.onMetaChange(this.meta, this.recordType, o);
15096 return this.readRecords(o);
15099 // private function a store will implement
15100 onMetaChange : function(meta, recordType, o){
15107 simpleAccess: function(obj, subsc) {
15114 getJsonAccessor: function(){
15116 return function(expr) {
15118 return(re.test(expr))
15119 ? new Function("obj", "return obj." + expr)
15124 return Roo.emptyFn;
15129 * Create a data block containing Roo.data.Records from an XML document.
15130 * @param {Object} o An object which contains an Array of row objects in the property specified
15131 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15132 * which contains the total size of the dataset.
15133 * @return {Object} data A data block which is used by an Roo.data.Store object as
15134 * a cache of Roo.data.Records.
15136 readRecords : function(o){
15138 * After any data loads, the raw JSON data is available for further custom processing.
15142 var s = this.meta, Record = this.recordType,
15143 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15145 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15147 if(s.totalProperty) {
15148 this.getTotal = this.getJsonAccessor(s.totalProperty);
15150 if(s.successProperty) {
15151 this.getSuccess = this.getJsonAccessor(s.successProperty);
15153 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15155 var g = this.getJsonAccessor(s.id);
15156 this.getId = function(rec) {
15158 return (r === undefined || r === "") ? null : r;
15161 this.getId = function(){return null;};
15164 for(var jj = 0; jj < fl; jj++){
15166 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15167 this.ef[jj] = this.getJsonAccessor(map);
15171 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15172 if(s.totalProperty){
15173 var vt = parseInt(this.getTotal(o), 10);
15178 if(s.successProperty){
15179 var vs = this.getSuccess(o);
15180 if(vs === false || vs === 'false'){
15185 for(var i = 0; i < c; i++){
15188 var id = this.getId(n);
15189 for(var j = 0; j < fl; j++){
15191 var v = this.ef[j](n);
15193 Roo.log('missing convert for ' + f.name);
15197 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15199 var record = new Record(values, id);
15201 records[i] = record;
15207 totalRecords : totalRecords
15210 // used when loading children.. @see loadDataFromChildren
15211 toLoadData: function(rec)
15213 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15214 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15215 return { data : data, total : data.length };
15220 * Ext JS Library 1.1.1
15221 * Copyright(c) 2006-2007, Ext JS, LLC.
15223 * Originally Released Under LGPL - original licence link has changed is not relivant.
15226 * <script type="text/javascript">
15230 * @class Roo.data.ArrayReader
15231 * @extends Roo.data.DataReader
15232 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15233 * Each element of that Array represents a row of data fields. The
15234 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15235 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15239 var RecordDef = Roo.data.Record.create([
15240 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15241 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15243 var myReader = new Roo.data.ArrayReader({
15244 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15248 * This would consume an Array like this:
15250 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15254 * Create a new JsonReader
15255 * @param {Object} meta Metadata configuration options.
15256 * @param {Object|Array} recordType Either an Array of field definition objects
15258 * @cfg {Array} fields Array of field definition objects
15259 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15260 * as specified to {@link Roo.data.Record#create},
15261 * or an {@link Roo.data.Record} object
15264 * created using {@link Roo.data.Record#create}.
15266 Roo.data.ArrayReader = function(meta, recordType)
15268 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15271 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15274 * Create a data block containing Roo.data.Records from an XML document.
15275 * @param {Object} o An Array of row objects which represents the dataset.
15276 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15277 * a cache of Roo.data.Records.
15279 readRecords : function(o)
15281 var sid = this.meta ? this.meta.id : null;
15282 var recordType = this.recordType, fields = recordType.prototype.fields;
15285 for(var i = 0; i < root.length; i++){
15288 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15289 for(var j = 0, jlen = fields.length; j < jlen; j++){
15290 var f = fields.items[j];
15291 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15292 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15294 values[f.name] = v;
15296 var record = new recordType(values, id);
15298 records[records.length] = record;
15302 totalRecords : records.length
15305 // used when loading children.. @see loadDataFromChildren
15306 toLoadData: function(rec)
15308 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15309 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15320 * @class Roo.bootstrap.ComboBox
15321 * @extends Roo.bootstrap.TriggerField
15322 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15323 * @cfg {Boolean} append (true|false) default false
15324 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15325 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15326 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15327 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15328 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15329 * @cfg {Boolean} animate default true
15330 * @cfg {Boolean} emptyResultText only for touch device
15331 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15332 * @cfg {String} emptyTitle default ''
15333 * @cfg {Number} width fixed with? experimental
15335 * Create a new ComboBox.
15336 * @param {Object} config Configuration options
15338 Roo.bootstrap.ComboBox = function(config){
15339 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15343 * Fires when the dropdown list is expanded
15344 * @param {Roo.bootstrap.ComboBox} combo This combo box
15349 * Fires when the dropdown list is collapsed
15350 * @param {Roo.bootstrap.ComboBox} combo This combo box
15354 * @event beforeselect
15355 * Fires before a list item is selected. Return false to cancel the selection.
15356 * @param {Roo.bootstrap.ComboBox} combo This combo box
15357 * @param {Roo.data.Record} record The data record returned from the underlying store
15358 * @param {Number} index The index of the selected item in the dropdown list
15360 'beforeselect' : true,
15363 * Fires when a list item is selected
15364 * @param {Roo.bootstrap.ComboBox} combo This combo box
15365 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15366 * @param {Number} index The index of the selected item in the dropdown list
15370 * @event beforequery
15371 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15372 * The event object passed has these properties:
15373 * @param {Roo.bootstrap.ComboBox} combo This combo box
15374 * @param {String} query The query
15375 * @param {Boolean} forceAll true to force "all" query
15376 * @param {Boolean} cancel true to cancel the query
15377 * @param {Object} e The query event object
15379 'beforequery': true,
15382 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15383 * @param {Roo.bootstrap.ComboBox} combo This combo box
15388 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15389 * @param {Roo.bootstrap.ComboBox} combo This combo box
15390 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15395 * Fires when the remove value from the combobox array
15396 * @param {Roo.bootstrap.ComboBox} combo This combo box
15400 * @event afterremove
15401 * Fires when the remove value from the combobox array
15402 * @param {Roo.bootstrap.ComboBox} combo This combo box
15404 'afterremove' : true,
15406 * @event specialfilter
15407 * Fires when specialfilter
15408 * @param {Roo.bootstrap.ComboBox} combo This combo box
15410 'specialfilter' : true,
15413 * Fires when tick the element
15414 * @param {Roo.bootstrap.ComboBox} combo This combo box
15418 * @event touchviewdisplay
15419 * Fires when touch view require special display (default is using displayField)
15420 * @param {Roo.bootstrap.ComboBox} combo This combo box
15421 * @param {Object} cfg set html .
15423 'touchviewdisplay' : true
15428 this.tickItems = [];
15430 this.selectedIndex = -1;
15431 if(this.mode == 'local'){
15432 if(config.queryDelay === undefined){
15433 this.queryDelay = 10;
15435 if(config.minChars === undefined){
15441 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15444 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15445 * rendering into an Roo.Editor, defaults to false)
15448 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15449 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15452 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15455 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15456 * the dropdown list (defaults to undefined, with no header element)
15460 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15464 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15466 listWidth: undefined,
15468 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15469 * mode = 'remote' or 'text' if mode = 'local')
15471 displayField: undefined,
15474 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15475 * mode = 'remote' or 'value' if mode = 'local').
15476 * Note: use of a valueField requires the user make a selection
15477 * in order for a value to be mapped.
15479 valueField: undefined,
15481 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15486 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15487 * field's data value (defaults to the underlying DOM element's name)
15489 hiddenName: undefined,
15491 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15495 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15497 selectedClass: 'active',
15500 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15504 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15505 * anchor positions (defaults to 'tl-bl')
15507 listAlign: 'tl-bl?',
15509 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15513 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15514 * query specified by the allQuery config option (defaults to 'query')
15516 triggerAction: 'query',
15518 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15519 * (defaults to 4, does not apply if editable = false)
15523 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15524 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15528 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15529 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15533 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15534 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15538 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15539 * when editable = true (defaults to false)
15541 selectOnFocus:false,
15543 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15545 queryParam: 'query',
15547 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15548 * when mode = 'remote' (defaults to 'Loading...')
15550 loadingText: 'Loading...',
15552 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15556 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15560 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15561 * traditional select (defaults to true)
15565 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15569 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15573 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15574 * listWidth has a higher value)
15578 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15579 * allow the user to set arbitrary text into the field (defaults to false)
15581 forceSelection:false,
15583 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15584 * if typeAhead = true (defaults to 250)
15586 typeAheadDelay : 250,
15588 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15589 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15591 valueNotFoundText : undefined,
15593 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15595 blockFocus : false,
15598 * @cfg {Boolean} disableClear Disable showing of clear button.
15600 disableClear : false,
15602 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15604 alwaysQuery : false,
15607 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15612 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15614 invalidClass : "has-warning",
15617 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15619 validClass : "has-success",
15622 * @cfg {Boolean} specialFilter (true|false) special filter default false
15624 specialFilter : false,
15627 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15629 mobileTouchView : true,
15632 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15634 useNativeIOS : false,
15637 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15639 mobile_restrict_height : false,
15641 ios_options : false,
15653 btnPosition : 'right',
15654 triggerList : true,
15655 showToggleBtn : true,
15657 emptyResultText: 'Empty',
15658 triggerText : 'Select',
15662 // element that contains real text value.. (when hidden is used..)
15664 getAutoCreate : function()
15669 * Render classic select for iso
15672 if(Roo.isIOS && this.useNativeIOS){
15673 cfg = this.getAutoCreateNativeIOS();
15681 if(Roo.isTouch && this.mobileTouchView){
15682 cfg = this.getAutoCreateTouchView();
15689 if(!this.tickable){
15690 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15695 * ComboBox with tickable selections
15698 var align = this.labelAlign || this.parentLabelAlign();
15701 cls : 'form-group roo-combobox-tickable' //input-group
15704 var btn_text_select = '';
15705 var btn_text_done = '';
15706 var btn_text_cancel = '';
15708 if (this.btn_text_show) {
15709 btn_text_select = 'Select';
15710 btn_text_done = 'Done';
15711 btn_text_cancel = 'Cancel';
15716 cls : 'tickable-buttons',
15721 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15722 //html : this.triggerText
15723 html: btn_text_select
15729 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15731 html: btn_text_done
15737 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15739 html: btn_text_cancel
15745 buttons.cn.unshift({
15747 cls: 'roo-select2-search-field-input'
15753 Roo.each(buttons.cn, function(c){
15755 c.cls += ' btn-' + _this.size;
15758 if (_this.disabled) {
15765 style : 'display: contents',
15770 cls: 'form-hidden-field'
15774 cls: 'roo-select2-choices',
15778 cls: 'roo-select2-search-field',
15789 cls: 'roo-select2-container input-group roo-select2-container-multi',
15795 // cls: 'typeahead typeahead-long dropdown-menu',
15796 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15801 if(this.hasFeedback && !this.allowBlank){
15805 cls: 'glyphicon form-control-feedback'
15808 combobox.cn.push(feedback);
15815 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15816 tooltip : 'This field is required'
15818 if (Roo.bootstrap.version == 4) {
15821 style : 'display:none'
15824 if (align ==='left' && this.fieldLabel.length) {
15826 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15833 cls : 'control-label col-form-label',
15834 html : this.fieldLabel
15846 var labelCfg = cfg.cn[1];
15847 var contentCfg = cfg.cn[2];
15850 if(this.indicatorpos == 'right'){
15856 cls : 'control-label col-form-label',
15860 html : this.fieldLabel
15876 labelCfg = cfg.cn[0];
15877 contentCfg = cfg.cn[1];
15881 if(this.labelWidth > 12){
15882 labelCfg.style = "width: " + this.labelWidth + 'px';
15884 if(this.width * 1 > 0){
15885 contentCfg.style = "width: " + this.width + 'px';
15887 if(this.labelWidth < 13 && this.labelmd == 0){
15888 this.labelmd = this.labelWidth;
15891 if(this.labellg > 0){
15892 labelCfg.cls += ' col-lg-' + this.labellg;
15893 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15896 if(this.labelmd > 0){
15897 labelCfg.cls += ' col-md-' + this.labelmd;
15898 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15901 if(this.labelsm > 0){
15902 labelCfg.cls += ' col-sm-' + this.labelsm;
15903 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15906 if(this.labelxs > 0){
15907 labelCfg.cls += ' col-xs-' + this.labelxs;
15908 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15912 } else if ( this.fieldLabel.length) {
15913 // Roo.log(" label");
15918 //cls : 'input-group-addon',
15919 html : this.fieldLabel
15924 if(this.indicatorpos == 'right'){
15928 //cls : 'input-group-addon',
15929 html : this.fieldLabel
15939 // Roo.log(" no label && no align");
15946 ['xs','sm','md','lg'].map(function(size){
15947 if (settings[size]) {
15948 cfg.cls += ' col-' + size + '-' + settings[size];
15956 _initEventsCalled : false,
15959 initEvents: function()
15961 if (this._initEventsCalled) { // as we call render... prevent looping...
15964 this._initEventsCalled = true;
15967 throw "can not find store for combo";
15970 this.indicator = this.indicatorEl();
15972 this.store = Roo.factory(this.store, Roo.data);
15973 this.store.parent = this;
15975 // if we are building from html. then this element is so complex, that we can not really
15976 // use the rendered HTML.
15977 // so we have to trash and replace the previous code.
15978 if (Roo.XComponent.build_from_html) {
15979 // remove this element....
15980 var e = this.el.dom, k=0;
15981 while (e ) { e = e.previousSibling; ++k;}
15986 this.rendered = false;
15988 this.render(this.parent().getChildContainer(true), k);
15991 if(Roo.isIOS && this.useNativeIOS){
15992 this.initIOSView();
16000 if(Roo.isTouch && this.mobileTouchView){
16001 this.initTouchView();
16006 this.initTickableEvents();
16010 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16012 if(this.hiddenName){
16014 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16016 this.hiddenField.dom.value =
16017 this.hiddenValue !== undefined ? this.hiddenValue :
16018 this.value !== undefined ? this.value : '';
16020 // prevent input submission
16021 this.el.dom.removeAttribute('name');
16022 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16027 // this.el.dom.setAttribute('autocomplete', 'off');
16030 var cls = 'x-combo-list';
16032 //this.list = new Roo.Layer({
16033 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16039 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16040 _this.list.setWidth(lw);
16043 this.list.on('mouseover', this.onViewOver, this);
16044 this.list.on('mousemove', this.onViewMove, this);
16045 this.list.on('scroll', this.onViewScroll, this);
16048 this.list.swallowEvent('mousewheel');
16049 this.assetHeight = 0;
16052 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16053 this.assetHeight += this.header.getHeight();
16056 this.innerList = this.list.createChild({cls:cls+'-inner'});
16057 this.innerList.on('mouseover', this.onViewOver, this);
16058 this.innerList.on('mousemove', this.onViewMove, this);
16059 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16061 if(this.allowBlank && !this.pageSize && !this.disableClear){
16062 this.footer = this.list.createChild({cls:cls+'-ft'});
16063 this.pageTb = new Roo.Toolbar(this.footer);
16067 this.footer = this.list.createChild({cls:cls+'-ft'});
16068 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16069 {pageSize: this.pageSize});
16073 if (this.pageTb && this.allowBlank && !this.disableClear) {
16075 this.pageTb.add(new Roo.Toolbar.Fill(), {
16076 cls: 'x-btn-icon x-btn-clear',
16078 handler: function()
16081 _this.clearValue();
16082 _this.onSelect(false, -1);
16087 this.assetHeight += this.footer.getHeight();
16092 this.tpl = Roo.bootstrap.version == 4 ?
16093 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16094 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16097 this.view = new Roo.View(this.list, this.tpl, {
16098 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16100 //this.view.wrapEl.setDisplayed(false);
16101 this.view.on('click', this.onViewClick, this);
16104 this.store.on('beforeload', this.onBeforeLoad, this);
16105 this.store.on('load', this.onLoad, this);
16106 this.store.on('loadexception', this.onLoadException, this);
16108 if(this.resizable){
16109 this.resizer = new Roo.Resizable(this.list, {
16110 pinned:true, handles:'se'
16112 this.resizer.on('resize', function(r, w, h){
16113 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16114 this.listWidth = w;
16115 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16116 this.restrictHeight();
16118 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16121 if(!this.editable){
16122 this.editable = true;
16123 this.setEditable(false);
16128 if (typeof(this.events.add.listeners) != 'undefined') {
16130 this.addicon = this.wrap.createChild(
16131 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16133 this.addicon.on('click', function(e) {
16134 this.fireEvent('add', this);
16137 if (typeof(this.events.edit.listeners) != 'undefined') {
16139 this.editicon = this.wrap.createChild(
16140 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16141 if (this.addicon) {
16142 this.editicon.setStyle('margin-left', '40px');
16144 this.editicon.on('click', function(e) {
16146 // we fire even if inothing is selected..
16147 this.fireEvent('edit', this, this.lastData );
16153 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16154 "up" : function(e){
16155 this.inKeyMode = true;
16159 "down" : function(e){
16160 if(!this.isExpanded()){
16161 this.onTriggerClick();
16163 this.inKeyMode = true;
16168 "enter" : function(e){
16169 // this.onViewClick();
16173 if(this.fireEvent("specialkey", this, e)){
16174 this.onViewClick(false);
16180 "esc" : function(e){
16184 "tab" : function(e){
16187 if(this.fireEvent("specialkey", this, e)){
16188 this.onViewClick(false);
16196 doRelay : function(foo, bar, hname){
16197 if(hname == 'down' || this.scope.isExpanded()){
16198 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16207 this.queryDelay = Math.max(this.queryDelay || 10,
16208 this.mode == 'local' ? 10 : 250);
16211 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16213 if(this.typeAhead){
16214 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16216 if(this.editable !== false){
16217 this.inputEl().on("keyup", this.onKeyUp, this);
16219 if(this.forceSelection){
16220 this.inputEl().on('blur', this.doForce, this);
16224 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16225 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16229 initTickableEvents: function()
16233 if(this.hiddenName){
16235 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16237 this.hiddenField.dom.value =
16238 this.hiddenValue !== undefined ? this.hiddenValue :
16239 this.value !== undefined ? this.value : '';
16241 // prevent input submission
16242 this.el.dom.removeAttribute('name');
16243 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16248 // this.list = this.el.select('ul.dropdown-menu',true).first();
16250 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16251 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16252 if(this.triggerList){
16253 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16256 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16257 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16259 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16260 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16262 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16263 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16265 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16266 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16267 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16270 this.cancelBtn.hide();
16275 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16276 _this.list.setWidth(lw);
16279 this.list.on('mouseover', this.onViewOver, this);
16280 this.list.on('mousemove', this.onViewMove, this);
16282 this.list.on('scroll', this.onViewScroll, this);
16285 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16286 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16289 this.view = new Roo.View(this.list, this.tpl, {
16294 selectedClass: this.selectedClass
16297 //this.view.wrapEl.setDisplayed(false);
16298 this.view.on('click', this.onViewClick, this);
16302 this.store.on('beforeload', this.onBeforeLoad, this);
16303 this.store.on('load', this.onLoad, this);
16304 this.store.on('loadexception', this.onLoadException, this);
16307 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16308 "up" : function(e){
16309 this.inKeyMode = true;
16313 "down" : function(e){
16314 this.inKeyMode = true;
16318 "enter" : function(e){
16319 if(this.fireEvent("specialkey", this, e)){
16320 this.onViewClick(false);
16326 "esc" : function(e){
16327 this.onTickableFooterButtonClick(e, false, false);
16330 "tab" : function(e){
16331 this.fireEvent("specialkey", this, e);
16333 this.onTickableFooterButtonClick(e, false, false);
16340 doRelay : function(e, fn, key){
16341 if(this.scope.isExpanded()){
16342 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16351 this.queryDelay = Math.max(this.queryDelay || 10,
16352 this.mode == 'local' ? 10 : 250);
16355 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16357 if(this.typeAhead){
16358 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16361 if(this.editable !== false){
16362 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16365 this.indicator = this.indicatorEl();
16367 if(this.indicator){
16368 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16369 this.indicator.hide();
16374 onDestroy : function(){
16376 this.view.setStore(null);
16377 this.view.el.removeAllListeners();
16378 this.view.el.remove();
16379 this.view.purgeListeners();
16382 this.list.dom.innerHTML = '';
16386 this.store.un('beforeload', this.onBeforeLoad, this);
16387 this.store.un('load', this.onLoad, this);
16388 this.store.un('loadexception', this.onLoadException, this);
16390 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16394 fireKey : function(e){
16395 if(e.isNavKeyPress() && !this.list.isVisible()){
16396 this.fireEvent("specialkey", this, e);
16401 onResize: function(w, h)
16405 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16407 // if(typeof w != 'number'){
16408 // // we do not handle it!?!?
16411 // var tw = this.trigger.getWidth();
16412 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16413 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16415 // this.inputEl().setWidth( this.adjustWidth('input', x));
16417 // //this.trigger.setStyle('left', x+'px');
16419 // if(this.list && this.listWidth === undefined){
16420 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16421 // this.list.setWidth(lw);
16422 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16430 * Allow or prevent the user from directly editing the field text. If false is passed,
16431 * the user will only be able to select from the items defined in the dropdown list. This method
16432 * is the runtime equivalent of setting the 'editable' config option at config time.
16433 * @param {Boolean} value True to allow the user to directly edit the field text
16435 setEditable : function(value){
16436 if(value == this.editable){
16439 this.editable = value;
16441 this.inputEl().dom.setAttribute('readOnly', true);
16442 this.inputEl().on('mousedown', this.onTriggerClick, this);
16443 this.inputEl().addClass('x-combo-noedit');
16445 this.inputEl().dom.removeAttribute('readOnly');
16446 this.inputEl().un('mousedown', this.onTriggerClick, this);
16447 this.inputEl().removeClass('x-combo-noedit');
16453 onBeforeLoad : function(combo,opts){
16454 if(!this.hasFocus){
16458 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16460 this.restrictHeight();
16461 this.selectedIndex = -1;
16465 onLoad : function(){
16467 this.hasQuery = false;
16469 if(!this.hasFocus){
16473 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16474 this.loading.hide();
16477 if(this.store.getCount() > 0){
16480 this.restrictHeight();
16481 if(this.lastQuery == this.allQuery){
16482 if(this.editable && !this.tickable){
16483 this.inputEl().dom.select();
16487 !this.selectByValue(this.value, true) &&
16490 !this.store.lastOptions ||
16491 typeof(this.store.lastOptions.add) == 'undefined' ||
16492 this.store.lastOptions.add != true
16495 this.select(0, true);
16498 if(this.autoFocus){
16501 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16502 this.taTask.delay(this.typeAheadDelay);
16506 this.onEmptyResults();
16512 onLoadException : function()
16514 this.hasQuery = false;
16516 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16517 this.loading.hide();
16520 if(this.tickable && this.editable){
16525 // only causes errors at present
16526 //Roo.log(this.store.reader.jsonData);
16527 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16529 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16535 onTypeAhead : function(){
16536 if(this.store.getCount() > 0){
16537 var r = this.store.getAt(0);
16538 var newValue = r.data[this.displayField];
16539 var len = newValue.length;
16540 var selStart = this.getRawValue().length;
16542 if(selStart != len){
16543 this.setRawValue(newValue);
16544 this.selectText(selStart, newValue.length);
16550 onSelect : function(record, index){
16552 if(this.fireEvent('beforeselect', this, record, index) !== false){
16554 this.setFromData(index > -1 ? record.data : false);
16557 this.fireEvent('select', this, record, index);
16562 * Returns the currently selected field value or empty string if no value is set.
16563 * @return {String} value The selected value
16565 getValue : function()
16567 if(Roo.isIOS && this.useNativeIOS){
16568 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16572 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16575 if(this.valueField){
16576 return typeof this.value != 'undefined' ? this.value : '';
16578 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16582 getRawValue : function()
16584 if(Roo.isIOS && this.useNativeIOS){
16585 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16588 var v = this.inputEl().getValue();
16594 * Clears any text/value currently set in the field
16596 clearValue : function(){
16598 if(this.hiddenField){
16599 this.hiddenField.dom.value = '';
16602 this.setRawValue('');
16603 this.lastSelectionText = '';
16604 this.lastData = false;
16606 var close = this.closeTriggerEl();
16617 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16618 * will be displayed in the field. If the value does not match the data value of an existing item,
16619 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16620 * Otherwise the field will be blank (although the value will still be set).
16621 * @param {String} value The value to match
16623 setValue : function(v)
16625 if(Roo.isIOS && this.useNativeIOS){
16626 this.setIOSValue(v);
16636 if(this.valueField){
16637 var r = this.findRecord(this.valueField, v);
16639 text = r.data[this.displayField];
16640 }else if(this.valueNotFoundText !== undefined){
16641 text = this.valueNotFoundText;
16644 this.lastSelectionText = text;
16645 if(this.hiddenField){
16646 this.hiddenField.dom.value = v;
16648 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16651 var close = this.closeTriggerEl();
16654 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16660 * @property {Object} the last set data for the element
16665 * Sets the value of the field based on a object which is related to the record format for the store.
16666 * @param {Object} value the value to set as. or false on reset?
16668 setFromData : function(o){
16675 var dv = ''; // display value
16676 var vv = ''; // value value..
16678 if (this.displayField) {
16679 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16681 // this is an error condition!!!
16682 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16685 if(this.valueField){
16686 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16689 var close = this.closeTriggerEl();
16692 if(dv.length || vv * 1 > 0){
16694 this.blockFocus=true;
16700 if(this.hiddenField){
16701 this.hiddenField.dom.value = vv;
16703 this.lastSelectionText = dv;
16704 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16708 // no hidden field.. - we store the value in 'value', but still display
16709 // display field!!!!
16710 this.lastSelectionText = dv;
16711 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16718 reset : function(){
16719 // overridden so that last data is reset..
16726 this.setValue(this.originalValue);
16727 //this.clearInvalid();
16728 this.lastData = false;
16730 this.view.clearSelections();
16736 findRecord : function(prop, value){
16738 if(this.store.getCount() > 0){
16739 this.store.each(function(r){
16740 if(r.data[prop] == value){
16750 getName: function()
16752 // returns hidden if it's set..
16753 if (!this.rendered) {return ''};
16754 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16758 onViewMove : function(e, t){
16759 this.inKeyMode = false;
16763 onViewOver : function(e, t){
16764 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16767 var item = this.view.findItemFromChild(t);
16770 var index = this.view.indexOf(item);
16771 this.select(index, false);
16776 onViewClick : function(view, doFocus, el, e)
16778 var index = this.view.getSelectedIndexes()[0];
16780 var r = this.store.getAt(index);
16784 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16791 Roo.each(this.tickItems, function(v,k){
16793 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16795 _this.tickItems.splice(k, 1);
16797 if(typeof(e) == 'undefined' && view == false){
16798 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16810 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16811 this.tickItems.push(r.data);
16814 if(typeof(e) == 'undefined' && view == false){
16815 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16822 this.onSelect(r, index);
16824 if(doFocus !== false && !this.blockFocus){
16825 this.inputEl().focus();
16830 restrictHeight : function(){
16831 //this.innerList.dom.style.height = '';
16832 //var inner = this.innerList.dom;
16833 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16834 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16835 //this.list.beginUpdate();
16836 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16837 this.list.alignTo(this.inputEl(), this.listAlign);
16838 this.list.alignTo(this.inputEl(), this.listAlign);
16839 //this.list.endUpdate();
16843 onEmptyResults : function(){
16845 if(this.tickable && this.editable){
16846 this.hasFocus = false;
16847 this.restrictHeight();
16855 * Returns true if the dropdown list is expanded, else false.
16857 isExpanded : function(){
16858 return this.list.isVisible();
16862 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16863 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16864 * @param {String} value The data value of the item to select
16865 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16866 * selected item if it is not currently in view (defaults to true)
16867 * @return {Boolean} True if the value matched an item in the list, else false
16869 selectByValue : function(v, scrollIntoView){
16870 if(v !== undefined && v !== null){
16871 var r = this.findRecord(this.valueField || this.displayField, v);
16873 this.select(this.store.indexOf(r), scrollIntoView);
16881 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16882 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16883 * @param {Number} index The zero-based index of the list item to select
16884 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16885 * selected item if it is not currently in view (defaults to true)
16887 select : function(index, scrollIntoView){
16888 this.selectedIndex = index;
16889 this.view.select(index);
16890 if(scrollIntoView !== false){
16891 var el = this.view.getNode(index);
16893 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16896 this.list.scrollChildIntoView(el, false);
16902 selectNext : function(){
16903 var ct = this.store.getCount();
16905 if(this.selectedIndex == -1){
16907 }else if(this.selectedIndex < ct-1){
16908 this.select(this.selectedIndex+1);
16914 selectPrev : function(){
16915 var ct = this.store.getCount();
16917 if(this.selectedIndex == -1){
16919 }else if(this.selectedIndex != 0){
16920 this.select(this.selectedIndex-1);
16926 onKeyUp : function(e){
16927 if(this.editable !== false && !e.isSpecialKey()){
16928 this.lastKey = e.getKey();
16929 this.dqTask.delay(this.queryDelay);
16934 validateBlur : function(){
16935 return !this.list || !this.list.isVisible();
16939 initQuery : function(){
16941 var v = this.getRawValue();
16943 if(this.tickable && this.editable){
16944 v = this.tickableInputEl().getValue();
16951 doForce : function(){
16952 if(this.inputEl().dom.value.length > 0){
16953 this.inputEl().dom.value =
16954 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16960 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16961 * query allowing the query action to be canceled if needed.
16962 * @param {String} query The SQL query to execute
16963 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16964 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16965 * saved in the current store (defaults to false)
16967 doQuery : function(q, forceAll){
16969 if(q === undefined || q === null){
16974 forceAll: forceAll,
16978 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16983 forceAll = qe.forceAll;
16984 if(forceAll === true || (q.length >= this.minChars)){
16986 this.hasQuery = true;
16988 if(this.lastQuery != q || this.alwaysQuery){
16989 this.lastQuery = q;
16990 if(this.mode == 'local'){
16991 this.selectedIndex = -1;
16993 this.store.clearFilter();
16996 if(this.specialFilter){
16997 this.fireEvent('specialfilter', this);
17002 this.store.filter(this.displayField, q);
17005 this.store.fireEvent("datachanged", this.store);
17012 this.store.baseParams[this.queryParam] = q;
17014 var options = {params : this.getParams(q)};
17017 options.add = true;
17018 options.params.start = this.page * this.pageSize;
17021 this.store.load(options);
17024 * this code will make the page width larger, at the beginning, the list not align correctly,
17025 * we should expand the list on onLoad
17026 * so command out it
17031 this.selectedIndex = -1;
17036 this.loadNext = false;
17040 getParams : function(q){
17042 //p[this.queryParam] = q;
17046 p.limit = this.pageSize;
17052 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17054 collapse : function(){
17055 if(!this.isExpanded()){
17061 this.hasFocus = false;
17065 this.cancelBtn.hide();
17066 this.trigger.show();
17069 this.tickableInputEl().dom.value = '';
17070 this.tickableInputEl().blur();
17075 Roo.get(document).un('mousedown', this.collapseIf, this);
17076 Roo.get(document).un('mousewheel', this.collapseIf, this);
17077 if (!this.editable) {
17078 Roo.get(document).un('keydown', this.listKeyPress, this);
17080 this.fireEvent('collapse', this);
17086 collapseIf : function(e){
17087 var in_combo = e.within(this.el);
17088 var in_list = e.within(this.list);
17089 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17091 if (in_combo || in_list || is_list) {
17092 //e.stopPropagation();
17097 this.onTickableFooterButtonClick(e, false, false);
17105 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17107 expand : function(){
17109 if(this.isExpanded() || !this.hasFocus){
17113 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17114 this.list.setWidth(lw);
17120 this.restrictHeight();
17124 this.tickItems = Roo.apply([], this.item);
17127 this.cancelBtn.show();
17128 this.trigger.hide();
17131 this.tickableInputEl().focus();
17136 Roo.get(document).on('mousedown', this.collapseIf, this);
17137 Roo.get(document).on('mousewheel', this.collapseIf, this);
17138 if (!this.editable) {
17139 Roo.get(document).on('keydown', this.listKeyPress, this);
17142 this.fireEvent('expand', this);
17146 // Implements the default empty TriggerField.onTriggerClick function
17147 onTriggerClick : function(e)
17149 Roo.log('trigger click');
17151 if(this.disabled || !this.triggerList){
17156 this.loadNext = false;
17158 if(this.isExpanded()){
17160 if (!this.blockFocus) {
17161 this.inputEl().focus();
17165 this.hasFocus = true;
17166 if(this.triggerAction == 'all') {
17167 this.doQuery(this.allQuery, true);
17169 this.doQuery(this.getRawValue());
17171 if (!this.blockFocus) {
17172 this.inputEl().focus();
17177 onTickableTriggerClick : function(e)
17184 this.loadNext = false;
17185 this.hasFocus = true;
17187 if(this.triggerAction == 'all') {
17188 this.doQuery(this.allQuery, true);
17190 this.doQuery(this.getRawValue());
17194 onSearchFieldClick : function(e)
17196 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17197 this.onTickableFooterButtonClick(e, false, false);
17201 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17206 this.loadNext = false;
17207 this.hasFocus = true;
17209 if(this.triggerAction == 'all') {
17210 this.doQuery(this.allQuery, true);
17212 this.doQuery(this.getRawValue());
17216 listKeyPress : function(e)
17218 //Roo.log('listkeypress');
17219 // scroll to first matching element based on key pres..
17220 if (e.isSpecialKey()) {
17223 var k = String.fromCharCode(e.getKey()).toUpperCase();
17226 var csel = this.view.getSelectedNodes();
17227 var cselitem = false;
17229 var ix = this.view.indexOf(csel[0]);
17230 cselitem = this.store.getAt(ix);
17231 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17237 this.store.each(function(v) {
17239 // start at existing selection.
17240 if (cselitem.id == v.id) {
17246 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17247 match = this.store.indexOf(v);
17253 if (match === false) {
17254 return true; // no more action?
17257 this.view.select(match);
17258 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17259 sn.scrollIntoView(sn.dom.parentNode, false);
17262 onViewScroll : function(e, t){
17264 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){
17268 this.hasQuery = true;
17270 this.loading = this.list.select('.loading', true).first();
17272 if(this.loading === null){
17273 this.list.createChild({
17275 cls: 'loading roo-select2-more-results roo-select2-active',
17276 html: 'Loading more results...'
17279 this.loading = this.list.select('.loading', true).first();
17281 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17283 this.loading.hide();
17286 this.loading.show();
17291 this.loadNext = true;
17293 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17298 addItem : function(o)
17300 var dv = ''; // display value
17302 if (this.displayField) {
17303 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17305 // this is an error condition!!!
17306 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17313 var choice = this.choices.createChild({
17315 cls: 'roo-select2-search-choice',
17324 cls: 'roo-select2-search-choice-close fa fa-times',
17329 }, this.searchField);
17331 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17333 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17341 this.inputEl().dom.value = '';
17346 onRemoveItem : function(e, _self, o)
17348 e.preventDefault();
17350 this.lastItem = Roo.apply([], this.item);
17352 var index = this.item.indexOf(o.data) * 1;
17355 Roo.log('not this item?!');
17359 this.item.splice(index, 1);
17364 this.fireEvent('remove', this, e);
17370 syncValue : function()
17372 if(!this.item.length){
17379 Roo.each(this.item, function(i){
17380 if(_this.valueField){
17381 value.push(i[_this.valueField]);
17388 this.value = value.join(',');
17390 if(this.hiddenField){
17391 this.hiddenField.dom.value = this.value;
17394 this.store.fireEvent("datachanged", this.store);
17399 clearItem : function()
17401 if(!this.multiple){
17407 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17415 if(this.tickable && !Roo.isTouch){
17416 this.view.refresh();
17420 inputEl: function ()
17422 if(Roo.isIOS && this.useNativeIOS){
17423 return this.el.select('select.roo-ios-select', true).first();
17426 if(Roo.isTouch && this.mobileTouchView){
17427 return this.el.select('input.form-control',true).first();
17431 return this.searchField;
17434 return this.el.select('input.form-control',true).first();
17437 onTickableFooterButtonClick : function(e, btn, el)
17439 e.preventDefault();
17441 this.lastItem = Roo.apply([], this.item);
17443 if(btn && btn.name == 'cancel'){
17444 this.tickItems = Roo.apply([], this.item);
17453 Roo.each(this.tickItems, function(o){
17461 validate : function()
17463 if(this.getVisibilityEl().hasClass('hidden')){
17467 var v = this.getRawValue();
17470 v = this.getValue();
17473 if(this.disabled || this.allowBlank || v.length){
17478 this.markInvalid();
17482 tickableInputEl : function()
17484 if(!this.tickable || !this.editable){
17485 return this.inputEl();
17488 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17492 getAutoCreateTouchView : function()
17497 cls: 'form-group' //input-group
17503 type : this.inputType,
17504 cls : 'form-control x-combo-noedit',
17505 autocomplete: 'new-password',
17506 placeholder : this.placeholder || '',
17511 input.name = this.name;
17515 input.cls += ' input-' + this.size;
17518 if (this.disabled) {
17519 input.disabled = true;
17523 cls : 'roo-combobox-wrap',
17530 inputblock.cls += ' input-group';
17532 inputblock.cn.unshift({
17534 cls : 'input-group-addon input-group-prepend input-group-text',
17539 if(this.removable && !this.multiple){
17540 inputblock.cls += ' roo-removable';
17542 inputblock.cn.push({
17545 cls : 'roo-combo-removable-btn close'
17549 if(this.hasFeedback && !this.allowBlank){
17551 inputblock.cls += ' has-feedback';
17553 inputblock.cn.push({
17555 cls: 'glyphicon form-control-feedback'
17562 inputblock.cls += (this.before) ? '' : ' input-group';
17564 inputblock.cn.push({
17566 cls : 'input-group-addon input-group-append input-group-text',
17572 var ibwrap = inputblock;
17577 cls: 'roo-select2-choices',
17581 cls: 'roo-select2-search-field',
17594 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17599 cls: 'form-hidden-field'
17605 if(!this.multiple && this.showToggleBtn){
17611 if (this.caret != false) {
17614 cls: 'fa fa-' + this.caret
17621 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17623 Roo.bootstrap.version == 3 ? caret : '',
17626 cls: 'combobox-clear',
17640 combobox.cls += ' roo-select2-container-multi';
17643 var required = this.allowBlank ? {
17645 style: 'display: none'
17648 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17649 tooltip : 'This field is required'
17652 var align = this.labelAlign || this.parentLabelAlign();
17654 if (align ==='left' && this.fieldLabel.length) {
17660 cls : 'control-label col-form-label',
17661 html : this.fieldLabel
17665 cls : 'roo-combobox-wrap ',
17672 var labelCfg = cfg.cn[1];
17673 var contentCfg = cfg.cn[2];
17676 if(this.indicatorpos == 'right'){
17681 cls : 'control-label col-form-label',
17685 html : this.fieldLabel
17691 cls : "roo-combobox-wrap ",
17699 labelCfg = cfg.cn[0];
17700 contentCfg = cfg.cn[1];
17705 if(this.labelWidth > 12){
17706 labelCfg.style = "width: " + this.labelWidth + 'px';
17709 if(this.labelWidth < 13 && this.labelmd == 0){
17710 this.labelmd = this.labelWidth;
17713 if(this.labellg > 0){
17714 labelCfg.cls += ' col-lg-' + this.labellg;
17715 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17718 if(this.labelmd > 0){
17719 labelCfg.cls += ' col-md-' + this.labelmd;
17720 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17723 if(this.labelsm > 0){
17724 labelCfg.cls += ' col-sm-' + this.labelsm;
17725 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17728 if(this.labelxs > 0){
17729 labelCfg.cls += ' col-xs-' + this.labelxs;
17730 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17734 } else if ( this.fieldLabel.length) {
17739 cls : 'control-label',
17740 html : this.fieldLabel
17751 if(this.indicatorpos == 'right'){
17755 cls : 'control-label',
17756 html : this.fieldLabel,
17774 var settings = this;
17776 ['xs','sm','md','lg'].map(function(size){
17777 if (settings[size]) {
17778 cfg.cls += ' col-' + size + '-' + settings[size];
17785 initTouchView : function()
17787 this.renderTouchView();
17789 this.touchViewEl.on('scroll', function(){
17790 this.el.dom.scrollTop = 0;
17793 this.originalValue = this.getValue();
17795 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17797 this.inputEl().on("click", this.showTouchView, this);
17798 if (this.triggerEl) {
17799 this.triggerEl.on("click", this.showTouchView, this);
17803 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17804 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17806 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17808 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17809 this.store.on('load', this.onTouchViewLoad, this);
17810 this.store.on('loadexception', this.onTouchViewLoadException, this);
17812 if(this.hiddenName){
17814 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17816 this.hiddenField.dom.value =
17817 this.hiddenValue !== undefined ? this.hiddenValue :
17818 this.value !== undefined ? this.value : '';
17820 this.el.dom.removeAttribute('name');
17821 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17825 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17826 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17829 if(this.removable && !this.multiple){
17830 var close = this.closeTriggerEl();
17832 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17833 close.on('click', this.removeBtnClick, this, close);
17837 * fix the bug in Safari iOS8
17839 this.inputEl().on("focus", function(e){
17840 document.activeElement.blur();
17843 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17850 renderTouchView : function()
17852 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17853 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17855 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17856 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17858 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17859 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17860 this.touchViewBodyEl.setStyle('overflow', 'auto');
17862 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17863 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17865 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17866 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17870 showTouchView : function()
17876 this.touchViewHeaderEl.hide();
17878 if(this.modalTitle.length){
17879 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17880 this.touchViewHeaderEl.show();
17883 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17884 this.touchViewEl.show();
17886 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17888 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17889 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17891 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17893 if(this.modalTitle.length){
17894 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17897 this.touchViewBodyEl.setHeight(bodyHeight);
17901 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17903 this.touchViewEl.addClass(['in','show']);
17906 if(this._touchViewMask){
17907 Roo.get(document.body).addClass("x-body-masked");
17908 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17909 this._touchViewMask.setStyle('z-index', 10000);
17910 this._touchViewMask.addClass('show');
17913 this.doTouchViewQuery();
17917 hideTouchView : function()
17919 this.touchViewEl.removeClass(['in','show']);
17923 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17925 this.touchViewEl.setStyle('display', 'none');
17928 if(this._touchViewMask){
17929 this._touchViewMask.removeClass('show');
17930 Roo.get(document.body).removeClass("x-body-masked");
17934 setTouchViewValue : function()
17941 Roo.each(this.tickItems, function(o){
17946 this.hideTouchView();
17949 doTouchViewQuery : function()
17958 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17962 if(!this.alwaysQuery || this.mode == 'local'){
17963 this.onTouchViewLoad();
17970 onTouchViewBeforeLoad : function(combo,opts)
17976 onTouchViewLoad : function()
17978 if(this.store.getCount() < 1){
17979 this.onTouchViewEmptyResults();
17983 this.clearTouchView();
17985 var rawValue = this.getRawValue();
17987 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17989 this.tickItems = [];
17991 this.store.data.each(function(d, rowIndex){
17992 var row = this.touchViewListGroup.createChild(template);
17994 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17995 row.addClass(d.data.cls);
17998 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18001 html : d.data[this.displayField]
18004 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18005 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18008 row.removeClass('selected');
18009 if(!this.multiple && this.valueField &&
18010 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18013 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18014 row.addClass('selected');
18017 if(this.multiple && this.valueField &&
18018 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18022 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18023 this.tickItems.push(d.data);
18026 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18030 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18032 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18034 if(this.modalTitle.length){
18035 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18038 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18040 if(this.mobile_restrict_height && listHeight < bodyHeight){
18041 this.touchViewBodyEl.setHeight(listHeight);
18046 if(firstChecked && listHeight > bodyHeight){
18047 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18052 onTouchViewLoadException : function()
18054 this.hideTouchView();
18057 onTouchViewEmptyResults : function()
18059 this.clearTouchView();
18061 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18063 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18067 clearTouchView : function()
18069 this.touchViewListGroup.dom.innerHTML = '';
18072 onTouchViewClick : function(e, el, o)
18074 e.preventDefault();
18077 var rowIndex = o.rowIndex;
18079 var r = this.store.getAt(rowIndex);
18081 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18083 if(!this.multiple){
18084 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18085 c.dom.removeAttribute('checked');
18088 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18090 this.setFromData(r.data);
18092 var close = this.closeTriggerEl();
18098 this.hideTouchView();
18100 this.fireEvent('select', this, r, rowIndex);
18105 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18106 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18107 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18111 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18112 this.addItem(r.data);
18113 this.tickItems.push(r.data);
18117 getAutoCreateNativeIOS : function()
18120 cls: 'form-group' //input-group,
18125 cls : 'roo-ios-select'
18129 combobox.name = this.name;
18132 if (this.disabled) {
18133 combobox.disabled = true;
18136 var settings = this;
18138 ['xs','sm','md','lg'].map(function(size){
18139 if (settings[size]) {
18140 cfg.cls += ' col-' + size + '-' + settings[size];
18150 initIOSView : function()
18152 this.store.on('load', this.onIOSViewLoad, this);
18157 onIOSViewLoad : function()
18159 if(this.store.getCount() < 1){
18163 this.clearIOSView();
18165 if(this.allowBlank) {
18167 var default_text = '-- SELECT --';
18169 if(this.placeholder.length){
18170 default_text = this.placeholder;
18173 if(this.emptyTitle.length){
18174 default_text += ' - ' + this.emptyTitle + ' -';
18177 var opt = this.inputEl().createChild({
18180 html : default_text
18184 o[this.valueField] = 0;
18185 o[this.displayField] = default_text;
18187 this.ios_options.push({
18194 this.store.data.each(function(d, rowIndex){
18198 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18199 html = d.data[this.displayField];
18204 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18205 value = d.data[this.valueField];
18214 if(this.value == d.data[this.valueField]){
18215 option['selected'] = true;
18218 var opt = this.inputEl().createChild(option);
18220 this.ios_options.push({
18227 this.inputEl().on('change', function(){
18228 this.fireEvent('select', this);
18233 clearIOSView: function()
18235 this.inputEl().dom.innerHTML = '';
18237 this.ios_options = [];
18240 setIOSValue: function(v)
18244 if(!this.ios_options){
18248 Roo.each(this.ios_options, function(opts){
18250 opts.el.dom.removeAttribute('selected');
18252 if(opts.data[this.valueField] != v){
18256 opts.el.dom.setAttribute('selected', true);
18262 * @cfg {Boolean} grow
18266 * @cfg {Number} growMin
18270 * @cfg {Number} growMax
18279 Roo.apply(Roo.bootstrap.ComboBox, {
18283 cls: 'modal-header',
18305 cls: 'list-group-item',
18309 cls: 'roo-combobox-list-group-item-value'
18313 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18327 listItemCheckbox : {
18329 cls: 'list-group-item',
18333 cls: 'roo-combobox-list-group-item-value'
18337 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18353 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18358 cls: 'modal-footer',
18366 cls: 'col-xs-6 text-left',
18369 cls: 'btn btn-danger roo-touch-view-cancel',
18375 cls: 'col-xs-6 text-right',
18378 cls: 'btn btn-success roo-touch-view-ok',
18389 Roo.apply(Roo.bootstrap.ComboBox, {
18391 touchViewTemplate : {
18393 cls: 'modal fade roo-combobox-touch-view',
18397 cls: 'modal-dialog',
18398 style : 'position:fixed', // we have to fix position....
18402 cls: 'modal-content',
18404 Roo.bootstrap.ComboBox.header,
18405 Roo.bootstrap.ComboBox.body,
18406 Roo.bootstrap.ComboBox.footer
18415 * Ext JS Library 1.1.1
18416 * Copyright(c) 2006-2007, Ext JS, LLC.
18418 * Originally Released Under LGPL - original licence link has changed is not relivant.
18421 * <script type="text/javascript">
18426 * @extends Roo.util.Observable
18427 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18428 * This class also supports single and multi selection modes. <br>
18429 * Create a data model bound view:
18431 var store = new Roo.data.Store(...);
18433 var view = new Roo.View({
18435 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18437 singleSelect: true,
18438 selectedClass: "ydataview-selected",
18442 // listen for node click?
18443 view.on("click", function(vw, index, node, e){
18444 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18448 dataModel.load("foobar.xml");
18450 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18452 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18453 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18455 * Note: old style constructor is still suported (container, template, config)
18458 * Create a new View
18459 * @param {Object} config The config object
18462 Roo.View = function(config, depreciated_tpl, depreciated_config){
18464 this.parent = false;
18466 if (typeof(depreciated_tpl) == 'undefined') {
18467 // new way.. - universal constructor.
18468 Roo.apply(this, config);
18469 this.el = Roo.get(this.el);
18472 this.el = Roo.get(config);
18473 this.tpl = depreciated_tpl;
18474 Roo.apply(this, depreciated_config);
18476 this.wrapEl = this.el.wrap().wrap();
18477 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18480 if(typeof(this.tpl) == "string"){
18481 this.tpl = new Roo.Template(this.tpl);
18483 // support xtype ctors..
18484 this.tpl = new Roo.factory(this.tpl, Roo);
18488 this.tpl.compile();
18493 * @event beforeclick
18494 * Fires before a click is processed. Returns false to cancel the default action.
18495 * @param {Roo.View} this
18496 * @param {Number} index The index of the target node
18497 * @param {HTMLElement} node The target node
18498 * @param {Roo.EventObject} e The raw event object
18500 "beforeclick" : true,
18503 * Fires when a template node is clicked.
18504 * @param {Roo.View} this
18505 * @param {Number} index The index of the target node
18506 * @param {HTMLElement} node The target node
18507 * @param {Roo.EventObject} e The raw event object
18512 * Fires when a template node is double clicked.
18513 * @param {Roo.View} this
18514 * @param {Number} index The index of the target node
18515 * @param {HTMLElement} node The target node
18516 * @param {Roo.EventObject} e The raw event object
18520 * @event contextmenu
18521 * Fires when a template node is right clicked.
18522 * @param {Roo.View} this
18523 * @param {Number} index The index of the target node
18524 * @param {HTMLElement} node The target node
18525 * @param {Roo.EventObject} e The raw event object
18527 "contextmenu" : true,
18529 * @event selectionchange
18530 * Fires when the selected nodes change.
18531 * @param {Roo.View} this
18532 * @param {Array} selections Array of the selected nodes
18534 "selectionchange" : true,
18537 * @event beforeselect
18538 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18539 * @param {Roo.View} this
18540 * @param {HTMLElement} node The node to be selected
18541 * @param {Array} selections Array of currently selected nodes
18543 "beforeselect" : true,
18545 * @event preparedata
18546 * Fires on every row to render, to allow you to change the data.
18547 * @param {Roo.View} this
18548 * @param {Object} data to be rendered (change this)
18550 "preparedata" : true
18558 "click": this.onClick,
18559 "dblclick": this.onDblClick,
18560 "contextmenu": this.onContextMenu,
18564 this.selections = [];
18566 this.cmp = new Roo.CompositeElementLite([]);
18568 this.store = Roo.factory(this.store, Roo.data);
18569 this.setStore(this.store, true);
18572 if ( this.footer && this.footer.xtype) {
18574 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18576 this.footer.dataSource = this.store;
18577 this.footer.container = fctr;
18578 this.footer = Roo.factory(this.footer, Roo);
18579 fctr.insertFirst(this.el);
18581 // this is a bit insane - as the paging toolbar seems to detach the el..
18582 // dom.parentNode.parentNode.parentNode
18583 // they get detached?
18587 Roo.View.superclass.constructor.call(this);
18592 Roo.extend(Roo.View, Roo.util.Observable, {
18595 * @cfg {Roo.data.Store} store Data store to load data from.
18600 * @cfg {String|Roo.Element} el The container element.
18605 * @cfg {String|Roo.Template} tpl The template used by this View
18609 * @cfg {String} dataName the named area of the template to use as the data area
18610 * Works with domtemplates roo-name="name"
18614 * @cfg {String} selectedClass The css class to add to selected nodes
18616 selectedClass : "x-view-selected",
18618 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18623 * @cfg {String} text to display on mask (default Loading)
18627 * @cfg {Boolean} multiSelect Allow multiple selection
18629 multiSelect : false,
18631 * @cfg {Boolean} singleSelect Allow single selection
18633 singleSelect: false,
18636 * @cfg {Boolean} toggleSelect - selecting
18638 toggleSelect : false,
18641 * @cfg {Boolean} tickable - selecting
18646 * Returns the element this view is bound to.
18647 * @return {Roo.Element}
18649 getEl : function(){
18650 return this.wrapEl;
18656 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18658 refresh : function(){
18659 //Roo.log('refresh');
18662 // if we are using something like 'domtemplate', then
18663 // the what gets used is:
18664 // t.applySubtemplate(NAME, data, wrapping data..)
18665 // the outer template then get' applied with
18666 // the store 'extra data'
18667 // and the body get's added to the
18668 // roo-name="data" node?
18669 // <span class='roo-tpl-{name}'></span> ?????
18673 this.clearSelections();
18674 this.el.update("");
18676 var records = this.store.getRange();
18677 if(records.length < 1) {
18679 // is this valid?? = should it render a template??
18681 this.el.update(this.emptyText);
18685 if (this.dataName) {
18686 this.el.update(t.apply(this.store.meta)); //????
18687 el = this.el.child('.roo-tpl-' + this.dataName);
18690 for(var i = 0, len = records.length; i < len; i++){
18691 var data = this.prepareData(records[i].data, i, records[i]);
18692 this.fireEvent("preparedata", this, data, i, records[i]);
18694 var d = Roo.apply({}, data);
18697 Roo.apply(d, {'roo-id' : Roo.id()});
18701 Roo.each(this.parent.item, function(item){
18702 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18705 Roo.apply(d, {'roo-data-checked' : 'checked'});
18709 html[html.length] = Roo.util.Format.trim(
18711 t.applySubtemplate(this.dataName, d, this.store.meta) :
18718 el.update(html.join(""));
18719 this.nodes = el.dom.childNodes;
18720 this.updateIndexes(0);
18725 * Function to override to reformat the data that is sent to
18726 * the template for each node.
18727 * DEPRICATED - use the preparedata event handler.
18728 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18729 * a JSON object for an UpdateManager bound view).
18731 prepareData : function(data, index, record)
18733 this.fireEvent("preparedata", this, data, index, record);
18737 onUpdate : function(ds, record){
18738 // Roo.log('on update');
18739 this.clearSelections();
18740 var index = this.store.indexOf(record);
18741 var n = this.nodes[index];
18742 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18743 n.parentNode.removeChild(n);
18744 this.updateIndexes(index, index);
18750 onAdd : function(ds, records, index)
18752 //Roo.log(['on Add', ds, records, index] );
18753 this.clearSelections();
18754 if(this.nodes.length == 0){
18758 var n = this.nodes[index];
18759 for(var i = 0, len = records.length; i < len; i++){
18760 var d = this.prepareData(records[i].data, i, records[i]);
18762 this.tpl.insertBefore(n, d);
18765 this.tpl.append(this.el, d);
18768 this.updateIndexes(index);
18771 onRemove : function(ds, record, index){
18772 // Roo.log('onRemove');
18773 this.clearSelections();
18774 var el = this.dataName ?
18775 this.el.child('.roo-tpl-' + this.dataName) :
18778 el.dom.removeChild(this.nodes[index]);
18779 this.updateIndexes(index);
18783 * Refresh an individual node.
18784 * @param {Number} index
18786 refreshNode : function(index){
18787 this.onUpdate(this.store, this.store.getAt(index));
18790 updateIndexes : function(startIndex, endIndex){
18791 var ns = this.nodes;
18792 startIndex = startIndex || 0;
18793 endIndex = endIndex || ns.length - 1;
18794 for(var i = startIndex; i <= endIndex; i++){
18795 ns[i].nodeIndex = i;
18800 * Changes the data store this view uses and refresh the view.
18801 * @param {Store} store
18803 setStore : function(store, initial){
18804 if(!initial && this.store){
18805 this.store.un("datachanged", this.refresh);
18806 this.store.un("add", this.onAdd);
18807 this.store.un("remove", this.onRemove);
18808 this.store.un("update", this.onUpdate);
18809 this.store.un("clear", this.refresh);
18810 this.store.un("beforeload", this.onBeforeLoad);
18811 this.store.un("load", this.onLoad);
18812 this.store.un("loadexception", this.onLoad);
18816 store.on("datachanged", this.refresh, this);
18817 store.on("add", this.onAdd, this);
18818 store.on("remove", this.onRemove, this);
18819 store.on("update", this.onUpdate, this);
18820 store.on("clear", this.refresh, this);
18821 store.on("beforeload", this.onBeforeLoad, this);
18822 store.on("load", this.onLoad, this);
18823 store.on("loadexception", this.onLoad, this);
18831 * onbeforeLoad - masks the loading area.
18834 onBeforeLoad : function(store,opts)
18836 //Roo.log('onBeforeLoad');
18838 this.el.update("");
18840 this.el.mask(this.mask ? this.mask : "Loading" );
18842 onLoad : function ()
18849 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18850 * @param {HTMLElement} node
18851 * @return {HTMLElement} The template node
18853 findItemFromChild : function(node){
18854 var el = this.dataName ?
18855 this.el.child('.roo-tpl-' + this.dataName,true) :
18858 if(!node || node.parentNode == el){
18861 var p = node.parentNode;
18862 while(p && p != el){
18863 if(p.parentNode == el){
18872 onClick : function(e){
18873 var item = this.findItemFromChild(e.getTarget());
18875 var index = this.indexOf(item);
18876 if(this.onItemClick(item, index, e) !== false){
18877 this.fireEvent("click", this, index, item, e);
18880 this.clearSelections();
18885 onContextMenu : function(e){
18886 var item = this.findItemFromChild(e.getTarget());
18888 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18893 onDblClick : function(e){
18894 var item = this.findItemFromChild(e.getTarget());
18896 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18900 onItemClick : function(item, index, e)
18902 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18905 if (this.toggleSelect) {
18906 var m = this.isSelected(item) ? 'unselect' : 'select';
18909 _t[m](item, true, false);
18912 if(this.multiSelect || this.singleSelect){
18913 if(this.multiSelect && e.shiftKey && this.lastSelection){
18914 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18916 this.select(item, this.multiSelect && e.ctrlKey);
18917 this.lastSelection = item;
18920 if(!this.tickable){
18921 e.preventDefault();
18929 * Get the number of selected nodes.
18932 getSelectionCount : function(){
18933 return this.selections.length;
18937 * Get the currently selected nodes.
18938 * @return {Array} An array of HTMLElements
18940 getSelectedNodes : function(){
18941 return this.selections;
18945 * Get the indexes of the selected nodes.
18948 getSelectedIndexes : function(){
18949 var indexes = [], s = this.selections;
18950 for(var i = 0, len = s.length; i < len; i++){
18951 indexes.push(s[i].nodeIndex);
18957 * Clear all selections
18958 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18960 clearSelections : function(suppressEvent){
18961 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18962 this.cmp.elements = this.selections;
18963 this.cmp.removeClass(this.selectedClass);
18964 this.selections = [];
18965 if(!suppressEvent){
18966 this.fireEvent("selectionchange", this, this.selections);
18972 * Returns true if the passed node is selected
18973 * @param {HTMLElement/Number} node The node or node index
18974 * @return {Boolean}
18976 isSelected : function(node){
18977 var s = this.selections;
18981 node = this.getNode(node);
18982 return s.indexOf(node) !== -1;
18987 * @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
18988 * @param {Boolean} keepExisting (optional) true to keep existing selections
18989 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18991 select : function(nodeInfo, keepExisting, suppressEvent){
18992 if(nodeInfo instanceof Array){
18994 this.clearSelections(true);
18996 for(var i = 0, len = nodeInfo.length; i < len; i++){
18997 this.select(nodeInfo[i], true, true);
19001 var node = this.getNode(nodeInfo);
19002 if(!node || this.isSelected(node)){
19003 return; // already selected.
19006 this.clearSelections(true);
19009 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19010 Roo.fly(node).addClass(this.selectedClass);
19011 this.selections.push(node);
19012 if(!suppressEvent){
19013 this.fireEvent("selectionchange", this, this.selections);
19021 * @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
19022 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19023 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19025 unselect : function(nodeInfo, keepExisting, suppressEvent)
19027 if(nodeInfo instanceof Array){
19028 Roo.each(this.selections, function(s) {
19029 this.unselect(s, nodeInfo);
19033 var node = this.getNode(nodeInfo);
19034 if(!node || !this.isSelected(node)){
19035 //Roo.log("not selected");
19036 return; // not selected.
19040 Roo.each(this.selections, function(s) {
19042 Roo.fly(node).removeClass(this.selectedClass);
19049 this.selections= ns;
19050 this.fireEvent("selectionchange", this, this.selections);
19054 * Gets a template node.
19055 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19056 * @return {HTMLElement} The node or null if it wasn't found
19058 getNode : function(nodeInfo){
19059 if(typeof nodeInfo == "string"){
19060 return document.getElementById(nodeInfo);
19061 }else if(typeof nodeInfo == "number"){
19062 return this.nodes[nodeInfo];
19068 * Gets a range template nodes.
19069 * @param {Number} startIndex
19070 * @param {Number} endIndex
19071 * @return {Array} An array of nodes
19073 getNodes : function(start, end){
19074 var ns = this.nodes;
19075 start = start || 0;
19076 end = typeof end == "undefined" ? ns.length - 1 : end;
19079 for(var i = start; i <= end; i++){
19083 for(var i = start; i >= end; i--){
19091 * Finds the index of the passed node
19092 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19093 * @return {Number} The index of the node or -1
19095 indexOf : function(node){
19096 node = this.getNode(node);
19097 if(typeof node.nodeIndex == "number"){
19098 return node.nodeIndex;
19100 var ns = this.nodes;
19101 for(var i = 0, len = ns.length; i < len; i++){
19112 * based on jquery fullcalendar
19116 Roo.bootstrap = Roo.bootstrap || {};
19118 * @class Roo.bootstrap.Calendar
19119 * @extends Roo.bootstrap.Component
19120 * Bootstrap Calendar class
19121 * @cfg {Boolean} loadMask (true|false) default false
19122 * @cfg {Object} header generate the user specific header of the calendar, default false
19125 * Create a new Container
19126 * @param {Object} config The config object
19131 Roo.bootstrap.Calendar = function(config){
19132 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19136 * Fires when a date is selected
19137 * @param {DatePicker} this
19138 * @param {Date} date The selected date
19142 * @event monthchange
19143 * Fires when the displayed month changes
19144 * @param {DatePicker} this
19145 * @param {Date} date The selected month
19147 'monthchange': true,
19149 * @event evententer
19150 * Fires when mouse over an event
19151 * @param {Calendar} this
19152 * @param {event} Event
19154 'evententer': true,
19156 * @event eventleave
19157 * Fires when the mouse leaves an
19158 * @param {Calendar} this
19161 'eventleave': true,
19163 * @event eventclick
19164 * Fires when the mouse click an
19165 * @param {Calendar} this
19174 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19177 * @cfg {Number} startDay
19178 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19186 getAutoCreate : function(){
19189 var fc_button = function(name, corner, style, content ) {
19190 return Roo.apply({},{
19192 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19194 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19197 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19208 style : 'width:100%',
19215 cls : 'fc-header-left',
19217 fc_button('prev', 'left', 'arrow', '‹' ),
19218 fc_button('next', 'right', 'arrow', '›' ),
19219 { tag: 'span', cls: 'fc-header-space' },
19220 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19228 cls : 'fc-header-center',
19232 cls: 'fc-header-title',
19235 html : 'month / year'
19243 cls : 'fc-header-right',
19245 /* fc_button('month', 'left', '', 'month' ),
19246 fc_button('week', '', '', 'week' ),
19247 fc_button('day', 'right', '', 'day' )
19259 header = this.header;
19262 var cal_heads = function() {
19264 // fixme - handle this.
19266 for (var i =0; i < Date.dayNames.length; i++) {
19267 var d = Date.dayNames[i];
19270 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19271 html : d.substring(0,3)
19275 ret[0].cls += ' fc-first';
19276 ret[6].cls += ' fc-last';
19279 var cal_cell = function(n) {
19282 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19287 cls: 'fc-day-number',
19291 cls: 'fc-day-content',
19295 style: 'position: relative;' // height: 17px;
19307 var cal_rows = function() {
19310 for (var r = 0; r < 6; r++) {
19317 for (var i =0; i < Date.dayNames.length; i++) {
19318 var d = Date.dayNames[i];
19319 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19322 row.cn[0].cls+=' fc-first';
19323 row.cn[0].cn[0].style = 'min-height:90px';
19324 row.cn[6].cls+=' fc-last';
19328 ret[0].cls += ' fc-first';
19329 ret[4].cls += ' fc-prev-last';
19330 ret[5].cls += ' fc-last';
19337 cls: 'fc-border-separate',
19338 style : 'width:100%',
19346 cls : 'fc-first fc-last',
19364 cls : 'fc-content',
19365 style : "position: relative;",
19368 cls : 'fc-view fc-view-month fc-grid',
19369 style : 'position: relative',
19370 unselectable : 'on',
19373 cls : 'fc-event-container',
19374 style : 'position:absolute;z-index:8;top:0;left:0;'
19392 initEvents : function()
19395 throw "can not find store for calendar";
19401 style: "text-align:center",
19405 style: "background-color:white;width:50%;margin:250 auto",
19409 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19420 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19422 var size = this.el.select('.fc-content', true).first().getSize();
19423 this.maskEl.setSize(size.width, size.height);
19424 this.maskEl.enableDisplayMode("block");
19425 if(!this.loadMask){
19426 this.maskEl.hide();
19429 this.store = Roo.factory(this.store, Roo.data);
19430 this.store.on('load', this.onLoad, this);
19431 this.store.on('beforeload', this.onBeforeLoad, this);
19435 this.cells = this.el.select('.fc-day',true);
19436 //Roo.log(this.cells);
19437 this.textNodes = this.el.query('.fc-day-number');
19438 this.cells.addClassOnOver('fc-state-hover');
19440 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19441 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19442 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19443 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19445 this.on('monthchange', this.onMonthChange, this);
19447 this.update(new Date().clearTime());
19450 resize : function() {
19451 var sz = this.el.getSize();
19453 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19454 this.el.select('.fc-day-content div',true).setHeight(34);
19459 showPrevMonth : function(e){
19460 this.update(this.activeDate.add("mo", -1));
19462 showToday : function(e){
19463 this.update(new Date().clearTime());
19466 showNextMonth : function(e){
19467 this.update(this.activeDate.add("mo", 1));
19471 showPrevYear : function(){
19472 this.update(this.activeDate.add("y", -1));
19476 showNextYear : function(){
19477 this.update(this.activeDate.add("y", 1));
19482 update : function(date)
19484 var vd = this.activeDate;
19485 this.activeDate = date;
19486 // if(vd && this.el){
19487 // var t = date.getTime();
19488 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19489 // Roo.log('using add remove');
19491 // this.fireEvent('monthchange', this, date);
19493 // this.cells.removeClass("fc-state-highlight");
19494 // this.cells.each(function(c){
19495 // if(c.dateValue == t){
19496 // c.addClass("fc-state-highlight");
19497 // setTimeout(function(){
19498 // try{c.dom.firstChild.focus();}catch(e){}
19508 var days = date.getDaysInMonth();
19510 var firstOfMonth = date.getFirstDateOfMonth();
19511 var startingPos = firstOfMonth.getDay()-this.startDay;
19513 if(startingPos < this.startDay){
19517 var pm = date.add(Date.MONTH, -1);
19518 var prevStart = pm.getDaysInMonth()-startingPos;
19520 this.cells = this.el.select('.fc-day',true);
19521 this.textNodes = this.el.query('.fc-day-number');
19522 this.cells.addClassOnOver('fc-state-hover');
19524 var cells = this.cells.elements;
19525 var textEls = this.textNodes;
19527 Roo.each(cells, function(cell){
19528 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19531 days += startingPos;
19533 // convert everything to numbers so it's fast
19534 var day = 86400000;
19535 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19538 //Roo.log(prevStart);
19540 var today = new Date().clearTime().getTime();
19541 var sel = date.clearTime().getTime();
19542 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19543 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19544 var ddMatch = this.disabledDatesRE;
19545 var ddText = this.disabledDatesText;
19546 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19547 var ddaysText = this.disabledDaysText;
19548 var format = this.format;
19550 var setCellClass = function(cal, cell){
19554 //Roo.log('set Cell Class');
19556 var t = d.getTime();
19560 cell.dateValue = t;
19562 cell.className += " fc-today";
19563 cell.className += " fc-state-highlight";
19564 cell.title = cal.todayText;
19567 // disable highlight in other month..
19568 //cell.className += " fc-state-highlight";
19573 cell.className = " fc-state-disabled";
19574 cell.title = cal.minText;
19578 cell.className = " fc-state-disabled";
19579 cell.title = cal.maxText;
19583 if(ddays.indexOf(d.getDay()) != -1){
19584 cell.title = ddaysText;
19585 cell.className = " fc-state-disabled";
19588 if(ddMatch && format){
19589 var fvalue = d.dateFormat(format);
19590 if(ddMatch.test(fvalue)){
19591 cell.title = ddText.replace("%0", fvalue);
19592 cell.className = " fc-state-disabled";
19596 if (!cell.initialClassName) {
19597 cell.initialClassName = cell.dom.className;
19600 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19605 for(; i < startingPos; i++) {
19606 textEls[i].innerHTML = (++prevStart);
19607 d.setDate(d.getDate()+1);
19609 cells[i].className = "fc-past fc-other-month";
19610 setCellClass(this, cells[i]);
19615 for(; i < days; i++){
19616 intDay = i - startingPos + 1;
19617 textEls[i].innerHTML = (intDay);
19618 d.setDate(d.getDate()+1);
19620 cells[i].className = ''; // "x-date-active";
19621 setCellClass(this, cells[i]);
19625 for(; i < 42; i++) {
19626 textEls[i].innerHTML = (++extraDays);
19627 d.setDate(d.getDate()+1);
19629 cells[i].className = "fc-future fc-other-month";
19630 setCellClass(this, cells[i]);
19633 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19635 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19637 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19638 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19640 if(totalRows != 6){
19641 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19642 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19645 this.fireEvent('monthchange', this, date);
19649 if(!this.internalRender){
19650 var main = this.el.dom.firstChild;
19651 var w = main.offsetWidth;
19652 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19653 Roo.fly(main).setWidth(w);
19654 this.internalRender = true;
19655 // opera does not respect the auto grow header center column
19656 // then, after it gets a width opera refuses to recalculate
19657 // without a second pass
19658 if(Roo.isOpera && !this.secondPass){
19659 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19660 this.secondPass = true;
19661 this.update.defer(10, this, [date]);
19668 findCell : function(dt) {
19669 dt = dt.clearTime().getTime();
19671 this.cells.each(function(c){
19672 //Roo.log("check " +c.dateValue + '?=' + dt);
19673 if(c.dateValue == dt){
19683 findCells : function(ev) {
19684 var s = ev.start.clone().clearTime().getTime();
19686 var e= ev.end.clone().clearTime().getTime();
19689 this.cells.each(function(c){
19690 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19692 if(c.dateValue > e){
19695 if(c.dateValue < s){
19704 // findBestRow: function(cells)
19708 // for (var i =0 ; i < cells.length;i++) {
19709 // ret = Math.max(cells[i].rows || 0,ret);
19716 addItem : function(ev)
19718 // look for vertical location slot in
19719 var cells = this.findCells(ev);
19721 // ev.row = this.findBestRow(cells);
19723 // work out the location.
19727 for(var i =0; i < cells.length; i++) {
19729 cells[i].row = cells[0].row;
19732 cells[i].row = cells[i].row + 1;
19742 if (crow.start.getY() == cells[i].getY()) {
19744 crow.end = cells[i];
19761 cells[0].events.push(ev);
19763 this.calevents.push(ev);
19766 clearEvents: function() {
19768 if(!this.calevents){
19772 Roo.each(this.cells.elements, function(c){
19778 Roo.each(this.calevents, function(e) {
19779 Roo.each(e.els, function(el) {
19780 el.un('mouseenter' ,this.onEventEnter, this);
19781 el.un('mouseleave' ,this.onEventLeave, this);
19786 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19792 renderEvents: function()
19796 this.cells.each(function(c) {
19805 if(c.row != c.events.length){
19806 r = 4 - (4 - (c.row - c.events.length));
19809 c.events = ev.slice(0, r);
19810 c.more = ev.slice(r);
19812 if(c.more.length && c.more.length == 1){
19813 c.events.push(c.more.pop());
19816 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19820 this.cells.each(function(c) {
19822 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19825 for (var e = 0; e < c.events.length; e++){
19826 var ev = c.events[e];
19827 var rows = ev.rows;
19829 for(var i = 0; i < rows.length; i++) {
19831 // how many rows should it span..
19834 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19835 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19837 unselectable : "on",
19840 cls: 'fc-event-inner',
19844 // cls: 'fc-event-time',
19845 // html : cells.length > 1 ? '' : ev.time
19849 cls: 'fc-event-title',
19850 html : String.format('{0}', ev.title)
19857 cls: 'ui-resizable-handle ui-resizable-e',
19858 html : '  '
19865 cfg.cls += ' fc-event-start';
19867 if ((i+1) == rows.length) {
19868 cfg.cls += ' fc-event-end';
19871 var ctr = _this.el.select('.fc-event-container',true).first();
19872 var cg = ctr.createChild(cfg);
19874 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19875 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19877 var r = (c.more.length) ? 1 : 0;
19878 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19879 cg.setWidth(ebox.right - sbox.x -2);
19881 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19882 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19883 cg.on('click', _this.onEventClick, _this, ev);
19894 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19895 style : 'position: absolute',
19896 unselectable : "on",
19899 cls: 'fc-event-inner',
19903 cls: 'fc-event-title',
19911 cls: 'ui-resizable-handle ui-resizable-e',
19912 html : '  '
19918 var ctr = _this.el.select('.fc-event-container',true).first();
19919 var cg = ctr.createChild(cfg);
19921 var sbox = c.select('.fc-day-content',true).first().getBox();
19922 var ebox = c.select('.fc-day-content',true).first().getBox();
19924 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19925 cg.setWidth(ebox.right - sbox.x -2);
19927 cg.on('click', _this.onMoreEventClick, _this, c.more);
19937 onEventEnter: function (e, el,event,d) {
19938 this.fireEvent('evententer', this, el, event);
19941 onEventLeave: function (e, el,event,d) {
19942 this.fireEvent('eventleave', this, el, event);
19945 onEventClick: function (e, el,event,d) {
19946 this.fireEvent('eventclick', this, el, event);
19949 onMonthChange: function () {
19953 onMoreEventClick: function(e, el, more)
19957 this.calpopover.placement = 'right';
19958 this.calpopover.setTitle('More');
19960 this.calpopover.setContent('');
19962 var ctr = this.calpopover.el.select('.popover-content', true).first();
19964 Roo.each(more, function(m){
19966 cls : 'fc-event-hori fc-event-draggable',
19969 var cg = ctr.createChild(cfg);
19971 cg.on('click', _this.onEventClick, _this, m);
19974 this.calpopover.show(el);
19979 onLoad: function ()
19981 this.calevents = [];
19984 if(this.store.getCount() > 0){
19985 this.store.data.each(function(d){
19988 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19989 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19990 time : d.data.start_time,
19991 title : d.data.title,
19992 description : d.data.description,
19993 venue : d.data.venue
19998 this.renderEvents();
20000 if(this.calevents.length && this.loadMask){
20001 this.maskEl.hide();
20005 onBeforeLoad: function()
20007 this.clearEvents();
20009 this.maskEl.show();
20023 * @class Roo.bootstrap.Popover
20024 * @extends Roo.bootstrap.Component
20025 * Bootstrap Popover class
20026 * @cfg {String} html contents of the popover (or false to use children..)
20027 * @cfg {String} title of popover (or false to hide)
20028 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20029 * @cfg {String} trigger click || hover (or false to trigger manually)
20030 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20031 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20032 * - if false and it has a 'parent' then it will be automatically added to that element
20033 * - if string - Roo.get will be called
20034 * @cfg {Number} delay - delay before showing
20037 * Create a new Popover
20038 * @param {Object} config The config object
20041 Roo.bootstrap.Popover = function(config){
20042 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20048 * After the popover show
20050 * @param {Roo.bootstrap.Popover} this
20055 * After the popover hide
20057 * @param {Roo.bootstrap.Popover} this
20063 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20068 placement : 'right',
20069 trigger : 'hover', // hover
20075 can_build_overlaid : false,
20077 maskEl : false, // the mask element
20080 alignEl : false, // when show is called with an element - this get's stored.
20082 getChildContainer : function()
20084 return this.contentEl;
20087 getPopoverHeader : function()
20089 this.title = true; // flag not to hide it..
20090 this.headerEl.addClass('p-0');
20091 return this.headerEl
20095 getAutoCreate : function(){
20098 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20099 style: 'display:block',
20105 cls : 'popover-inner ',
20109 cls: 'popover-title popover-header',
20110 html : this.title === false ? '' : this.title
20113 cls : 'popover-content popover-body ' + (this.cls || ''),
20114 html : this.html || ''
20125 * @param {string} the title
20127 setTitle: function(str)
20131 this.headerEl.dom.innerHTML = str;
20136 * @param {string} the body content
20138 setContent: function(str)
20141 if (this.contentEl) {
20142 this.contentEl.dom.innerHTML = str;
20146 // as it get's added to the bottom of the page.
20147 onRender : function(ct, position)
20149 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20154 var cfg = Roo.apply({}, this.getAutoCreate());
20158 cfg.cls += ' ' + this.cls;
20161 cfg.style = this.style;
20163 //Roo.log("adding to ");
20164 this.el = Roo.get(document.body).createChild(cfg, position);
20165 // Roo.log(this.el);
20168 this.contentEl = this.el.select('.popover-content',true).first();
20169 this.headerEl = this.el.select('.popover-title',true).first();
20172 if(typeof(this.items) != 'undefined'){
20173 var items = this.items;
20176 for(var i =0;i < items.length;i++) {
20177 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20181 this.items = nitems;
20183 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20184 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20191 resizeMask : function()
20193 this.maskEl.setSize(
20194 Roo.lib.Dom.getViewWidth(true),
20195 Roo.lib.Dom.getViewHeight(true)
20199 initEvents : function()
20203 Roo.bootstrap.Popover.register(this);
20206 this.arrowEl = this.el.select('.arrow',true).first();
20207 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20208 this.el.enableDisplayMode('block');
20212 if (this.over === false && !this.parent()) {
20215 if (this.triggers === false) {
20220 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20221 var triggers = this.trigger ? this.trigger.split(' ') : [];
20222 Roo.each(triggers, function(trigger) {
20224 if (trigger == 'click') {
20225 on_el.on('click', this.toggle, this);
20226 } else if (trigger != 'manual') {
20227 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20228 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20230 on_el.on(eventIn ,this.enter, this);
20231 on_el.on(eventOut, this.leave, this);
20241 toggle : function () {
20242 this.hoverState == 'in' ? this.leave() : this.enter();
20245 enter : function () {
20247 clearTimeout(this.timeout);
20249 this.hoverState = 'in';
20251 if (!this.delay || !this.delay.show) {
20256 this.timeout = setTimeout(function () {
20257 if (_t.hoverState == 'in') {
20260 }, this.delay.show)
20263 leave : function() {
20264 clearTimeout(this.timeout);
20266 this.hoverState = 'out';
20268 if (!this.delay || !this.delay.hide) {
20273 this.timeout = setTimeout(function () {
20274 if (_t.hoverState == 'out') {
20277 }, this.delay.hide)
20281 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20282 * @param {string} (left|right|top|bottom) position
20284 show : function (on_el, placement)
20286 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20287 on_el = on_el || false; // default to false
20290 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20291 on_el = this.parent().el;
20292 } else if (this.over) {
20293 on_el = Roo.get(this.over);
20298 this.alignEl = Roo.get( on_el );
20301 this.render(document.body);
20307 if (this.title === false) {
20308 this.headerEl.hide();
20313 this.el.dom.style.display = 'block';
20316 if (this.alignEl) {
20317 this.updatePosition(this.placement, true);
20320 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20321 var es = this.el.getSize();
20322 var x = Roo.lib.Dom.getViewWidth()/2;
20323 var y = Roo.lib.Dom.getViewHeight()/2;
20324 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20329 //var arrow = this.el.select('.arrow',true).first();
20330 //arrow.set(align[2],
20332 this.el.addClass('in');
20336 this.hoverState = 'in';
20339 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20340 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20341 this.maskEl.dom.style.display = 'block';
20342 this.maskEl.addClass('show');
20344 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20346 this.fireEvent('show', this);
20350 * fire this manually after loading a grid in the table for example
20351 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20352 * @param {Boolean} try and move it if we cant get right position.
20354 updatePosition : function(placement, try_move)
20356 // allow for calling with no parameters
20357 placement = placement ? placement : this.placement;
20358 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20360 this.el.removeClass([
20361 'fade','top','bottom', 'left', 'right','in',
20362 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20364 this.el.addClass(placement + ' bs-popover-' + placement);
20366 if (!this.alignEl ) {
20370 switch (placement) {
20372 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20373 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20374 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20375 //normal display... or moved up/down.
20376 this.el.setXY(offset);
20377 var xy = this.alignEl.getAnchorXY('tr', false);
20379 this.arrowEl.setXY(xy);
20382 // continue through...
20383 return this.updatePosition('left', false);
20387 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20388 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20389 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20390 //normal display... or moved up/down.
20391 this.el.setXY(offset);
20392 var xy = this.alignEl.getAnchorXY('tl', false);
20393 xy[0]-=10;xy[1]+=5; // << fix me
20394 this.arrowEl.setXY(xy);
20398 return this.updatePosition('right', false);
20401 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20402 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20403 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20404 //normal display... or moved up/down.
20405 this.el.setXY(offset);
20406 var xy = this.alignEl.getAnchorXY('t', false);
20407 xy[1]-=10; // << fix me
20408 this.arrowEl.setXY(xy);
20412 return this.updatePosition('bottom', false);
20415 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20416 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20417 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20418 //normal display... or moved up/down.
20419 this.el.setXY(offset);
20420 var xy = this.alignEl.getAnchorXY('b', false);
20421 xy[1]+=2; // << fix me
20422 this.arrowEl.setXY(xy);
20426 return this.updatePosition('top', false);
20437 this.el.setXY([0,0]);
20438 this.el.removeClass('in');
20440 this.hoverState = null;
20441 this.maskEl.hide(); // always..
20442 this.fireEvent('hide', this);
20448 Roo.apply(Roo.bootstrap.Popover, {
20451 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20452 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20453 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20454 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20459 clickHander : false,
20463 onMouseDown : function(e)
20465 if (this.popups.length && !e.getTarget(".roo-popover")) {
20466 /// what is nothing is showing..
20475 register : function(popup)
20477 if (!Roo.bootstrap.Popover.clickHandler) {
20478 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20480 // hide other popups.
20481 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20482 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20483 this.hideAll(); //<< why?
20484 //this.popups.push(popup);
20486 hideAll : function()
20488 this.popups.forEach(function(p) {
20492 onShow : function() {
20493 Roo.bootstrap.Popover.popups.push(this);
20495 onHide : function() {
20496 Roo.bootstrap.Popover.popups.remove(this);
20502 * Card header - holder for the card header elements.
20507 * @class Roo.bootstrap.PopoverNav
20508 * @extends Roo.bootstrap.NavGroup
20509 * Bootstrap Popover header navigation class
20511 * Create a new Popover Header Navigation
20512 * @param {Object} config The config object
20515 Roo.bootstrap.PopoverNav = function(config){
20516 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20519 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20522 container_method : 'getPopoverHeader'
20540 * @class Roo.bootstrap.Progress
20541 * @extends Roo.bootstrap.Component
20542 * Bootstrap Progress class
20543 * @cfg {Boolean} striped striped of the progress bar
20544 * @cfg {Boolean} active animated of the progress bar
20548 * Create a new Progress
20549 * @param {Object} config The config object
20552 Roo.bootstrap.Progress = function(config){
20553 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20556 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20561 getAutoCreate : function(){
20569 cfg.cls += ' progress-striped';
20573 cfg.cls += ' active';
20592 * @class Roo.bootstrap.ProgressBar
20593 * @extends Roo.bootstrap.Component
20594 * Bootstrap ProgressBar class
20595 * @cfg {Number} aria_valuenow aria-value now
20596 * @cfg {Number} aria_valuemin aria-value min
20597 * @cfg {Number} aria_valuemax aria-value max
20598 * @cfg {String} label label for the progress bar
20599 * @cfg {String} panel (success | info | warning | danger )
20600 * @cfg {String} role role of the progress bar
20601 * @cfg {String} sr_only text
20605 * Create a new ProgressBar
20606 * @param {Object} config The config object
20609 Roo.bootstrap.ProgressBar = function(config){
20610 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20613 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20617 aria_valuemax : 100,
20623 getAutoCreate : function()
20628 cls: 'progress-bar',
20629 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20641 cfg.role = this.role;
20644 if(this.aria_valuenow){
20645 cfg['aria-valuenow'] = this.aria_valuenow;
20648 if(this.aria_valuemin){
20649 cfg['aria-valuemin'] = this.aria_valuemin;
20652 if(this.aria_valuemax){
20653 cfg['aria-valuemax'] = this.aria_valuemax;
20656 if(this.label && !this.sr_only){
20657 cfg.html = this.label;
20661 cfg.cls += ' progress-bar-' + this.panel;
20667 update : function(aria_valuenow)
20669 this.aria_valuenow = aria_valuenow;
20671 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20686 * @class Roo.bootstrap.TabGroup
20687 * @extends Roo.bootstrap.Column
20688 * Bootstrap Column class
20689 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20690 * @cfg {Boolean} carousel true to make the group behave like a carousel
20691 * @cfg {Boolean} bullets show bullets for the panels
20692 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20693 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20694 * @cfg {Boolean} showarrow (true|false) show arrow default true
20697 * Create a new TabGroup
20698 * @param {Object} config The config object
20701 Roo.bootstrap.TabGroup = function(config){
20702 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20704 this.navId = Roo.id();
20707 Roo.bootstrap.TabGroup.register(this);
20711 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20714 transition : false,
20719 slideOnTouch : false,
20722 getAutoCreate : function()
20724 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20726 cfg.cls += ' tab-content';
20728 if (this.carousel) {
20729 cfg.cls += ' carousel slide';
20732 cls : 'carousel-inner',
20736 if(this.bullets && !Roo.isTouch){
20739 cls : 'carousel-bullets',
20743 if(this.bullets_cls){
20744 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20751 cfg.cn[0].cn.push(bullets);
20754 if(this.showarrow){
20755 cfg.cn[0].cn.push({
20757 class : 'carousel-arrow',
20761 class : 'carousel-prev',
20765 class : 'fa fa-chevron-left'
20771 class : 'carousel-next',
20775 class : 'fa fa-chevron-right'
20788 initEvents: function()
20790 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20791 // this.el.on("touchstart", this.onTouchStart, this);
20794 if(this.autoslide){
20797 this.slideFn = window.setInterval(function() {
20798 _this.showPanelNext();
20802 if(this.showarrow){
20803 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20804 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20810 // onTouchStart : function(e, el, o)
20812 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20816 // this.showPanelNext();
20820 getChildContainer : function()
20822 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20826 * register a Navigation item
20827 * @param {Roo.bootstrap.NavItem} the navitem to add
20829 register : function(item)
20831 this.tabs.push( item);
20832 item.navId = this.navId; // not really needed..
20837 getActivePanel : function()
20840 Roo.each(this.tabs, function(t) {
20850 getPanelByName : function(n)
20853 Roo.each(this.tabs, function(t) {
20854 if (t.tabId == n) {
20862 indexOfPanel : function(p)
20865 Roo.each(this.tabs, function(t,i) {
20866 if (t.tabId == p.tabId) {
20875 * show a specific panel
20876 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20877 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20879 showPanel : function (pan)
20881 if(this.transition || typeof(pan) == 'undefined'){
20882 Roo.log("waiting for the transitionend");
20886 if (typeof(pan) == 'number') {
20887 pan = this.tabs[pan];
20890 if (typeof(pan) == 'string') {
20891 pan = this.getPanelByName(pan);
20894 var cur = this.getActivePanel();
20897 Roo.log('pan or acitve pan is undefined');
20901 if (pan.tabId == this.getActivePanel().tabId) {
20905 if (false === cur.fireEvent('beforedeactivate')) {
20909 if(this.bullets > 0 && !Roo.isTouch){
20910 this.setActiveBullet(this.indexOfPanel(pan));
20913 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20915 //class="carousel-item carousel-item-next carousel-item-left"
20917 this.transition = true;
20918 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20919 var lr = dir == 'next' ? 'left' : 'right';
20920 pan.el.addClass(dir); // or prev
20921 pan.el.addClass('carousel-item-' + dir); // or prev
20922 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20923 cur.el.addClass(lr); // or right
20924 pan.el.addClass(lr);
20925 cur.el.addClass('carousel-item-' +lr); // or right
20926 pan.el.addClass('carousel-item-' +lr);
20930 cur.el.on('transitionend', function() {
20931 Roo.log("trans end?");
20933 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20934 pan.setActive(true);
20936 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20937 cur.setActive(false);
20939 _this.transition = false;
20941 }, this, { single: true } );
20946 cur.setActive(false);
20947 pan.setActive(true);
20952 showPanelNext : function()
20954 var i = this.indexOfPanel(this.getActivePanel());
20956 if (i >= this.tabs.length - 1 && !this.autoslide) {
20960 if (i >= this.tabs.length - 1 && this.autoslide) {
20964 this.showPanel(this.tabs[i+1]);
20967 showPanelPrev : function()
20969 var i = this.indexOfPanel(this.getActivePanel());
20971 if (i < 1 && !this.autoslide) {
20975 if (i < 1 && this.autoslide) {
20976 i = this.tabs.length;
20979 this.showPanel(this.tabs[i-1]);
20983 addBullet: function()
20985 if(!this.bullets || Roo.isTouch){
20988 var ctr = this.el.select('.carousel-bullets',true).first();
20989 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20990 var bullet = ctr.createChild({
20991 cls : 'bullet bullet-' + i
20992 },ctr.dom.lastChild);
20997 bullet.on('click', (function(e, el, o, ii, t){
20999 e.preventDefault();
21001 this.showPanel(ii);
21003 if(this.autoslide && this.slideFn){
21004 clearInterval(this.slideFn);
21005 this.slideFn = window.setInterval(function() {
21006 _this.showPanelNext();
21010 }).createDelegate(this, [i, bullet], true));
21015 setActiveBullet : function(i)
21021 Roo.each(this.el.select('.bullet', true).elements, function(el){
21022 el.removeClass('selected');
21025 var bullet = this.el.select('.bullet-' + i, true).first();
21031 bullet.addClass('selected');
21042 Roo.apply(Roo.bootstrap.TabGroup, {
21046 * register a Navigation Group
21047 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21049 register : function(navgrp)
21051 this.groups[navgrp.navId] = navgrp;
21055 * fetch a Navigation Group based on the navigation ID
21056 * if one does not exist , it will get created.
21057 * @param {string} the navgroup to add
21058 * @returns {Roo.bootstrap.NavGroup} the navgroup
21060 get: function(navId) {
21061 if (typeof(this.groups[navId]) == 'undefined') {
21062 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21064 return this.groups[navId] ;
21079 * @class Roo.bootstrap.TabPanel
21080 * @extends Roo.bootstrap.Component
21081 * Bootstrap TabPanel class
21082 * @cfg {Boolean} active panel active
21083 * @cfg {String} html panel content
21084 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21085 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21086 * @cfg {String} href click to link..
21087 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21091 * Create a new TabPanel
21092 * @param {Object} config The config object
21095 Roo.bootstrap.TabPanel = function(config){
21096 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21100 * Fires when the active status changes
21101 * @param {Roo.bootstrap.TabPanel} this
21102 * @param {Boolean} state the new state
21107 * @event beforedeactivate
21108 * Fires before a tab is de-activated - can be used to do validation on a form.
21109 * @param {Roo.bootstrap.TabPanel} this
21110 * @return {Boolean} false if there is an error
21113 'beforedeactivate': true
21116 this.tabId = this.tabId || Roo.id();
21120 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21127 touchSlide : false,
21128 getAutoCreate : function(){
21133 // item is needed for carousel - not sure if it has any effect otherwise
21134 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21135 html: this.html || ''
21139 cfg.cls += ' active';
21143 cfg.tabId = this.tabId;
21151 initEvents: function()
21153 var p = this.parent();
21155 this.navId = this.navId || p.navId;
21157 if (typeof(this.navId) != 'undefined') {
21158 // not really needed.. but just in case.. parent should be a NavGroup.
21159 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21163 var i = tg.tabs.length - 1;
21165 if(this.active && tg.bullets > 0 && i < tg.bullets){
21166 tg.setActiveBullet(i);
21170 this.el.on('click', this.onClick, this);
21172 if(Roo.isTouch && this.touchSlide){
21173 this.el.on("touchstart", this.onTouchStart, this);
21174 this.el.on("touchmove", this.onTouchMove, this);
21175 this.el.on("touchend", this.onTouchEnd, this);
21180 onRender : function(ct, position)
21182 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21185 setActive : function(state)
21187 Roo.log("panel - set active " + this.tabId + "=" + state);
21189 this.active = state;
21191 this.el.removeClass('active');
21193 } else if (!this.el.hasClass('active')) {
21194 this.el.addClass('active');
21197 this.fireEvent('changed', this, state);
21200 onClick : function(e)
21202 e.preventDefault();
21204 if(!this.href.length){
21208 window.location.href = this.href;
21217 onTouchStart : function(e)
21219 this.swiping = false;
21221 this.startX = e.browserEvent.touches[0].clientX;
21222 this.startY = e.browserEvent.touches[0].clientY;
21225 onTouchMove : function(e)
21227 this.swiping = true;
21229 this.endX = e.browserEvent.touches[0].clientX;
21230 this.endY = e.browserEvent.touches[0].clientY;
21233 onTouchEnd : function(e)
21240 var tabGroup = this.parent();
21242 if(this.endX > this.startX){ // swiping right
21243 tabGroup.showPanelPrev();
21247 if(this.startX > this.endX){ // swiping left
21248 tabGroup.showPanelNext();
21267 * @class Roo.bootstrap.DateField
21268 * @extends Roo.bootstrap.Input
21269 * Bootstrap DateField class
21270 * @cfg {Number} weekStart default 0
21271 * @cfg {String} viewMode default empty, (months|years)
21272 * @cfg {String} minViewMode default empty, (months|years)
21273 * @cfg {Number} startDate default -Infinity
21274 * @cfg {Number} endDate default Infinity
21275 * @cfg {Boolean} todayHighlight default false
21276 * @cfg {Boolean} todayBtn default false
21277 * @cfg {Boolean} calendarWeeks default false
21278 * @cfg {Object} daysOfWeekDisabled default empty
21279 * @cfg {Boolean} singleMode default false (true | false)
21281 * @cfg {Boolean} keyboardNavigation default true
21282 * @cfg {String} language default en
21285 * Create a new DateField
21286 * @param {Object} config The config object
21289 Roo.bootstrap.DateField = function(config){
21290 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21294 * Fires when this field show.
21295 * @param {Roo.bootstrap.DateField} this
21296 * @param {Mixed} date The date value
21301 * Fires when this field hide.
21302 * @param {Roo.bootstrap.DateField} this
21303 * @param {Mixed} date The date value
21308 * Fires when select a date.
21309 * @param {Roo.bootstrap.DateField} this
21310 * @param {Mixed} date The date value
21314 * @event beforeselect
21315 * Fires when before select a date.
21316 * @param {Roo.bootstrap.DateField} this
21317 * @param {Mixed} date The date value
21319 beforeselect : true
21323 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21326 * @cfg {String} format
21327 * The default date format string which can be overriden for localization support. The format must be
21328 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21332 * @cfg {String} altFormats
21333 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21334 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21336 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21344 todayHighlight : false,
21350 keyboardNavigation: true,
21352 calendarWeeks: false,
21354 startDate: -Infinity,
21358 daysOfWeekDisabled: [],
21362 singleMode : false,
21364 UTCDate: function()
21366 return new Date(Date.UTC.apply(Date, arguments));
21369 UTCToday: function()
21371 var today = new Date();
21372 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21375 getDate: function() {
21376 var d = this.getUTCDate();
21377 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21380 getUTCDate: function() {
21384 setDate: function(d) {
21385 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21388 setUTCDate: function(d) {
21390 this.setValue(this.formatDate(this.date));
21393 onRender: function(ct, position)
21396 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21398 this.language = this.language || 'en';
21399 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21400 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21402 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21403 this.format = this.format || 'm/d/y';
21404 this.isInline = false;
21405 this.isInput = true;
21406 this.component = this.el.select('.add-on', true).first() || false;
21407 this.component = (this.component && this.component.length === 0) ? false : this.component;
21408 this.hasInput = this.component && this.inputEl().length;
21410 if (typeof(this.minViewMode === 'string')) {
21411 switch (this.minViewMode) {
21413 this.minViewMode = 1;
21416 this.minViewMode = 2;
21419 this.minViewMode = 0;
21424 if (typeof(this.viewMode === 'string')) {
21425 switch (this.viewMode) {
21438 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21440 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21442 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21444 this.picker().on('mousedown', this.onMousedown, this);
21445 this.picker().on('click', this.onClick, this);
21447 this.picker().addClass('datepicker-dropdown');
21449 this.startViewMode = this.viewMode;
21451 if(this.singleMode){
21452 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21453 v.setVisibilityMode(Roo.Element.DISPLAY);
21457 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21458 v.setStyle('width', '189px');
21462 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21463 if(!this.calendarWeeks){
21468 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21469 v.attr('colspan', function(i, val){
21470 return parseInt(val) + 1;
21475 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21477 this.setStartDate(this.startDate);
21478 this.setEndDate(this.endDate);
21480 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21487 if(this.isInline) {
21492 picker : function()
21494 return this.pickerEl;
21495 // return this.el.select('.datepicker', true).first();
21498 fillDow: function()
21500 var dowCnt = this.weekStart;
21509 if(this.calendarWeeks){
21517 while (dowCnt < this.weekStart + 7) {
21521 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21525 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21528 fillMonths: function()
21531 var months = this.picker().select('>.datepicker-months td', true).first();
21533 months.dom.innerHTML = '';
21539 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21542 months.createChild(month);
21549 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;
21551 if (this.date < this.startDate) {
21552 this.viewDate = new Date(this.startDate);
21553 } else if (this.date > this.endDate) {
21554 this.viewDate = new Date(this.endDate);
21556 this.viewDate = new Date(this.date);
21564 var d = new Date(this.viewDate),
21565 year = d.getUTCFullYear(),
21566 month = d.getUTCMonth(),
21567 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21568 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21569 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21570 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21571 currentDate = this.date && this.date.valueOf(),
21572 today = this.UTCToday();
21574 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21576 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21578 // this.picker.select('>tfoot th.today').
21579 // .text(dates[this.language].today)
21580 // .toggle(this.todayBtn !== false);
21582 this.updateNavArrows();
21585 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21587 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21589 prevMonth.setUTCDate(day);
21591 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21593 var nextMonth = new Date(prevMonth);
21595 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21597 nextMonth = nextMonth.valueOf();
21599 var fillMonths = false;
21601 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21603 while(prevMonth.valueOf() <= nextMonth) {
21606 if (prevMonth.getUTCDay() === this.weekStart) {
21608 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21616 if(this.calendarWeeks){
21617 // ISO 8601: First week contains first thursday.
21618 // ISO also states week starts on Monday, but we can be more abstract here.
21620 // Start of current week: based on weekstart/current date
21621 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21622 // Thursday of this week
21623 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21624 // First Thursday of year, year from thursday
21625 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21626 // Calendar week: ms between thursdays, div ms per day, div 7 days
21627 calWeek = (th - yth) / 864e5 / 7 + 1;
21629 fillMonths.cn.push({
21637 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21639 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21642 if (this.todayHighlight &&
21643 prevMonth.getUTCFullYear() == today.getFullYear() &&
21644 prevMonth.getUTCMonth() == today.getMonth() &&
21645 prevMonth.getUTCDate() == today.getDate()) {
21646 clsName += ' today';
21649 if (currentDate && prevMonth.valueOf() === currentDate) {
21650 clsName += ' active';
21653 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21654 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21655 clsName += ' disabled';
21658 fillMonths.cn.push({
21660 cls: 'day ' + clsName,
21661 html: prevMonth.getDate()
21664 prevMonth.setDate(prevMonth.getDate()+1);
21667 var currentYear = this.date && this.date.getUTCFullYear();
21668 var currentMonth = this.date && this.date.getUTCMonth();
21670 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21672 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21673 v.removeClass('active');
21675 if(currentYear === year && k === currentMonth){
21676 v.addClass('active');
21679 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21680 v.addClass('disabled');
21686 year = parseInt(year/10, 10) * 10;
21688 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21690 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21693 for (var i = -1; i < 11; i++) {
21694 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21696 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21704 showMode: function(dir)
21707 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21710 Roo.each(this.picker().select('>div',true).elements, function(v){
21711 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21714 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21719 if(this.isInline) {
21723 this.picker().removeClass(['bottom', 'top']);
21725 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21727 * place to the top of element!
21731 this.picker().addClass('top');
21732 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21737 this.picker().addClass('bottom');
21739 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21742 parseDate : function(value)
21744 if(!value || value instanceof Date){
21747 var v = Date.parseDate(value, this.format);
21748 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21749 v = Date.parseDate(value, 'Y-m-d');
21751 if(!v && this.altFormats){
21752 if(!this.altFormatsArray){
21753 this.altFormatsArray = this.altFormats.split("|");
21755 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21756 v = Date.parseDate(value, this.altFormatsArray[i]);
21762 formatDate : function(date, fmt)
21764 return (!date || !(date instanceof Date)) ?
21765 date : date.dateFormat(fmt || this.format);
21768 onFocus : function()
21770 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21774 onBlur : function()
21776 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21778 var d = this.inputEl().getValue();
21785 showPopup : function()
21787 this.picker().show();
21791 this.fireEvent('showpopup', this, this.date);
21794 hidePopup : function()
21796 if(this.isInline) {
21799 this.picker().hide();
21800 this.viewMode = this.startViewMode;
21803 this.fireEvent('hidepopup', this, this.date);
21807 onMousedown: function(e)
21809 e.stopPropagation();
21810 e.preventDefault();
21815 Roo.bootstrap.DateField.superclass.keyup.call(this);
21819 setValue: function(v)
21821 if(this.fireEvent('beforeselect', this, v) !== false){
21822 var d = new Date(this.parseDate(v) ).clearTime();
21824 if(isNaN(d.getTime())){
21825 this.date = this.viewDate = '';
21826 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21830 v = this.formatDate(d);
21832 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21834 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21838 this.fireEvent('select', this, this.date);
21842 getValue: function()
21844 return this.formatDate(this.date);
21847 fireKey: function(e)
21849 if (!this.picker().isVisible()){
21850 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21856 var dateChanged = false,
21858 newDate, newViewDate;
21863 e.preventDefault();
21867 if (!this.keyboardNavigation) {
21870 dir = e.keyCode == 37 ? -1 : 1;
21873 newDate = this.moveYear(this.date, dir);
21874 newViewDate = this.moveYear(this.viewDate, dir);
21875 } else if (e.shiftKey){
21876 newDate = this.moveMonth(this.date, dir);
21877 newViewDate = this.moveMonth(this.viewDate, dir);
21879 newDate = new Date(this.date);
21880 newDate.setUTCDate(this.date.getUTCDate() + dir);
21881 newViewDate = new Date(this.viewDate);
21882 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21884 if (this.dateWithinRange(newDate)){
21885 this.date = newDate;
21886 this.viewDate = newViewDate;
21887 this.setValue(this.formatDate(this.date));
21889 e.preventDefault();
21890 dateChanged = true;
21895 if (!this.keyboardNavigation) {
21898 dir = e.keyCode == 38 ? -1 : 1;
21900 newDate = this.moveYear(this.date, dir);
21901 newViewDate = this.moveYear(this.viewDate, dir);
21902 } else if (e.shiftKey){
21903 newDate = this.moveMonth(this.date, dir);
21904 newViewDate = this.moveMonth(this.viewDate, dir);
21906 newDate = new Date(this.date);
21907 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21908 newViewDate = new Date(this.viewDate);
21909 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21911 if (this.dateWithinRange(newDate)){
21912 this.date = newDate;
21913 this.viewDate = newViewDate;
21914 this.setValue(this.formatDate(this.date));
21916 e.preventDefault();
21917 dateChanged = true;
21921 this.setValue(this.formatDate(this.date));
21923 e.preventDefault();
21926 this.setValue(this.formatDate(this.date));
21940 onClick: function(e)
21942 e.stopPropagation();
21943 e.preventDefault();
21945 var target = e.getTarget();
21947 if(target.nodeName.toLowerCase() === 'i'){
21948 target = Roo.get(target).dom.parentNode;
21951 var nodeName = target.nodeName;
21952 var className = target.className;
21953 var html = target.innerHTML;
21954 //Roo.log(nodeName);
21956 switch(nodeName.toLowerCase()) {
21958 switch(className) {
21964 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21965 switch(this.viewMode){
21967 this.viewDate = this.moveMonth(this.viewDate, dir);
21971 this.viewDate = this.moveYear(this.viewDate, dir);
21977 var date = new Date();
21978 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21980 this.setValue(this.formatDate(this.date));
21987 if (className.indexOf('disabled') < 0) {
21988 if (!this.viewDate) {
21989 this.viewDate = new Date();
21991 this.viewDate.setUTCDate(1);
21992 if (className.indexOf('month') > -1) {
21993 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21995 var year = parseInt(html, 10) || 0;
21996 this.viewDate.setUTCFullYear(year);
22000 if(this.singleMode){
22001 this.setValue(this.formatDate(this.viewDate));
22012 //Roo.log(className);
22013 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22014 var day = parseInt(html, 10) || 1;
22015 var year = (this.viewDate || new Date()).getUTCFullYear(),
22016 month = (this.viewDate || new Date()).getUTCMonth();
22018 if (className.indexOf('old') > -1) {
22025 } else if (className.indexOf('new') > -1) {
22033 //Roo.log([year,month,day]);
22034 this.date = this.UTCDate(year, month, day,0,0,0,0);
22035 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22037 //Roo.log(this.formatDate(this.date));
22038 this.setValue(this.formatDate(this.date));
22045 setStartDate: function(startDate)
22047 this.startDate = startDate || -Infinity;
22048 if (this.startDate !== -Infinity) {
22049 this.startDate = this.parseDate(this.startDate);
22052 this.updateNavArrows();
22055 setEndDate: function(endDate)
22057 this.endDate = endDate || Infinity;
22058 if (this.endDate !== Infinity) {
22059 this.endDate = this.parseDate(this.endDate);
22062 this.updateNavArrows();
22065 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22067 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22068 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22069 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22071 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22072 return parseInt(d, 10);
22075 this.updateNavArrows();
22078 updateNavArrows: function()
22080 if(this.singleMode){
22084 var d = new Date(this.viewDate),
22085 year = d.getUTCFullYear(),
22086 month = d.getUTCMonth();
22088 Roo.each(this.picker().select('.prev', true).elements, function(v){
22090 switch (this.viewMode) {
22093 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22099 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22106 Roo.each(this.picker().select('.next', true).elements, function(v){
22108 switch (this.viewMode) {
22111 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22117 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22125 moveMonth: function(date, dir)
22130 var new_date = new Date(date.valueOf()),
22131 day = new_date.getUTCDate(),
22132 month = new_date.getUTCMonth(),
22133 mag = Math.abs(dir),
22135 dir = dir > 0 ? 1 : -1;
22138 // If going back one month, make sure month is not current month
22139 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22141 return new_date.getUTCMonth() == month;
22143 // If going forward one month, make sure month is as expected
22144 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22146 return new_date.getUTCMonth() != new_month;
22148 new_month = month + dir;
22149 new_date.setUTCMonth(new_month);
22150 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22151 if (new_month < 0 || new_month > 11) {
22152 new_month = (new_month + 12) % 12;
22155 // For magnitudes >1, move one month at a time...
22156 for (var i=0; i<mag; i++) {
22157 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22158 new_date = this.moveMonth(new_date, dir);
22160 // ...then reset the day, keeping it in the new month
22161 new_month = new_date.getUTCMonth();
22162 new_date.setUTCDate(day);
22164 return new_month != new_date.getUTCMonth();
22167 // Common date-resetting loop -- if date is beyond end of month, make it
22170 new_date.setUTCDate(--day);
22171 new_date.setUTCMonth(new_month);
22176 moveYear: function(date, dir)
22178 return this.moveMonth(date, dir*12);
22181 dateWithinRange: function(date)
22183 return date >= this.startDate && date <= this.endDate;
22189 this.picker().remove();
22192 validateValue : function(value)
22194 if(this.getVisibilityEl().hasClass('hidden')){
22198 if(value.length < 1) {
22199 if(this.allowBlank){
22205 if(value.length < this.minLength){
22208 if(value.length > this.maxLength){
22212 var vt = Roo.form.VTypes;
22213 if(!vt[this.vtype](value, this)){
22217 if(typeof this.validator == "function"){
22218 var msg = this.validator(value);
22224 if(this.regex && !this.regex.test(value)){
22228 if(typeof(this.parseDate(value)) == 'undefined'){
22232 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22236 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22246 this.date = this.viewDate = '';
22248 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22253 Roo.apply(Roo.bootstrap.DateField, {
22264 html: '<i class="fa fa-arrow-left"/>'
22274 html: '<i class="fa fa-arrow-right"/>'
22316 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22317 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22318 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22319 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22320 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22333 navFnc: 'FullYear',
22338 navFnc: 'FullYear',
22343 Roo.apply(Roo.bootstrap.DateField, {
22347 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22351 cls: 'datepicker-days',
22355 cls: 'table-condensed',
22357 Roo.bootstrap.DateField.head,
22361 Roo.bootstrap.DateField.footer
22368 cls: 'datepicker-months',
22372 cls: 'table-condensed',
22374 Roo.bootstrap.DateField.head,
22375 Roo.bootstrap.DateField.content,
22376 Roo.bootstrap.DateField.footer
22383 cls: 'datepicker-years',
22387 cls: 'table-condensed',
22389 Roo.bootstrap.DateField.head,
22390 Roo.bootstrap.DateField.content,
22391 Roo.bootstrap.DateField.footer
22410 * @class Roo.bootstrap.TimeField
22411 * @extends Roo.bootstrap.Input
22412 * Bootstrap DateField class
22416 * Create a new TimeField
22417 * @param {Object} config The config object
22420 Roo.bootstrap.TimeField = function(config){
22421 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22425 * Fires when this field show.
22426 * @param {Roo.bootstrap.DateField} thisthis
22427 * @param {Mixed} date The date value
22432 * Fires when this field hide.
22433 * @param {Roo.bootstrap.DateField} this
22434 * @param {Mixed} date The date value
22439 * Fires when select a date.
22440 * @param {Roo.bootstrap.DateField} this
22441 * @param {Mixed} date The date value
22447 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22450 * @cfg {String} format
22451 * The default time format string which can be overriden for localization support. The format must be
22452 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22456 getAutoCreate : function()
22458 this.after = '<i class="fa far fa-clock"></i>';
22459 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22463 onRender: function(ct, position)
22466 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22468 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22470 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22472 this.pop = this.picker().select('>.datepicker-time',true).first();
22473 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22475 this.picker().on('mousedown', this.onMousedown, this);
22476 this.picker().on('click', this.onClick, this);
22478 this.picker().addClass('datepicker-dropdown');
22483 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22484 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22485 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22486 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22487 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22488 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22492 fireKey: function(e){
22493 if (!this.picker().isVisible()){
22494 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22500 e.preventDefault();
22508 this.onTogglePeriod();
22511 this.onIncrementMinutes();
22514 this.onDecrementMinutes();
22523 onClick: function(e) {
22524 e.stopPropagation();
22525 e.preventDefault();
22528 picker : function()
22530 return this.pickerEl;
22533 fillTime: function()
22535 var time = this.pop.select('tbody', true).first();
22537 time.dom.innerHTML = '';
22552 cls: 'hours-up fa fas fa-chevron-up'
22572 cls: 'minutes-up fa fas fa-chevron-up'
22593 cls: 'timepicker-hour',
22608 cls: 'timepicker-minute',
22623 cls: 'btn btn-primary period',
22645 cls: 'hours-down fa fas fa-chevron-down'
22665 cls: 'minutes-down fa fas fa-chevron-down'
22683 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22690 var hours = this.time.getHours();
22691 var minutes = this.time.getMinutes();
22704 hours = hours - 12;
22708 hours = '0' + hours;
22712 minutes = '0' + minutes;
22715 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22716 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22717 this.pop.select('button', true).first().dom.innerHTML = period;
22723 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22725 var cls = ['bottom'];
22727 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22734 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22738 //this.picker().setXY(20000,20000);
22739 this.picker().addClass(cls.join('-'));
22743 Roo.each(cls, function(c){
22748 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22749 //_this.picker().setTop(_this.inputEl().getHeight());
22753 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22755 //_this.picker().setTop(0 - _this.picker().getHeight());
22760 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22764 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22772 onFocus : function()
22774 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22778 onBlur : function()
22780 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22786 this.picker().show();
22791 this.fireEvent('show', this, this.date);
22796 this.picker().hide();
22799 this.fireEvent('hide', this, this.date);
22802 setTime : function()
22805 this.setValue(this.time.format(this.format));
22807 this.fireEvent('select', this, this.date);
22812 onMousedown: function(e){
22813 e.stopPropagation();
22814 e.preventDefault();
22817 onIncrementHours: function()
22819 Roo.log('onIncrementHours');
22820 this.time = this.time.add(Date.HOUR, 1);
22825 onDecrementHours: function()
22827 Roo.log('onDecrementHours');
22828 this.time = this.time.add(Date.HOUR, -1);
22832 onIncrementMinutes: function()
22834 Roo.log('onIncrementMinutes');
22835 this.time = this.time.add(Date.MINUTE, 1);
22839 onDecrementMinutes: function()
22841 Roo.log('onDecrementMinutes');
22842 this.time = this.time.add(Date.MINUTE, -1);
22846 onTogglePeriod: function()
22848 Roo.log('onTogglePeriod');
22849 this.time = this.time.add(Date.HOUR, 12);
22857 Roo.apply(Roo.bootstrap.TimeField, {
22861 cls: 'datepicker dropdown-menu',
22865 cls: 'datepicker-time',
22869 cls: 'table-condensed',
22898 cls: 'btn btn-info ok',
22926 * @class Roo.bootstrap.MonthField
22927 * @extends Roo.bootstrap.Input
22928 * Bootstrap MonthField class
22930 * @cfg {String} language default en
22933 * Create a new MonthField
22934 * @param {Object} config The config object
22937 Roo.bootstrap.MonthField = function(config){
22938 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22943 * Fires when this field show.
22944 * @param {Roo.bootstrap.MonthField} this
22945 * @param {Mixed} date The date value
22950 * Fires when this field hide.
22951 * @param {Roo.bootstrap.MonthField} this
22952 * @param {Mixed} date The date value
22957 * Fires when select a date.
22958 * @param {Roo.bootstrap.MonthField} this
22959 * @param {String} oldvalue The old value
22960 * @param {String} newvalue The new value
22966 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22968 onRender: function(ct, position)
22971 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22973 this.language = this.language || 'en';
22974 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22975 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22977 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22978 this.isInline = false;
22979 this.isInput = true;
22980 this.component = this.el.select('.add-on', true).first() || false;
22981 this.component = (this.component && this.component.length === 0) ? false : this.component;
22982 this.hasInput = this.component && this.inputEL().length;
22984 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22986 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22988 this.picker().on('mousedown', this.onMousedown, this);
22989 this.picker().on('click', this.onClick, this);
22991 this.picker().addClass('datepicker-dropdown');
22993 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22994 v.setStyle('width', '189px');
23001 if(this.isInline) {
23007 setValue: function(v, suppressEvent)
23009 var o = this.getValue();
23011 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23015 if(suppressEvent !== true){
23016 this.fireEvent('select', this, o, v);
23021 getValue: function()
23026 onClick: function(e)
23028 e.stopPropagation();
23029 e.preventDefault();
23031 var target = e.getTarget();
23033 if(target.nodeName.toLowerCase() === 'i'){
23034 target = Roo.get(target).dom.parentNode;
23037 var nodeName = target.nodeName;
23038 var className = target.className;
23039 var html = target.innerHTML;
23041 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23045 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23047 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23053 picker : function()
23055 return this.pickerEl;
23058 fillMonths: function()
23061 var months = this.picker().select('>.datepicker-months td', true).first();
23063 months.dom.innerHTML = '';
23069 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23072 months.createChild(month);
23081 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23082 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23085 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23086 e.removeClass('active');
23088 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23089 e.addClass('active');
23096 if(this.isInline) {
23100 this.picker().removeClass(['bottom', 'top']);
23102 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23104 * place to the top of element!
23108 this.picker().addClass('top');
23109 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23114 this.picker().addClass('bottom');
23116 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23119 onFocus : function()
23121 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23125 onBlur : function()
23127 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23129 var d = this.inputEl().getValue();
23138 this.picker().show();
23139 this.picker().select('>.datepicker-months', true).first().show();
23143 this.fireEvent('show', this, this.date);
23148 if(this.isInline) {
23151 this.picker().hide();
23152 this.fireEvent('hide', this, this.date);
23156 onMousedown: function(e)
23158 e.stopPropagation();
23159 e.preventDefault();
23164 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23168 fireKey: function(e)
23170 if (!this.picker().isVisible()){
23171 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23182 e.preventDefault();
23186 dir = e.keyCode == 37 ? -1 : 1;
23188 this.vIndex = this.vIndex + dir;
23190 if(this.vIndex < 0){
23194 if(this.vIndex > 11){
23198 if(isNaN(this.vIndex)){
23202 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23208 dir = e.keyCode == 38 ? -1 : 1;
23210 this.vIndex = this.vIndex + dir * 4;
23212 if(this.vIndex < 0){
23216 if(this.vIndex > 11){
23220 if(isNaN(this.vIndex)){
23224 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23229 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23230 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23234 e.preventDefault();
23237 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23238 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23254 this.picker().remove();
23259 Roo.apply(Roo.bootstrap.MonthField, {
23278 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23279 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23284 Roo.apply(Roo.bootstrap.MonthField, {
23288 cls: 'datepicker dropdown-menu roo-dynamic',
23292 cls: 'datepicker-months',
23296 cls: 'table-condensed',
23298 Roo.bootstrap.DateField.content
23318 * @class Roo.bootstrap.CheckBox
23319 * @extends Roo.bootstrap.Input
23320 * Bootstrap CheckBox class
23322 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23323 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23324 * @cfg {String} boxLabel The text that appears beside the checkbox
23325 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23326 * @cfg {Boolean} checked initnal the element
23327 * @cfg {Boolean} inline inline the element (default false)
23328 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23329 * @cfg {String} tooltip label tooltip
23332 * Create a new CheckBox
23333 * @param {Object} config The config object
23336 Roo.bootstrap.CheckBox = function(config){
23337 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23342 * Fires when the element is checked or unchecked.
23343 * @param {Roo.bootstrap.CheckBox} this This input
23344 * @param {Boolean} checked The new checked value
23349 * Fires when the element is click.
23350 * @param {Roo.bootstrap.CheckBox} this This input
23357 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23359 inputType: 'checkbox',
23368 // checkbox success does not make any sense really..
23373 getAutoCreate : function()
23375 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23381 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23384 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23390 type : this.inputType,
23391 value : this.inputValue,
23392 cls : 'roo-' + this.inputType, //'form-box',
23393 placeholder : this.placeholder || ''
23397 if(this.inputType != 'radio'){
23401 cls : 'roo-hidden-value',
23402 value : this.checked ? this.inputValue : this.valueOff
23407 if (this.weight) { // Validity check?
23408 cfg.cls += " " + this.inputType + "-" + this.weight;
23411 if (this.disabled) {
23412 input.disabled=true;
23416 input.checked = this.checked;
23421 input.name = this.name;
23423 if(this.inputType != 'radio'){
23424 hidden.name = this.name;
23425 input.name = '_hidden_' + this.name;
23430 input.cls += ' input-' + this.size;
23435 ['xs','sm','md','lg'].map(function(size){
23436 if (settings[size]) {
23437 cfg.cls += ' col-' + size + '-' + settings[size];
23441 var inputblock = input;
23443 if (this.before || this.after) {
23446 cls : 'input-group',
23451 inputblock.cn.push({
23453 cls : 'input-group-addon',
23458 inputblock.cn.push(input);
23460 if(this.inputType != 'radio'){
23461 inputblock.cn.push(hidden);
23465 inputblock.cn.push({
23467 cls : 'input-group-addon',
23473 var boxLabelCfg = false;
23479 //'for': id, // box label is handled by onclick - so no for...
23481 html: this.boxLabel
23484 boxLabelCfg.tooltip = this.tooltip;
23490 if (align ==='left' && this.fieldLabel.length) {
23491 // Roo.log("left and has label");
23496 cls : 'control-label',
23497 html : this.fieldLabel
23508 cfg.cn[1].cn.push(boxLabelCfg);
23511 if(this.labelWidth > 12){
23512 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23515 if(this.labelWidth < 13 && this.labelmd == 0){
23516 this.labelmd = this.labelWidth;
23519 if(this.labellg > 0){
23520 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23521 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23524 if(this.labelmd > 0){
23525 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23526 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23529 if(this.labelsm > 0){
23530 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23531 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23534 if(this.labelxs > 0){
23535 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23536 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23539 } else if ( this.fieldLabel.length) {
23540 // Roo.log(" label");
23544 tag: this.boxLabel ? 'span' : 'label',
23546 cls: 'control-label box-input-label',
23547 //cls : 'input-group-addon',
23548 html : this.fieldLabel
23555 cfg.cn.push(boxLabelCfg);
23560 // Roo.log(" no label && no align");
23561 cfg.cn = [ inputblock ] ;
23563 cfg.cn.push(boxLabelCfg);
23571 if(this.inputType != 'radio'){
23572 cfg.cn.push(hidden);
23580 * return the real input element.
23582 inputEl: function ()
23584 return this.el.select('input.roo-' + this.inputType,true).first();
23586 hiddenEl: function ()
23588 return this.el.select('input.roo-hidden-value',true).first();
23591 labelEl: function()
23593 return this.el.select('label.control-label',true).first();
23595 /* depricated... */
23599 return this.labelEl();
23602 boxLabelEl: function()
23604 return this.el.select('label.box-label',true).first();
23607 initEvents : function()
23609 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23611 this.inputEl().on('click', this.onClick, this);
23613 if (this.boxLabel) {
23614 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23617 this.startValue = this.getValue();
23620 Roo.bootstrap.CheckBox.register(this);
23624 onClick : function(e)
23626 if(this.fireEvent('click', this, e) !== false){
23627 this.setChecked(!this.checked);
23632 setChecked : function(state,suppressEvent)
23634 this.startValue = this.getValue();
23636 if(this.inputType == 'radio'){
23638 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23639 e.dom.checked = false;
23642 this.inputEl().dom.checked = true;
23644 this.inputEl().dom.value = this.inputValue;
23646 if(suppressEvent !== true){
23647 this.fireEvent('check', this, true);
23655 this.checked = state;
23657 this.inputEl().dom.checked = state;
23660 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23662 if(suppressEvent !== true){
23663 this.fireEvent('check', this, state);
23669 getValue : function()
23671 if(this.inputType == 'radio'){
23672 return this.getGroupValue();
23675 return this.hiddenEl().dom.value;
23679 getGroupValue : function()
23681 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23685 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23688 setValue : function(v,suppressEvent)
23690 if(this.inputType == 'radio'){
23691 this.setGroupValue(v, suppressEvent);
23695 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23700 setGroupValue : function(v, suppressEvent)
23702 this.startValue = this.getValue();
23704 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23705 e.dom.checked = false;
23707 if(e.dom.value == v){
23708 e.dom.checked = true;
23712 if(suppressEvent !== true){
23713 this.fireEvent('check', this, true);
23721 validate : function()
23723 if(this.getVisibilityEl().hasClass('hidden')){
23729 (this.inputType == 'radio' && this.validateRadio()) ||
23730 (this.inputType == 'checkbox' && this.validateCheckbox())
23736 this.markInvalid();
23740 validateRadio : function()
23742 if(this.getVisibilityEl().hasClass('hidden')){
23746 if(this.allowBlank){
23752 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23753 if(!e.dom.checked){
23765 validateCheckbox : function()
23768 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23769 //return (this.getValue() == this.inputValue) ? true : false;
23772 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23780 for(var i in group){
23781 if(group[i].el.isVisible(true)){
23789 for(var i in group){
23794 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23801 * Mark this field as valid
23803 markValid : function()
23807 this.fireEvent('valid', this);
23809 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23812 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23819 if(this.inputType == 'radio'){
23820 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23821 var fg = e.findParent('.form-group', false, true);
23822 if (Roo.bootstrap.version == 3) {
23823 fg.removeClass([_this.invalidClass, _this.validClass]);
23824 fg.addClass(_this.validClass);
23826 fg.removeClass(['is-valid', 'is-invalid']);
23827 fg.addClass('is-valid');
23835 var fg = this.el.findParent('.form-group', false, true);
23836 if (Roo.bootstrap.version == 3) {
23837 fg.removeClass([this.invalidClass, this.validClass]);
23838 fg.addClass(this.validClass);
23840 fg.removeClass(['is-valid', 'is-invalid']);
23841 fg.addClass('is-valid');
23846 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23852 for(var i in group){
23853 var fg = group[i].el.findParent('.form-group', false, true);
23854 if (Roo.bootstrap.version == 3) {
23855 fg.removeClass([this.invalidClass, this.validClass]);
23856 fg.addClass(this.validClass);
23858 fg.removeClass(['is-valid', 'is-invalid']);
23859 fg.addClass('is-valid');
23865 * Mark this field as invalid
23866 * @param {String} msg The validation message
23868 markInvalid : function(msg)
23870 if(this.allowBlank){
23876 this.fireEvent('invalid', this, msg);
23878 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23881 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23885 label.markInvalid();
23888 if(this.inputType == 'radio'){
23890 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23891 var fg = e.findParent('.form-group', false, true);
23892 if (Roo.bootstrap.version == 3) {
23893 fg.removeClass([_this.invalidClass, _this.validClass]);
23894 fg.addClass(_this.invalidClass);
23896 fg.removeClass(['is-invalid', 'is-valid']);
23897 fg.addClass('is-invalid');
23905 var fg = this.el.findParent('.form-group', false, true);
23906 if (Roo.bootstrap.version == 3) {
23907 fg.removeClass([_this.invalidClass, _this.validClass]);
23908 fg.addClass(_this.invalidClass);
23910 fg.removeClass(['is-invalid', 'is-valid']);
23911 fg.addClass('is-invalid');
23916 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23922 for(var i in group){
23923 var fg = group[i].el.findParent('.form-group', false, true);
23924 if (Roo.bootstrap.version == 3) {
23925 fg.removeClass([_this.invalidClass, _this.validClass]);
23926 fg.addClass(_this.invalidClass);
23928 fg.removeClass(['is-invalid', 'is-valid']);
23929 fg.addClass('is-invalid');
23935 clearInvalid : function()
23937 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23939 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23941 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23943 if (label && label.iconEl) {
23944 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23945 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23949 disable : function()
23951 if(this.inputType != 'radio'){
23952 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23959 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23960 _this.getActionEl().addClass(this.disabledClass);
23961 e.dom.disabled = true;
23965 this.disabled = true;
23966 this.fireEvent("disable", this);
23970 enable : function()
23972 if(this.inputType != 'radio'){
23973 Roo.bootstrap.CheckBox.superclass.enable.call(this);
23980 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23981 _this.getActionEl().removeClass(this.disabledClass);
23982 e.dom.disabled = false;
23986 this.disabled = false;
23987 this.fireEvent("enable", this);
23991 setBoxLabel : function(v)
23996 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24002 Roo.apply(Roo.bootstrap.CheckBox, {
24007 * register a CheckBox Group
24008 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24010 register : function(checkbox)
24012 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24013 this.groups[checkbox.groupId] = {};
24016 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24020 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24024 * fetch a CheckBox Group based on the group ID
24025 * @param {string} the group ID
24026 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24028 get: function(groupId) {
24029 if (typeof(this.groups[groupId]) == 'undefined') {
24033 return this.groups[groupId] ;
24046 * @class Roo.bootstrap.Radio
24047 * @extends Roo.bootstrap.Component
24048 * Bootstrap Radio class
24049 * @cfg {String} boxLabel - the label associated
24050 * @cfg {String} value - the value of radio
24053 * Create a new Radio
24054 * @param {Object} config The config object
24056 Roo.bootstrap.Radio = function(config){
24057 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24061 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24067 getAutoCreate : function()
24071 cls : 'form-group radio',
24076 html : this.boxLabel
24084 initEvents : function()
24086 this.parent().register(this);
24088 this.el.on('click', this.onClick, this);
24092 onClick : function(e)
24094 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24095 this.setChecked(true);
24099 setChecked : function(state, suppressEvent)
24101 this.parent().setValue(this.value, suppressEvent);
24105 setBoxLabel : function(v)
24110 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24125 * @class Roo.bootstrap.SecurePass
24126 * @extends Roo.bootstrap.Input
24127 * Bootstrap SecurePass class
24131 * Create a new SecurePass
24132 * @param {Object} config The config object
24135 Roo.bootstrap.SecurePass = function (config) {
24136 // these go here, so the translation tool can replace them..
24138 PwdEmpty: "Please type a password, and then retype it to confirm.",
24139 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24140 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24141 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24142 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24143 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24144 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24145 TooWeak: "Your password is Too Weak."
24147 this.meterLabel = "Password strength:";
24148 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24149 this.meterClass = [
24150 "roo-password-meter-tooweak",
24151 "roo-password-meter-weak",
24152 "roo-password-meter-medium",
24153 "roo-password-meter-strong",
24154 "roo-password-meter-grey"
24159 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24162 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24164 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24166 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24167 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24168 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24169 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24170 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24171 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24172 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24182 * @cfg {String/Object} Label for the strength meter (defaults to
24183 * 'Password strength:')
24188 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24189 * ['Weak', 'Medium', 'Strong'])
24192 pwdStrengths: false,
24205 initEvents: function ()
24207 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24209 if (this.el.is('input[type=password]') && Roo.isSafari) {
24210 this.el.on('keydown', this.SafariOnKeyDown, this);
24213 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24216 onRender: function (ct, position)
24218 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24219 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24220 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24222 this.trigger.createChild({
24227 cls: 'roo-password-meter-grey col-xs-12',
24230 //width: this.meterWidth + 'px'
24234 cls: 'roo-password-meter-text'
24240 if (this.hideTrigger) {
24241 this.trigger.setDisplayed(false);
24243 this.setSize(this.width || '', this.height || '');
24246 onDestroy: function ()
24248 if (this.trigger) {
24249 this.trigger.removeAllListeners();
24250 this.trigger.remove();
24253 this.wrap.remove();
24255 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24258 checkStrength: function ()
24260 var pwd = this.inputEl().getValue();
24261 if (pwd == this._lastPwd) {
24266 if (this.ClientSideStrongPassword(pwd)) {
24268 } else if (this.ClientSideMediumPassword(pwd)) {
24270 } else if (this.ClientSideWeakPassword(pwd)) {
24276 Roo.log('strength1: ' + strength);
24278 //var pm = this.trigger.child('div/div/div').dom;
24279 var pm = this.trigger.child('div/div');
24280 pm.removeClass(this.meterClass);
24281 pm.addClass(this.meterClass[strength]);
24284 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24286 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24288 this._lastPwd = pwd;
24292 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24294 this._lastPwd = '';
24296 var pm = this.trigger.child('div/div');
24297 pm.removeClass(this.meterClass);
24298 pm.addClass('roo-password-meter-grey');
24301 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24304 this.inputEl().dom.type='password';
24307 validateValue: function (value)
24309 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24312 if (value.length == 0) {
24313 if (this.allowBlank) {
24314 this.clearInvalid();
24318 this.markInvalid(this.errors.PwdEmpty);
24319 this.errorMsg = this.errors.PwdEmpty;
24327 if (!value.match(/[\x21-\x7e]+/)) {
24328 this.markInvalid(this.errors.PwdBadChar);
24329 this.errorMsg = this.errors.PwdBadChar;
24332 if (value.length < 6) {
24333 this.markInvalid(this.errors.PwdShort);
24334 this.errorMsg = this.errors.PwdShort;
24337 if (value.length > 16) {
24338 this.markInvalid(this.errors.PwdLong);
24339 this.errorMsg = this.errors.PwdLong;
24343 if (this.ClientSideStrongPassword(value)) {
24345 } else if (this.ClientSideMediumPassword(value)) {
24347 } else if (this.ClientSideWeakPassword(value)) {
24354 if (strength < 2) {
24355 //this.markInvalid(this.errors.TooWeak);
24356 this.errorMsg = this.errors.TooWeak;
24361 console.log('strength2: ' + strength);
24363 //var pm = this.trigger.child('div/div/div').dom;
24365 var pm = this.trigger.child('div/div');
24366 pm.removeClass(this.meterClass);
24367 pm.addClass(this.meterClass[strength]);
24369 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24371 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24373 this.errorMsg = '';
24377 CharacterSetChecks: function (type)
24380 this.fResult = false;
24383 isctype: function (character, type)
24386 case this.kCapitalLetter:
24387 if (character >= 'A' && character <= 'Z') {
24392 case this.kSmallLetter:
24393 if (character >= 'a' && character <= 'z') {
24399 if (character >= '0' && character <= '9') {
24404 case this.kPunctuation:
24405 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24416 IsLongEnough: function (pwd, size)
24418 return !(pwd == null || isNaN(size) || pwd.length < size);
24421 SpansEnoughCharacterSets: function (word, nb)
24423 if (!this.IsLongEnough(word, nb))
24428 var characterSetChecks = new Array(
24429 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24430 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24433 for (var index = 0; index < word.length; ++index) {
24434 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24435 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24436 characterSetChecks[nCharSet].fResult = true;
24443 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24444 if (characterSetChecks[nCharSet].fResult) {
24449 if (nCharSets < nb) {
24455 ClientSideStrongPassword: function (pwd)
24457 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24460 ClientSideMediumPassword: function (pwd)
24462 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24465 ClientSideWeakPassword: function (pwd)
24467 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24470 })//<script type="text/javascript">
24473 * Based Ext JS Library 1.1.1
24474 * Copyright(c) 2006-2007, Ext JS, LLC.
24480 * @class Roo.HtmlEditorCore
24481 * @extends Roo.Component
24482 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24484 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24487 Roo.HtmlEditorCore = function(config){
24490 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24495 * @event initialize
24496 * Fires when the editor is fully initialized (including the iframe)
24497 * @param {Roo.HtmlEditorCore} this
24502 * Fires when the editor is first receives the focus. Any insertion must wait
24503 * until after this event.
24504 * @param {Roo.HtmlEditorCore} this
24508 * @event beforesync
24509 * Fires before the textarea is updated with content from the editor iframe. Return false
24510 * to cancel the sync.
24511 * @param {Roo.HtmlEditorCore} this
24512 * @param {String} html
24516 * @event beforepush
24517 * Fires before the iframe editor is updated with content from the textarea. Return false
24518 * to cancel the push.
24519 * @param {Roo.HtmlEditorCore} this
24520 * @param {String} html
24525 * Fires when the textarea is updated with content from the editor iframe.
24526 * @param {Roo.HtmlEditorCore} this
24527 * @param {String} html
24532 * Fires when the iframe editor is updated with content from the textarea.
24533 * @param {Roo.HtmlEditorCore} this
24534 * @param {String} html
24539 * @event editorevent
24540 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24541 * @param {Roo.HtmlEditorCore} this
24547 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24549 // defaults : white / black...
24550 this.applyBlacklists();
24557 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24561 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24567 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24572 * @cfg {Number} height (in pixels)
24576 * @cfg {Number} width (in pixels)
24581 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24584 stylesheets: false,
24589 // private properties
24590 validationEvent : false,
24592 initialized : false,
24594 sourceEditMode : false,
24595 onFocus : Roo.emptyFn,
24597 hideMode:'offsets',
24601 // blacklist + whitelisted elements..
24608 * Protected method that will not generally be called directly. It
24609 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24610 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24612 getDocMarkup : function(){
24616 // inherit styels from page...??
24617 if (this.stylesheets === false) {
24619 Roo.get(document.head).select('style').each(function(node) {
24620 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24623 Roo.get(document.head).select('link').each(function(node) {
24624 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24627 } else if (!this.stylesheets.length) {
24629 st = '<style type="text/css">' +
24630 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24633 for (var i in this.stylesheets) {
24634 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24639 st += '<style type="text/css">' +
24640 'IMG { cursor: pointer } ' +
24643 var cls = 'roo-htmleditor-body';
24645 if(this.bodyCls.length){
24646 cls += ' ' + this.bodyCls;
24649 return '<html><head>' + st +
24650 //<style type="text/css">' +
24651 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24653 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24657 onRender : function(ct, position)
24660 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24661 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24664 this.el.dom.style.border = '0 none';
24665 this.el.dom.setAttribute('tabIndex', -1);
24666 this.el.addClass('x-hidden hide');
24670 if(Roo.isIE){ // fix IE 1px bogus margin
24671 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24675 this.frameId = Roo.id();
24679 var iframe = this.owner.wrap.createChild({
24681 cls: 'form-control', // bootstrap..
24683 name: this.frameId,
24684 frameBorder : 'no',
24685 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24690 this.iframe = iframe.dom;
24692 this.assignDocWin();
24694 this.doc.designMode = 'on';
24697 this.doc.write(this.getDocMarkup());
24701 var task = { // must defer to wait for browser to be ready
24703 //console.log("run task?" + this.doc.readyState);
24704 this.assignDocWin();
24705 if(this.doc.body || this.doc.readyState == 'complete'){
24707 this.doc.designMode="on";
24711 Roo.TaskMgr.stop(task);
24712 this.initEditor.defer(10, this);
24719 Roo.TaskMgr.start(task);
24724 onResize : function(w, h)
24726 Roo.log('resize: ' +w + ',' + h );
24727 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24731 if(typeof w == 'number'){
24733 this.iframe.style.width = w + 'px';
24735 if(typeof h == 'number'){
24737 this.iframe.style.height = h + 'px';
24739 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24746 * Toggles the editor between standard and source edit mode.
24747 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24749 toggleSourceEdit : function(sourceEditMode){
24751 this.sourceEditMode = sourceEditMode === true;
24753 if(this.sourceEditMode){
24755 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24758 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24759 //this.iframe.className = '';
24762 //this.setSize(this.owner.wrap.getSize());
24763 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24770 * Protected method that will not generally be called directly. If you need/want
24771 * custom HTML cleanup, this is the method you should override.
24772 * @param {String} html The HTML to be cleaned
24773 * return {String} The cleaned HTML
24775 cleanHtml : function(html){
24776 html = String(html);
24777 if(html.length > 5){
24778 if(Roo.isSafari){ // strip safari nonsense
24779 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24782 if(html == ' '){
24789 * HTML Editor -> Textarea
24790 * Protected method that will not generally be called directly. Syncs the contents
24791 * of the editor iframe with the textarea.
24793 syncValue : function(){
24794 if(this.initialized){
24795 var bd = (this.doc.body || this.doc.documentElement);
24796 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24797 var html = bd.innerHTML;
24799 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24800 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24802 html = '<div style="'+m[0]+'">' + html + '</div>';
24805 html = this.cleanHtml(html);
24806 // fix up the special chars.. normaly like back quotes in word...
24807 // however we do not want to do this with chinese..
24808 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24810 var cc = match.charCodeAt();
24812 // Get the character value, handling surrogate pairs
24813 if (match.length == 2) {
24814 // It's a surrogate pair, calculate the Unicode code point
24815 var high = match.charCodeAt(0) - 0xD800;
24816 var low = match.charCodeAt(1) - 0xDC00;
24817 cc = (high * 0x400) + low + 0x10000;
24819 (cc >= 0x4E00 && cc < 0xA000 ) ||
24820 (cc >= 0x3400 && cc < 0x4E00 ) ||
24821 (cc >= 0xf900 && cc < 0xfb00 )
24826 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24827 return "&#" + cc + ";";
24834 if(this.owner.fireEvent('beforesync', this, html) !== false){
24835 this.el.dom.value = html;
24836 this.owner.fireEvent('sync', this, html);
24842 * Protected method that will not generally be called directly. Pushes the value of the textarea
24843 * into the iframe editor.
24845 pushValue : function(){
24846 if(this.initialized){
24847 var v = this.el.dom.value.trim();
24849 // if(v.length < 1){
24853 if(this.owner.fireEvent('beforepush', this, v) !== false){
24854 var d = (this.doc.body || this.doc.documentElement);
24856 this.cleanUpPaste();
24857 this.el.dom.value = d.innerHTML;
24858 this.owner.fireEvent('push', this, v);
24864 deferFocus : function(){
24865 this.focus.defer(10, this);
24869 focus : function(){
24870 if(this.win && !this.sourceEditMode){
24877 assignDocWin: function()
24879 var iframe = this.iframe;
24882 this.doc = iframe.contentWindow.document;
24883 this.win = iframe.contentWindow;
24885 // if (!Roo.get(this.frameId)) {
24888 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24889 // this.win = Roo.get(this.frameId).dom.contentWindow;
24891 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24895 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24896 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24901 initEditor : function(){
24902 //console.log("INIT EDITOR");
24903 this.assignDocWin();
24907 this.doc.designMode="on";
24909 this.doc.write(this.getDocMarkup());
24912 var dbody = (this.doc.body || this.doc.documentElement);
24913 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24914 // this copies styles from the containing element into thsi one..
24915 // not sure why we need all of this..
24916 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24918 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24919 //ss['background-attachment'] = 'fixed'; // w3c
24920 dbody.bgProperties = 'fixed'; // ie
24921 //Roo.DomHelper.applyStyles(dbody, ss);
24922 Roo.EventManager.on(this.doc, {
24923 //'mousedown': this.onEditorEvent,
24924 'mouseup': this.onEditorEvent,
24925 'dblclick': this.onEditorEvent,
24926 'click': this.onEditorEvent,
24927 'keyup': this.onEditorEvent,
24932 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24934 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24935 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24937 this.initialized = true;
24939 this.owner.fireEvent('initialize', this);
24944 onDestroy : function(){
24950 //for (var i =0; i < this.toolbars.length;i++) {
24951 // // fixme - ask toolbars for heights?
24952 // this.toolbars[i].onDestroy();
24955 //this.wrap.dom.innerHTML = '';
24956 //this.wrap.remove();
24961 onFirstFocus : function(){
24963 this.assignDocWin();
24966 this.activated = true;
24969 if(Roo.isGecko){ // prevent silly gecko errors
24971 var s = this.win.getSelection();
24972 if(!s.focusNode || s.focusNode.nodeType != 3){
24973 var r = s.getRangeAt(0);
24974 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24979 this.execCmd('useCSS', true);
24980 this.execCmd('styleWithCSS', false);
24983 this.owner.fireEvent('activate', this);
24987 adjustFont: function(btn){
24988 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24989 //if(Roo.isSafari){ // safari
24992 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24993 if(Roo.isSafari){ // safari
24994 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24995 v = (v < 10) ? 10 : v;
24996 v = (v > 48) ? 48 : v;
24997 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25002 v = Math.max(1, v+adjust);
25004 this.execCmd('FontSize', v );
25007 onEditorEvent : function(e)
25009 this.owner.fireEvent('editorevent', this, e);
25010 // this.updateToolbar();
25011 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25014 insertTag : function(tg)
25016 // could be a bit smarter... -> wrap the current selected tRoo..
25017 if (tg.toLowerCase() == 'span' ||
25018 tg.toLowerCase() == 'code' ||
25019 tg.toLowerCase() == 'sup' ||
25020 tg.toLowerCase() == 'sub'
25023 range = this.createRange(this.getSelection());
25024 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25025 wrappingNode.appendChild(range.extractContents());
25026 range.insertNode(wrappingNode);
25033 this.execCmd("formatblock", tg);
25037 insertText : function(txt)
25041 var range = this.createRange();
25042 range.deleteContents();
25043 //alert(Sender.getAttribute('label'));
25045 range.insertNode(this.doc.createTextNode(txt));
25051 * Executes a Midas editor command on the editor document and performs necessary focus and
25052 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25053 * @param {String} cmd The Midas command
25054 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25056 relayCmd : function(cmd, value){
25058 this.execCmd(cmd, value);
25059 this.owner.fireEvent('editorevent', this);
25060 //this.updateToolbar();
25061 this.owner.deferFocus();
25065 * Executes a Midas editor command directly on the editor document.
25066 * For visual commands, you should use {@link #relayCmd} instead.
25067 * <b>This should only be called after the editor is initialized.</b>
25068 * @param {String} cmd The Midas command
25069 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25071 execCmd : function(cmd, value){
25072 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25079 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25081 * @param {String} text | dom node..
25083 insertAtCursor : function(text)
25086 if(!this.activated){
25092 var r = this.doc.selection.createRange();
25103 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25107 // from jquery ui (MIT licenced)
25109 var win = this.win;
25111 if (win.getSelection && win.getSelection().getRangeAt) {
25112 range = win.getSelection().getRangeAt(0);
25113 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25114 range.insertNode(node);
25115 } else if (win.document.selection && win.document.selection.createRange) {
25116 // no firefox support
25117 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25118 win.document.selection.createRange().pasteHTML(txt);
25120 // no firefox support
25121 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25122 this.execCmd('InsertHTML', txt);
25131 mozKeyPress : function(e){
25133 var c = e.getCharCode(), cmd;
25136 c = String.fromCharCode(c).toLowerCase();
25150 this.cleanUpPaste.defer(100, this);
25158 e.preventDefault();
25166 fixKeys : function(){ // load time branching for fastest keydown performance
25168 return function(e){
25169 var k = e.getKey(), r;
25172 r = this.doc.selection.createRange();
25175 r.pasteHTML('    ');
25182 r = this.doc.selection.createRange();
25184 var target = r.parentElement();
25185 if(!target || target.tagName.toLowerCase() != 'li'){
25187 r.pasteHTML('<br />');
25193 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25194 this.cleanUpPaste.defer(100, this);
25200 }else if(Roo.isOpera){
25201 return function(e){
25202 var k = e.getKey();
25206 this.execCmd('InsertHTML','    ');
25209 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25210 this.cleanUpPaste.defer(100, this);
25215 }else if(Roo.isSafari){
25216 return function(e){
25217 var k = e.getKey();
25221 this.execCmd('InsertText','\t');
25225 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25226 this.cleanUpPaste.defer(100, this);
25234 getAllAncestors: function()
25236 var p = this.getSelectedNode();
25239 a.push(p); // push blank onto stack..
25240 p = this.getParentElement();
25244 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25248 a.push(this.doc.body);
25252 lastSelNode : false,
25255 getSelection : function()
25257 this.assignDocWin();
25258 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25261 getSelectedNode: function()
25263 // this may only work on Gecko!!!
25265 // should we cache this!!!!
25270 var range = this.createRange(this.getSelection()).cloneRange();
25273 var parent = range.parentElement();
25275 var testRange = range.duplicate();
25276 testRange.moveToElementText(parent);
25277 if (testRange.inRange(range)) {
25280 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25283 parent = parent.parentElement;
25288 // is ancestor a text element.
25289 var ac = range.commonAncestorContainer;
25290 if (ac.nodeType == 3) {
25291 ac = ac.parentNode;
25294 var ar = ac.childNodes;
25297 var other_nodes = [];
25298 var has_other_nodes = false;
25299 for (var i=0;i<ar.length;i++) {
25300 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25303 // fullly contained node.
25305 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25310 // probably selected..
25311 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25312 other_nodes.push(ar[i]);
25316 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25321 has_other_nodes = true;
25323 if (!nodes.length && other_nodes.length) {
25324 nodes= other_nodes;
25326 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25332 createRange: function(sel)
25334 // this has strange effects when using with
25335 // top toolbar - not sure if it's a great idea.
25336 //this.editor.contentWindow.focus();
25337 if (typeof sel != "undefined") {
25339 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25341 return this.doc.createRange();
25344 return this.doc.createRange();
25347 getParentElement: function()
25350 this.assignDocWin();
25351 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25353 var range = this.createRange(sel);
25356 var p = range.commonAncestorContainer;
25357 while (p.nodeType == 3) { // text node
25368 * Range intersection.. the hard stuff...
25372 * [ -- selected range --- ]
25376 * if end is before start or hits it. fail.
25377 * if start is after end or hits it fail.
25379 * if either hits (but other is outside. - then it's not
25385 // @see http://www.thismuchiknow.co.uk/?p=64.
25386 rangeIntersectsNode : function(range, node)
25388 var nodeRange = node.ownerDocument.createRange();
25390 nodeRange.selectNode(node);
25392 nodeRange.selectNodeContents(node);
25395 var rangeStartRange = range.cloneRange();
25396 rangeStartRange.collapse(true);
25398 var rangeEndRange = range.cloneRange();
25399 rangeEndRange.collapse(false);
25401 var nodeStartRange = nodeRange.cloneRange();
25402 nodeStartRange.collapse(true);
25404 var nodeEndRange = nodeRange.cloneRange();
25405 nodeEndRange.collapse(false);
25407 return rangeStartRange.compareBoundaryPoints(
25408 Range.START_TO_START, nodeEndRange) == -1 &&
25409 rangeEndRange.compareBoundaryPoints(
25410 Range.START_TO_START, nodeStartRange) == 1;
25414 rangeCompareNode : function(range, node)
25416 var nodeRange = node.ownerDocument.createRange();
25418 nodeRange.selectNode(node);
25420 nodeRange.selectNodeContents(node);
25424 range.collapse(true);
25426 nodeRange.collapse(true);
25428 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25429 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25431 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25433 var nodeIsBefore = ss == 1;
25434 var nodeIsAfter = ee == -1;
25436 if (nodeIsBefore && nodeIsAfter) {
25439 if (!nodeIsBefore && nodeIsAfter) {
25440 return 1; //right trailed.
25443 if (nodeIsBefore && !nodeIsAfter) {
25444 return 2; // left trailed.
25450 // private? - in a new class?
25451 cleanUpPaste : function()
25453 // cleans up the whole document..
25454 Roo.log('cleanuppaste');
25456 this.cleanUpChildren(this.doc.body);
25457 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25458 if (clean != this.doc.body.innerHTML) {
25459 this.doc.body.innerHTML = clean;
25464 cleanWordChars : function(input) {// change the chars to hex code
25465 var he = Roo.HtmlEditorCore;
25467 var output = input;
25468 Roo.each(he.swapCodes, function(sw) {
25469 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25471 output = output.replace(swapper, sw[1]);
25478 cleanUpChildren : function (n)
25480 if (!n.childNodes.length) {
25483 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25484 this.cleanUpChild(n.childNodes[i]);
25491 cleanUpChild : function (node)
25494 //console.log(node);
25495 if (node.nodeName == "#text") {
25496 // clean up silly Windows -- stuff?
25499 if (node.nodeName == "#comment") {
25500 node.parentNode.removeChild(node);
25501 // clean up silly Windows -- stuff?
25504 var lcname = node.tagName.toLowerCase();
25505 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25506 // whitelist of tags..
25508 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25510 node.parentNode.removeChild(node);
25515 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25517 // spans with no attributes - just remove them..
25518 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25519 remove_keep_children = true;
25522 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25523 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25525 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25526 // remove_keep_children = true;
25529 if (remove_keep_children) {
25530 this.cleanUpChildren(node);
25531 // inserts everything just before this node...
25532 while (node.childNodes.length) {
25533 var cn = node.childNodes[0];
25534 node.removeChild(cn);
25535 node.parentNode.insertBefore(cn, node);
25537 node.parentNode.removeChild(node);
25541 if (!node.attributes || !node.attributes.length) {
25546 this.cleanUpChildren(node);
25550 function cleanAttr(n,v)
25553 if (v.match(/^\./) || v.match(/^\//)) {
25556 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25559 if (v.match(/^#/)) {
25562 if (v.match(/^\{/)) { // allow template editing.
25565 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25566 node.removeAttribute(n);
25570 var cwhite = this.cwhite;
25571 var cblack = this.cblack;
25573 function cleanStyle(n,v)
25575 if (v.match(/expression/)) { //XSS?? should we even bother..
25576 node.removeAttribute(n);
25580 var parts = v.split(/;/);
25583 Roo.each(parts, function(p) {
25584 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25588 var l = p.split(':').shift().replace(/\s+/g,'');
25589 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25591 if ( cwhite.length && cblack.indexOf(l) > -1) {
25592 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25593 //node.removeAttribute(n);
25597 // only allow 'c whitelisted system attributes'
25598 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25599 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25600 //node.removeAttribute(n);
25610 if (clean.length) {
25611 node.setAttribute(n, clean.join(';'));
25613 node.removeAttribute(n);
25619 for (var i = node.attributes.length-1; i > -1 ; i--) {
25620 var a = node.attributes[i];
25623 if (a.name.toLowerCase().substr(0,2)=='on') {
25624 node.removeAttribute(a.name);
25627 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25628 node.removeAttribute(a.name);
25631 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25632 cleanAttr(a.name,a.value); // fixme..
25635 if (a.name == 'style') {
25636 cleanStyle(a.name,a.value);
25639 /// clean up MS crap..
25640 // tecnically this should be a list of valid class'es..
25643 if (a.name == 'class') {
25644 if (a.value.match(/^Mso/)) {
25645 node.removeAttribute('class');
25648 if (a.value.match(/^body$/)) {
25649 node.removeAttribute('class');
25660 this.cleanUpChildren(node);
25666 * Clean up MS wordisms...
25668 cleanWord : function(node)
25671 this.cleanWord(this.doc.body);
25676 node.nodeName == 'SPAN' &&
25677 !node.hasAttributes() &&
25678 node.childNodes.length == 1 &&
25679 node.firstChild.nodeName == "#text"
25681 var textNode = node.firstChild;
25682 node.removeChild(textNode);
25683 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25684 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25686 node.parentNode.insertBefore(textNode, node);
25687 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25688 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25690 node.parentNode.removeChild(node);
25693 if (node.nodeName == "#text") {
25694 // clean up silly Windows -- stuff?
25697 if (node.nodeName == "#comment") {
25698 node.parentNode.removeChild(node);
25699 // clean up silly Windows -- stuff?
25703 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25704 node.parentNode.removeChild(node);
25707 //Roo.log(node.tagName);
25708 // remove - but keep children..
25709 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25710 //Roo.log('-- removed');
25711 while (node.childNodes.length) {
25712 var cn = node.childNodes[0];
25713 node.removeChild(cn);
25714 node.parentNode.insertBefore(cn, node);
25715 // move node to parent - and clean it..
25716 this.cleanWord(cn);
25718 node.parentNode.removeChild(node);
25719 /// no need to iterate chidlren = it's got none..
25720 //this.iterateChildren(node, this.cleanWord);
25724 if (node.className.length) {
25726 var cn = node.className.split(/\W+/);
25728 Roo.each(cn, function(cls) {
25729 if (cls.match(/Mso[a-zA-Z]+/)) {
25734 node.className = cna.length ? cna.join(' ') : '';
25736 node.removeAttribute("class");
25740 if (node.hasAttribute("lang")) {
25741 node.removeAttribute("lang");
25744 if (node.hasAttribute("style")) {
25746 var styles = node.getAttribute("style").split(";");
25748 Roo.each(styles, function(s) {
25749 if (!s.match(/:/)) {
25752 var kv = s.split(":");
25753 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25756 // what ever is left... we allow.
25759 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25760 if (!nstyle.length) {
25761 node.removeAttribute('style');
25764 this.iterateChildren(node, this.cleanWord);
25770 * iterateChildren of a Node, calling fn each time, using this as the scole..
25771 * @param {DomNode} node node to iterate children of.
25772 * @param {Function} fn method of this class to call on each item.
25774 iterateChildren : function(node, fn)
25776 if (!node.childNodes.length) {
25779 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25780 fn.call(this, node.childNodes[i])
25786 * cleanTableWidths.
25788 * Quite often pasting from word etc.. results in tables with column and widths.
25789 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25792 cleanTableWidths : function(node)
25797 this.cleanTableWidths(this.doc.body);
25802 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25805 Roo.log(node.tagName);
25806 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25807 this.iterateChildren(node, this.cleanTableWidths);
25810 if (node.hasAttribute('width')) {
25811 node.removeAttribute('width');
25815 if (node.hasAttribute("style")) {
25818 var styles = node.getAttribute("style").split(";");
25820 Roo.each(styles, function(s) {
25821 if (!s.match(/:/)) {
25824 var kv = s.split(":");
25825 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25828 // what ever is left... we allow.
25831 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25832 if (!nstyle.length) {
25833 node.removeAttribute('style');
25837 this.iterateChildren(node, this.cleanTableWidths);
25845 domToHTML : function(currentElement, depth, nopadtext) {
25847 depth = depth || 0;
25848 nopadtext = nopadtext || false;
25850 if (!currentElement) {
25851 return this.domToHTML(this.doc.body);
25854 //Roo.log(currentElement);
25856 var allText = false;
25857 var nodeName = currentElement.nodeName;
25858 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25860 if (nodeName == '#text') {
25862 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25867 if (nodeName != 'BODY') {
25870 // Prints the node tagName, such as <A>, <IMG>, etc
25873 for(i = 0; i < currentElement.attributes.length;i++) {
25875 var aname = currentElement.attributes.item(i).name;
25876 if (!currentElement.attributes.item(i).value.length) {
25879 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25882 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25891 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25894 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25899 // Traverse the tree
25901 var currentElementChild = currentElement.childNodes.item(i);
25902 var allText = true;
25903 var innerHTML = '';
25905 while (currentElementChild) {
25906 // Formatting code (indent the tree so it looks nice on the screen)
25907 var nopad = nopadtext;
25908 if (lastnode == 'SPAN') {
25912 if (currentElementChild.nodeName == '#text') {
25913 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25914 toadd = nopadtext ? toadd : toadd.trim();
25915 if (!nopad && toadd.length > 80) {
25916 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25918 innerHTML += toadd;
25921 currentElementChild = currentElement.childNodes.item(i);
25927 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25929 // Recursively traverse the tree structure of the child node
25930 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25931 lastnode = currentElementChild.nodeName;
25933 currentElementChild=currentElement.childNodes.item(i);
25939 // The remaining code is mostly for formatting the tree
25940 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25945 ret+= "</"+tagName+">";
25951 applyBlacklists : function()
25953 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25954 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25958 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25959 if (b.indexOf(tag) > -1) {
25962 this.white.push(tag);
25966 Roo.each(w, function(tag) {
25967 if (b.indexOf(tag) > -1) {
25970 if (this.white.indexOf(tag) > -1) {
25973 this.white.push(tag);
25978 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25979 if (w.indexOf(tag) > -1) {
25982 this.black.push(tag);
25986 Roo.each(b, function(tag) {
25987 if (w.indexOf(tag) > -1) {
25990 if (this.black.indexOf(tag) > -1) {
25993 this.black.push(tag);
25998 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
25999 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26003 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26004 if (b.indexOf(tag) > -1) {
26007 this.cwhite.push(tag);
26011 Roo.each(w, function(tag) {
26012 if (b.indexOf(tag) > -1) {
26015 if (this.cwhite.indexOf(tag) > -1) {
26018 this.cwhite.push(tag);
26023 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26024 if (w.indexOf(tag) > -1) {
26027 this.cblack.push(tag);
26031 Roo.each(b, function(tag) {
26032 if (w.indexOf(tag) > -1) {
26035 if (this.cblack.indexOf(tag) > -1) {
26038 this.cblack.push(tag);
26043 setStylesheets : function(stylesheets)
26045 if(typeof(stylesheets) == 'string'){
26046 Roo.get(this.iframe.contentDocument.head).createChild({
26048 rel : 'stylesheet',
26057 Roo.each(stylesheets, function(s) {
26062 Roo.get(_this.iframe.contentDocument.head).createChild({
26064 rel : 'stylesheet',
26073 removeStylesheets : function()
26077 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26082 setStyle : function(style)
26084 Roo.get(this.iframe.contentDocument.head).createChild({
26093 // hide stuff that is not compatible
26107 * @event specialkey
26111 * @cfg {String} fieldClass @hide
26114 * @cfg {String} focusClass @hide
26117 * @cfg {String} autoCreate @hide
26120 * @cfg {String} inputType @hide
26123 * @cfg {String} invalidClass @hide
26126 * @cfg {String} invalidText @hide
26129 * @cfg {String} msgFx @hide
26132 * @cfg {String} validateOnBlur @hide
26136 Roo.HtmlEditorCore.white = [
26137 'area', 'br', 'img', 'input', 'hr', 'wbr',
26139 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26140 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26141 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26142 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26143 'table', 'ul', 'xmp',
26145 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26148 'dir', 'menu', 'ol', 'ul', 'dl',
26154 Roo.HtmlEditorCore.black = [
26155 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26157 'base', 'basefont', 'bgsound', 'blink', 'body',
26158 'frame', 'frameset', 'head', 'html', 'ilayer',
26159 'iframe', 'layer', 'link', 'meta', 'object',
26160 'script', 'style' ,'title', 'xml' // clean later..
26162 Roo.HtmlEditorCore.clean = [
26163 'script', 'style', 'title', 'xml'
26165 Roo.HtmlEditorCore.remove = [
26170 Roo.HtmlEditorCore.ablack = [
26174 Roo.HtmlEditorCore.aclean = [
26175 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26179 Roo.HtmlEditorCore.pwhite= [
26180 'http', 'https', 'mailto'
26183 // white listed style attributes.
26184 Roo.HtmlEditorCore.cwhite= [
26185 // 'text-align', /// default is to allow most things..
26191 // black listed style attributes.
26192 Roo.HtmlEditorCore.cblack= [
26193 // 'font-size' -- this can be set by the project
26197 Roo.HtmlEditorCore.swapCodes =[
26198 [ 8211, "–" ],
26199 [ 8212, "—" ],
26216 * @class Roo.bootstrap.HtmlEditor
26217 * @extends Roo.bootstrap.TextArea
26218 * Bootstrap HtmlEditor class
26221 * Create a new HtmlEditor
26222 * @param {Object} config The config object
26225 Roo.bootstrap.HtmlEditor = function(config){
26226 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26227 if (!this.toolbars) {
26228 this.toolbars = [];
26231 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26234 * @event initialize
26235 * Fires when the editor is fully initialized (including the iframe)
26236 * @param {HtmlEditor} this
26241 * Fires when the editor is first receives the focus. Any insertion must wait
26242 * until after this event.
26243 * @param {HtmlEditor} this
26247 * @event beforesync
26248 * Fires before the textarea is updated with content from the editor iframe. Return false
26249 * to cancel the sync.
26250 * @param {HtmlEditor} this
26251 * @param {String} html
26255 * @event beforepush
26256 * Fires before the iframe editor is updated with content from the textarea. Return false
26257 * to cancel the push.
26258 * @param {HtmlEditor} this
26259 * @param {String} html
26264 * Fires when the textarea is updated with content from the editor iframe.
26265 * @param {HtmlEditor} this
26266 * @param {String} html
26271 * Fires when the iframe editor is updated with content from the textarea.
26272 * @param {HtmlEditor} this
26273 * @param {String} html
26277 * @event editmodechange
26278 * Fires when the editor switches edit modes
26279 * @param {HtmlEditor} this
26280 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26282 editmodechange: true,
26284 * @event editorevent
26285 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26286 * @param {HtmlEditor} this
26290 * @event firstfocus
26291 * Fires when on first focus - needed by toolbars..
26292 * @param {HtmlEditor} this
26297 * Auto save the htmlEditor value as a file into Events
26298 * @param {HtmlEditor} this
26302 * @event savedpreview
26303 * preview the saved version of htmlEditor
26304 * @param {HtmlEditor} this
26311 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26315 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26320 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26325 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26330 * @cfg {Number} height (in pixels)
26334 * @cfg {Number} width (in pixels)
26339 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26342 stylesheets: false,
26347 // private properties
26348 validationEvent : false,
26350 initialized : false,
26353 onFocus : Roo.emptyFn,
26355 hideMode:'offsets',
26357 tbContainer : false,
26361 toolbarContainer :function() {
26362 return this.wrap.select('.x-html-editor-tb',true).first();
26366 * Protected method that will not generally be called directly. It
26367 * is called when the editor creates its toolbar. Override this method if you need to
26368 * add custom toolbar buttons.
26369 * @param {HtmlEditor} editor
26371 createToolbar : function(){
26372 Roo.log('renewing');
26373 Roo.log("create toolbars");
26375 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26376 this.toolbars[0].render(this.toolbarContainer());
26380 // if (!editor.toolbars || !editor.toolbars.length) {
26381 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26384 // for (var i =0 ; i < editor.toolbars.length;i++) {
26385 // editor.toolbars[i] = Roo.factory(
26386 // typeof(editor.toolbars[i]) == 'string' ?
26387 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26388 // Roo.bootstrap.HtmlEditor);
26389 // editor.toolbars[i].init(editor);
26395 onRender : function(ct, position)
26397 // Roo.log("Call onRender: " + this.xtype);
26399 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26401 this.wrap = this.inputEl().wrap({
26402 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26405 this.editorcore.onRender(ct, position);
26407 if (this.resizable) {
26408 this.resizeEl = new Roo.Resizable(this.wrap, {
26412 minHeight : this.height,
26413 height: this.height,
26414 handles : this.resizable,
26417 resize : function(r, w, h) {
26418 _t.onResize(w,h); // -something
26424 this.createToolbar(this);
26427 if(!this.width && this.resizable){
26428 this.setSize(this.wrap.getSize());
26430 if (this.resizeEl) {
26431 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26432 // should trigger onReize..
26438 onResize : function(w, h)
26440 Roo.log('resize: ' +w + ',' + h );
26441 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26445 if(this.inputEl() ){
26446 if(typeof w == 'number'){
26447 var aw = w - this.wrap.getFrameWidth('lr');
26448 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26451 if(typeof h == 'number'){
26452 var tbh = -11; // fixme it needs to tool bar size!
26453 for (var i =0; i < this.toolbars.length;i++) {
26454 // fixme - ask toolbars for heights?
26455 tbh += this.toolbars[i].el.getHeight();
26456 //if (this.toolbars[i].footer) {
26457 // tbh += this.toolbars[i].footer.el.getHeight();
26465 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26466 ah -= 5; // knock a few pixes off for look..
26467 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26471 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26472 this.editorcore.onResize(ew,eh);
26477 * Toggles the editor between standard and source edit mode.
26478 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26480 toggleSourceEdit : function(sourceEditMode)
26482 this.editorcore.toggleSourceEdit(sourceEditMode);
26484 if(this.editorcore.sourceEditMode){
26485 Roo.log('editor - showing textarea');
26488 // Roo.log(this.syncValue());
26490 this.inputEl().removeClass(['hide', 'x-hidden']);
26491 this.inputEl().dom.removeAttribute('tabIndex');
26492 this.inputEl().focus();
26494 Roo.log('editor - hiding textarea');
26496 // Roo.log(this.pushValue());
26499 this.inputEl().addClass(['hide', 'x-hidden']);
26500 this.inputEl().dom.setAttribute('tabIndex', -1);
26501 //this.deferFocus();
26504 if(this.resizable){
26505 this.setSize(this.wrap.getSize());
26508 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26511 // private (for BoxComponent)
26512 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26514 // private (for BoxComponent)
26515 getResizeEl : function(){
26519 // private (for BoxComponent)
26520 getPositionEl : function(){
26525 initEvents : function(){
26526 this.originalValue = this.getValue();
26530 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26533 // markInvalid : Roo.emptyFn,
26535 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26538 // clearInvalid : Roo.emptyFn,
26540 setValue : function(v){
26541 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26542 this.editorcore.pushValue();
26547 deferFocus : function(){
26548 this.focus.defer(10, this);
26552 focus : function(){
26553 this.editorcore.focus();
26559 onDestroy : function(){
26565 for (var i =0; i < this.toolbars.length;i++) {
26566 // fixme - ask toolbars for heights?
26567 this.toolbars[i].onDestroy();
26570 this.wrap.dom.innerHTML = '';
26571 this.wrap.remove();
26576 onFirstFocus : function(){
26577 //Roo.log("onFirstFocus");
26578 this.editorcore.onFirstFocus();
26579 for (var i =0; i < this.toolbars.length;i++) {
26580 this.toolbars[i].onFirstFocus();
26586 syncValue : function()
26588 this.editorcore.syncValue();
26591 pushValue : function()
26593 this.editorcore.pushValue();
26597 // hide stuff that is not compatible
26611 * @event specialkey
26615 * @cfg {String} fieldClass @hide
26618 * @cfg {String} focusClass @hide
26621 * @cfg {String} autoCreate @hide
26624 * @cfg {String} inputType @hide
26628 * @cfg {String} invalidText @hide
26631 * @cfg {String} msgFx @hide
26634 * @cfg {String} validateOnBlur @hide
26643 Roo.namespace('Roo.bootstrap.htmleditor');
26645 * @class Roo.bootstrap.HtmlEditorToolbar1
26651 new Roo.bootstrap.HtmlEditor({
26654 new Roo.bootstrap.HtmlEditorToolbar1({
26655 disable : { fonts: 1 , format: 1, ..., ... , ...],
26661 * @cfg {Object} disable List of elements to disable..
26662 * @cfg {Array} btns List of additional buttons.
26666 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26669 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26672 Roo.apply(this, config);
26674 // default disabled, based on 'good practice'..
26675 this.disable = this.disable || {};
26676 Roo.applyIf(this.disable, {
26679 specialElements : true
26681 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26683 this.editor = config.editor;
26684 this.editorcore = config.editor.editorcore;
26686 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26688 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26689 // dont call parent... till later.
26691 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26696 editorcore : false,
26701 "h1","h2","h3","h4","h5","h6",
26703 "abbr", "acronym", "address", "cite", "samp", "var",
26707 onRender : function(ct, position)
26709 // Roo.log("Call onRender: " + this.xtype);
26711 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26713 this.el.dom.style.marginBottom = '0';
26715 var editorcore = this.editorcore;
26716 var editor= this.editor;
26719 var btn = function(id,cmd , toggle, handler, html){
26721 var event = toggle ? 'toggle' : 'click';
26726 xns: Roo.bootstrap,
26730 enableToggle:toggle !== false,
26732 pressed : toggle ? false : null,
26735 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26736 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26742 // var cb_box = function...
26747 xns: Roo.bootstrap,
26752 xns: Roo.bootstrap,
26756 Roo.each(this.formats, function(f) {
26757 style.menu.items.push({
26759 xns: Roo.bootstrap,
26760 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26765 editorcore.insertTag(this.tagname);
26772 children.push(style);
26774 btn('bold',false,true);
26775 btn('italic',false,true);
26776 btn('align-left', 'justifyleft',true);
26777 btn('align-center', 'justifycenter',true);
26778 btn('align-right' , 'justifyright',true);
26779 btn('link', false, false, function(btn) {
26780 //Roo.log("create link?");
26781 var url = prompt(this.createLinkText, this.defaultLinkValue);
26782 if(url && url != 'http:/'+'/'){
26783 this.editorcore.relayCmd('createlink', url);
26786 btn('list','insertunorderedlist',true);
26787 btn('pencil', false,true, function(btn){
26789 this.toggleSourceEdit(btn.pressed);
26792 if (this.editor.btns.length > 0) {
26793 for (var i = 0; i<this.editor.btns.length; i++) {
26794 children.push(this.editor.btns[i]);
26802 xns: Roo.bootstrap,
26807 xns: Roo.bootstrap,
26812 cog.menu.items.push({
26814 xns: Roo.bootstrap,
26815 html : Clean styles,
26820 editorcore.insertTag(this.tagname);
26829 this.xtype = 'NavSimplebar';
26831 for(var i=0;i< children.length;i++) {
26833 this.buttons.add(this.addxtypeChild(children[i]));
26837 editor.on('editorevent', this.updateToolbar, this);
26839 onBtnClick : function(id)
26841 this.editorcore.relayCmd(id);
26842 this.editorcore.focus();
26846 * Protected method that will not generally be called directly. It triggers
26847 * a toolbar update by reading the markup state of the current selection in the editor.
26849 updateToolbar: function(){
26851 if(!this.editorcore.activated){
26852 this.editor.onFirstFocus(); // is this neeed?
26856 var btns = this.buttons;
26857 var doc = this.editorcore.doc;
26858 btns.get('bold').setActive(doc.queryCommandState('bold'));
26859 btns.get('italic').setActive(doc.queryCommandState('italic'));
26860 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26862 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26863 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26864 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26866 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26867 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26870 var ans = this.editorcore.getAllAncestors();
26871 if (this.formatCombo) {
26874 var store = this.formatCombo.store;
26875 this.formatCombo.setValue("");
26876 for (var i =0; i < ans.length;i++) {
26877 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26879 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26887 // hides menus... - so this cant be on a menu...
26888 Roo.bootstrap.MenuMgr.hideAll();
26890 Roo.bootstrap.MenuMgr.hideAll();
26891 //this.editorsyncValue();
26893 onFirstFocus: function() {
26894 this.buttons.each(function(item){
26898 toggleSourceEdit : function(sourceEditMode){
26901 if(sourceEditMode){
26902 Roo.log("disabling buttons");
26903 this.buttons.each( function(item){
26904 if(item.cmd != 'pencil'){
26910 Roo.log("enabling buttons");
26911 if(this.editorcore.initialized){
26912 this.buttons.each( function(item){
26918 Roo.log("calling toggole on editor");
26919 // tell the editor that it's been pressed..
26920 this.editor.toggleSourceEdit(sourceEditMode);
26934 * @class Roo.bootstrap.Markdown
26935 * @extends Roo.bootstrap.TextArea
26936 * Bootstrap Showdown editable area
26937 * @cfg {string} content
26940 * Create a new Showdown
26943 Roo.bootstrap.Markdown = function(config){
26944 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26948 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26952 initEvents : function()
26955 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26956 this.markdownEl = this.el.createChild({
26957 cls : 'roo-markdown-area'
26959 this.inputEl().addClass('d-none');
26960 if (this.getValue() == '') {
26961 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26964 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26966 this.markdownEl.on('click', this.toggleTextEdit, this);
26967 this.on('blur', this.toggleTextEdit, this);
26968 this.on('specialkey', this.resizeTextArea, this);
26971 toggleTextEdit : function()
26973 var sh = this.markdownEl.getHeight();
26974 this.inputEl().addClass('d-none');
26975 this.markdownEl.addClass('d-none');
26976 if (!this.editing) {
26978 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26979 this.inputEl().removeClass('d-none');
26980 this.inputEl().focus();
26981 this.editing = true;
26984 // show showdown...
26985 this.updateMarkdown();
26986 this.markdownEl.removeClass('d-none');
26987 this.editing = false;
26990 updateMarkdown : function()
26992 if (this.getValue() == '') {
26993 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26997 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27000 resizeTextArea: function () {
27003 Roo.log([sh, this.getValue().split("\n").length * 30]);
27004 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27006 setValue : function(val)
27008 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27009 if (!this.editing) {
27010 this.updateMarkdown();
27016 if (!this.editing) {
27017 this.toggleTextEdit();
27025 * @class Roo.bootstrap.Table.AbstractSelectionModel
27026 * @extends Roo.util.Observable
27027 * Abstract base class for grid SelectionModels. It provides the interface that should be
27028 * implemented by descendant classes. This class should not be directly instantiated.
27031 Roo.bootstrap.Table.AbstractSelectionModel = function(){
27032 this.locked = false;
27033 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27037 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
27038 /** @ignore Called by the grid automatically. Do not call directly. */
27039 init : function(grid){
27045 * Locks the selections.
27048 this.locked = true;
27052 * Unlocks the selections.
27054 unlock : function(){
27055 this.locked = false;
27059 * Returns true if the selections are locked.
27060 * @return {Boolean}
27062 isLocked : function(){
27063 return this.locked;
27067 initEvents : function ()
27073 * @extends Roo.bootstrap.Table.AbstractSelectionModel
27074 * @class Roo.bootstrap.Table.RowSelectionModel
27075 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27076 * It supports multiple selections and keyboard selection/navigation.
27078 * @param {Object} config
27081 Roo.bootstrap.Table.RowSelectionModel = function(config){
27082 Roo.apply(this, config);
27083 this.selections = new Roo.util.MixedCollection(false, function(o){
27088 this.lastActive = false;
27092 * @event selectionchange
27093 * Fires when the selection changes
27094 * @param {SelectionModel} this
27096 "selectionchange" : true,
27098 * @event afterselectionchange
27099 * Fires after the selection changes (eg. by key press or clicking)
27100 * @param {SelectionModel} this
27102 "afterselectionchange" : true,
27104 * @event beforerowselect
27105 * Fires when a row is selected being selected, return false to cancel.
27106 * @param {SelectionModel} this
27107 * @param {Number} rowIndex The selected index
27108 * @param {Boolean} keepExisting False if other selections will be cleared
27110 "beforerowselect" : true,
27113 * Fires when a row is selected.
27114 * @param {SelectionModel} this
27115 * @param {Number} rowIndex The selected index
27116 * @param {Roo.data.Record} r The record
27118 "rowselect" : true,
27120 * @event rowdeselect
27121 * Fires when a row is deselected.
27122 * @param {SelectionModel} this
27123 * @param {Number} rowIndex The selected index
27125 "rowdeselect" : true
27127 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27128 this.locked = false;
27131 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
27133 * @cfg {Boolean} singleSelect
27134 * True to allow selection of only one row at a time (defaults to false)
27136 singleSelect : false,
27139 initEvents : function()
27142 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27143 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
27144 //}else{ // allow click to work like normal
27145 // this.grid.on("rowclick", this.handleDragableRowClick, this);
27147 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27148 this.grid.on("rowclick", this.handleMouseDown, this);
27150 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27151 "up" : function(e){
27153 this.selectPrevious(e.shiftKey);
27154 }else if(this.last !== false && this.lastActive !== false){
27155 var last = this.last;
27156 this.selectRange(this.last, this.lastActive-1);
27157 this.grid.getView().focusRow(this.lastActive);
27158 if(last !== false){
27162 this.selectFirstRow();
27164 this.fireEvent("afterselectionchange", this);
27166 "down" : function(e){
27168 this.selectNext(e.shiftKey);
27169 }else if(this.last !== false && this.lastActive !== false){
27170 var last = this.last;
27171 this.selectRange(this.last, this.lastActive+1);
27172 this.grid.getView().focusRow(this.lastActive);
27173 if(last !== false){
27177 this.selectFirstRow();
27179 this.fireEvent("afterselectionchange", this);
27183 this.grid.store.on('load', function(){
27184 this.selections.clear();
27187 var view = this.grid.view;
27188 view.on("refresh", this.onRefresh, this);
27189 view.on("rowupdated", this.onRowUpdated, this);
27190 view.on("rowremoved", this.onRemove, this);
27195 onRefresh : function()
27197 var ds = this.grid.store, i, v = this.grid.view;
27198 var s = this.selections;
27199 s.each(function(r){
27200 if((i = ds.indexOfId(r.id)) != -1){
27209 onRemove : function(v, index, r){
27210 this.selections.remove(r);
27214 onRowUpdated : function(v, index, r){
27215 if(this.isSelected(r)){
27216 v.onRowSelect(index);
27222 * @param {Array} records The records to select
27223 * @param {Boolean} keepExisting (optional) True to keep existing selections
27225 selectRecords : function(records, keepExisting)
27228 this.clearSelections();
27230 var ds = this.grid.store;
27231 for(var i = 0, len = records.length; i < len; i++){
27232 this.selectRow(ds.indexOf(records[i]), true);
27237 * Gets the number of selected rows.
27240 getCount : function(){
27241 return this.selections.length;
27245 * Selects the first row in the grid.
27247 selectFirstRow : function(){
27252 * Select the last row.
27253 * @param {Boolean} keepExisting (optional) True to keep existing selections
27255 selectLastRow : function(keepExisting){
27256 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27257 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27261 * Selects the row immediately following the last selected row.
27262 * @param {Boolean} keepExisting (optional) True to keep existing selections
27264 selectNext : function(keepExisting)
27266 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27267 this.selectRow(this.last+1, keepExisting);
27268 this.grid.getView().focusRow(this.last);
27273 * Selects the row that precedes the last selected row.
27274 * @param {Boolean} keepExisting (optional) True to keep existing selections
27276 selectPrevious : function(keepExisting){
27278 this.selectRow(this.last-1, keepExisting);
27279 this.grid.getView().focusRow(this.last);
27284 * Returns the selected records
27285 * @return {Array} Array of selected records
27287 getSelections : function(){
27288 return [].concat(this.selections.items);
27292 * Returns the first selected record.
27295 getSelected : function(){
27296 return this.selections.itemAt(0);
27301 * Clears all selections.
27303 clearSelections : function(fast)
27309 var ds = this.grid.store;
27310 var s = this.selections;
27311 s.each(function(r){
27312 this.deselectRow(ds.indexOfId(r.id));
27316 this.selections.clear();
27323 * Selects all rows.
27325 selectAll : function(){
27329 this.selections.clear();
27330 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27331 this.selectRow(i, true);
27336 * Returns True if there is a selection.
27337 * @return {Boolean}
27339 hasSelection : function(){
27340 return this.selections.length > 0;
27344 * Returns True if the specified row is selected.
27345 * @param {Number/Record} record The record or index of the record to check
27346 * @return {Boolean}
27348 isSelected : function(index){
27349 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27350 return (r && this.selections.key(r.id) ? true : false);
27354 * Returns True if the specified record id is selected.
27355 * @param {String} id The id of record to check
27356 * @return {Boolean}
27358 isIdSelected : function(id){
27359 return (this.selections.key(id) ? true : false);
27364 handleMouseDBClick : function(e, t){
27368 handleMouseDown : function(e, t)
27370 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27371 if(this.isLocked() || rowIndex < 0 ){
27374 if(e.shiftKey && this.last !== false){
27375 var last = this.last;
27376 this.selectRange(last, rowIndex, e.ctrlKey);
27377 this.last = last; // reset the last
27381 var isSelected = this.isSelected(rowIndex);
27382 //Roo.log("select row:" + rowIndex);
27384 this.deselectRow(rowIndex);
27386 this.selectRow(rowIndex, true);
27390 if(e.button !== 0 && isSelected){
27391 alert('rowIndex 2: ' + rowIndex);
27392 view.focusRow(rowIndex);
27393 }else if(e.ctrlKey && isSelected){
27394 this.deselectRow(rowIndex);
27395 }else if(!isSelected){
27396 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27397 view.focusRow(rowIndex);
27401 this.fireEvent("afterselectionchange", this);
27404 handleDragableRowClick : function(grid, rowIndex, e)
27406 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27407 this.selectRow(rowIndex, false);
27408 grid.view.focusRow(rowIndex);
27409 this.fireEvent("afterselectionchange", this);
27414 * Selects multiple rows.
27415 * @param {Array} rows Array of the indexes of the row to select
27416 * @param {Boolean} keepExisting (optional) True to keep existing selections
27418 selectRows : function(rows, keepExisting){
27420 this.clearSelections();
27422 for(var i = 0, len = rows.length; i < len; i++){
27423 this.selectRow(rows[i], true);
27428 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27429 * @param {Number} startRow The index of the first row in the range
27430 * @param {Number} endRow The index of the last row in the range
27431 * @param {Boolean} keepExisting (optional) True to retain existing selections
27433 selectRange : function(startRow, endRow, keepExisting){
27438 this.clearSelections();
27440 if(startRow <= endRow){
27441 for(var i = startRow; i <= endRow; i++){
27442 this.selectRow(i, true);
27445 for(var i = startRow; i >= endRow; i--){
27446 this.selectRow(i, true);
27452 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27453 * @param {Number} startRow The index of the first row in the range
27454 * @param {Number} endRow The index of the last row in the range
27456 deselectRange : function(startRow, endRow, preventViewNotify){
27460 for(var i = startRow; i <= endRow; i++){
27461 this.deselectRow(i, preventViewNotify);
27467 * @param {Number} row The index of the row to select
27468 * @param {Boolean} keepExisting (optional) True to keep existing selections
27470 selectRow : function(index, keepExisting, preventViewNotify)
27472 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27475 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27476 if(!keepExisting || this.singleSelect){
27477 this.clearSelections();
27480 var r = this.grid.store.getAt(index);
27481 //console.log('selectRow - record id :' + r.id);
27483 this.selections.add(r);
27484 this.last = this.lastActive = index;
27485 if(!preventViewNotify){
27486 var proxy = new Roo.Element(
27487 this.grid.getRowDom(index)
27489 proxy.addClass('bg-info info');
27491 this.fireEvent("rowselect", this, index, r);
27492 this.fireEvent("selectionchange", this);
27498 * @param {Number} row The index of the row to deselect
27500 deselectRow : function(index, preventViewNotify)
27505 if(this.last == index){
27508 if(this.lastActive == index){
27509 this.lastActive = false;
27512 var r = this.grid.store.getAt(index);
27517 this.selections.remove(r);
27518 //.console.log('deselectRow - record id :' + r.id);
27519 if(!preventViewNotify){
27521 var proxy = new Roo.Element(
27522 this.grid.getRowDom(index)
27524 proxy.removeClass('bg-info info');
27526 this.fireEvent("rowdeselect", this, index);
27527 this.fireEvent("selectionchange", this);
27531 restoreLast : function(){
27533 this.last = this._last;
27538 acceptsNav : function(row, col, cm){
27539 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27543 onEditorKey : function(field, e){
27544 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27549 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27551 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27553 }else if(k == e.ENTER && !e.ctrlKey){
27557 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27559 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27561 }else if(k == e.ESC){
27565 g.startEditing(newCell[0], newCell[1]);
27571 * Ext JS Library 1.1.1
27572 * Copyright(c) 2006-2007, Ext JS, LLC.
27574 * Originally Released Under LGPL - original licence link has changed is not relivant.
27577 * <script type="text/javascript">
27581 * @class Roo.bootstrap.PagingToolbar
27582 * @extends Roo.bootstrap.NavSimplebar
27583 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27585 * Create a new PagingToolbar
27586 * @param {Object} config The config object
27587 * @param {Roo.data.Store} store
27589 Roo.bootstrap.PagingToolbar = function(config)
27591 // old args format still supported... - xtype is prefered..
27592 // created from xtype...
27594 this.ds = config.dataSource;
27596 if (config.store && !this.ds) {
27597 this.store= Roo.factory(config.store, Roo.data);
27598 this.ds = this.store;
27599 this.ds.xmodule = this.xmodule || false;
27602 this.toolbarItems = [];
27603 if (config.items) {
27604 this.toolbarItems = config.items;
27607 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27612 this.bind(this.ds);
27615 if (Roo.bootstrap.version == 4) {
27616 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27618 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27623 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27625 * @cfg {Roo.data.Store} dataSource
27626 * The underlying data store providing the paged data
27629 * @cfg {String/HTMLElement/Element} container
27630 * container The id or element that will contain the toolbar
27633 * @cfg {Boolean} displayInfo
27634 * True to display the displayMsg (defaults to false)
27637 * @cfg {Number} pageSize
27638 * The number of records to display per page (defaults to 20)
27642 * @cfg {String} displayMsg
27643 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27645 displayMsg : 'Displaying {0} - {1} of {2}',
27647 * @cfg {String} emptyMsg
27648 * The message to display when no records are found (defaults to "No data to display")
27650 emptyMsg : 'No data to display',
27652 * Customizable piece of the default paging text (defaults to "Page")
27655 beforePageText : "Page",
27657 * Customizable piece of the default paging text (defaults to "of %0")
27660 afterPageText : "of {0}",
27662 * Customizable piece of the default paging text (defaults to "First Page")
27665 firstText : "First Page",
27667 * Customizable piece of the default paging text (defaults to "Previous Page")
27670 prevText : "Previous Page",
27672 * Customizable piece of the default paging text (defaults to "Next Page")
27675 nextText : "Next Page",
27677 * Customizable piece of the default paging text (defaults to "Last Page")
27680 lastText : "Last Page",
27682 * Customizable piece of the default paging text (defaults to "Refresh")
27685 refreshText : "Refresh",
27689 onRender : function(ct, position)
27691 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27692 this.navgroup.parentId = this.id;
27693 this.navgroup.onRender(this.el, null);
27694 // add the buttons to the navgroup
27696 if(this.displayInfo){
27697 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27698 this.displayEl = this.el.select('.x-paging-info', true).first();
27699 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27700 // this.displayEl = navel.el.select('span',true).first();
27706 Roo.each(_this.buttons, function(e){ // this might need to use render????
27707 Roo.factory(e).render(_this.el);
27711 Roo.each(_this.toolbarItems, function(e) {
27712 _this.navgroup.addItem(e);
27716 this.first = this.navgroup.addItem({
27717 tooltip: this.firstText,
27718 cls: "prev btn-outline-secondary",
27719 html : ' <i class="fa fa-step-backward"></i>',
27721 preventDefault: true,
27722 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27725 this.prev = this.navgroup.addItem({
27726 tooltip: this.prevText,
27727 cls: "prev btn-outline-secondary",
27728 html : ' <i class="fa fa-backward"></i>',
27730 preventDefault: true,
27731 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27733 //this.addSeparator();
27736 var field = this.navgroup.addItem( {
27738 cls : 'x-paging-position btn-outline-secondary',
27740 html : this.beforePageText +
27741 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27742 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27745 this.field = field.el.select('input', true).first();
27746 this.field.on("keydown", this.onPagingKeydown, this);
27747 this.field.on("focus", function(){this.dom.select();});
27750 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27751 //this.field.setHeight(18);
27752 //this.addSeparator();
27753 this.next = this.navgroup.addItem({
27754 tooltip: this.nextText,
27755 cls: "next btn-outline-secondary",
27756 html : ' <i class="fa fa-forward"></i>',
27758 preventDefault: true,
27759 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27761 this.last = this.navgroup.addItem({
27762 tooltip: this.lastText,
27763 html : ' <i class="fa fa-step-forward"></i>',
27764 cls: "next btn-outline-secondary",
27766 preventDefault: true,
27767 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27769 //this.addSeparator();
27770 this.loading = this.navgroup.addItem({
27771 tooltip: this.refreshText,
27772 cls: "btn-outline-secondary",
27773 html : ' <i class="fa fa-refresh"></i>',
27774 preventDefault: true,
27775 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27781 updateInfo : function(){
27782 if(this.displayEl){
27783 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27784 var msg = count == 0 ?
27788 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27790 this.displayEl.update(msg);
27795 onLoad : function(ds, r, o)
27797 this.cursor = o.params && o.params.start ? o.params.start : 0;
27799 var d = this.getPageData(),
27804 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27805 this.field.dom.value = ap;
27806 this.first.setDisabled(ap == 1);
27807 this.prev.setDisabled(ap == 1);
27808 this.next.setDisabled(ap == ps);
27809 this.last.setDisabled(ap == ps);
27810 this.loading.enable();
27815 getPageData : function(){
27816 var total = this.ds.getTotalCount();
27819 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27820 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27825 onLoadError : function(){
27826 this.loading.enable();
27830 onPagingKeydown : function(e){
27831 var k = e.getKey();
27832 var d = this.getPageData();
27834 var v = this.field.dom.value, pageNum;
27835 if(!v || isNaN(pageNum = parseInt(v, 10))){
27836 this.field.dom.value = d.activePage;
27839 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27840 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27843 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))
27845 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27846 this.field.dom.value = pageNum;
27847 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27850 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27852 var v = this.field.dom.value, pageNum;
27853 var increment = (e.shiftKey) ? 10 : 1;
27854 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27857 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27858 this.field.dom.value = d.activePage;
27861 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27863 this.field.dom.value = parseInt(v, 10) + increment;
27864 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27865 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27872 beforeLoad : function(){
27874 this.loading.disable();
27879 onClick : function(which){
27888 ds.load({params:{start: 0, limit: this.pageSize}});
27891 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27894 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27897 var total = ds.getTotalCount();
27898 var extra = total % this.pageSize;
27899 var lastStart = extra ? (total - extra) : total-this.pageSize;
27900 ds.load({params:{start: lastStart, limit: this.pageSize}});
27903 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27909 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27910 * @param {Roo.data.Store} store The data store to unbind
27912 unbind : function(ds){
27913 ds.un("beforeload", this.beforeLoad, this);
27914 ds.un("load", this.onLoad, this);
27915 ds.un("loadexception", this.onLoadError, this);
27916 ds.un("remove", this.updateInfo, this);
27917 ds.un("add", this.updateInfo, this);
27918 this.ds = undefined;
27922 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27923 * @param {Roo.data.Store} store The data store to bind
27925 bind : function(ds){
27926 ds.on("beforeload", this.beforeLoad, this);
27927 ds.on("load", this.onLoad, this);
27928 ds.on("loadexception", this.onLoadError, this);
27929 ds.on("remove", this.updateInfo, this);
27930 ds.on("add", this.updateInfo, this);
27941 * @class Roo.bootstrap.MessageBar
27942 * @extends Roo.bootstrap.Component
27943 * Bootstrap MessageBar class
27944 * @cfg {String} html contents of the MessageBar
27945 * @cfg {String} weight (info | success | warning | danger) default info
27946 * @cfg {String} beforeClass insert the bar before the given class
27947 * @cfg {Boolean} closable (true | false) default false
27948 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27951 * Create a new Element
27952 * @param {Object} config The config object
27955 Roo.bootstrap.MessageBar = function(config){
27956 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27959 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27965 beforeClass: 'bootstrap-sticky-wrap',
27967 getAutoCreate : function(){
27971 cls: 'alert alert-dismissable alert-' + this.weight,
27976 html: this.html || ''
27982 cfg.cls += ' alert-messages-fixed';
27996 onRender : function(ct, position)
27998 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28001 var cfg = Roo.apply({}, this.getAutoCreate());
28005 cfg.cls += ' ' + this.cls;
28008 cfg.style = this.style;
28010 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28012 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28015 this.el.select('>button.close').on('click', this.hide, this);
28021 if (!this.rendered) {
28027 this.fireEvent('show', this);
28033 if (!this.rendered) {
28039 this.fireEvent('hide', this);
28042 update : function()
28044 // var e = this.el.dom.firstChild;
28046 // if(this.closable){
28047 // e = e.nextSibling;
28050 // e.data = this.html || '';
28052 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28068 * @class Roo.bootstrap.Graph
28069 * @extends Roo.bootstrap.Component
28070 * Bootstrap Graph class
28074 @cfg {String} graphtype bar | vbar | pie
28075 @cfg {number} g_x coodinator | centre x (pie)
28076 @cfg {number} g_y coodinator | centre y (pie)
28077 @cfg {number} g_r radius (pie)
28078 @cfg {number} g_height height of the chart (respected by all elements in the set)
28079 @cfg {number} g_width width of the chart (respected by all elements in the set)
28080 @cfg {Object} title The title of the chart
28083 -opts (object) options for the chart
28085 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28086 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28088 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.
28089 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28091 o stretch (boolean)
28093 -opts (object) options for the pie
28096 o startAngle (number)
28097 o endAngle (number)
28101 * Create a new Input
28102 * @param {Object} config The config object
28105 Roo.bootstrap.Graph = function(config){
28106 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28112 * The img click event for the img.
28113 * @param {Roo.EventObject} e
28119 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28130 //g_colors: this.colors,
28137 getAutoCreate : function(){
28148 onRender : function(ct,position){
28151 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28153 if (typeof(Raphael) == 'undefined') {
28154 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28158 this.raphael = Raphael(this.el.dom);
28160 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28161 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28162 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28163 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28165 r.text(160, 10, "Single Series Chart").attr(txtattr);
28166 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28167 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28168 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28170 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28171 r.barchart(330, 10, 300, 220, data1);
28172 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28173 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28176 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28177 // r.barchart(30, 30, 560, 250, xdata, {
28178 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28179 // axis : "0 0 1 1",
28180 // axisxlabels : xdata
28181 // //yvalues : cols,
28184 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28186 // this.load(null,xdata,{
28187 // axis : "0 0 1 1",
28188 // axisxlabels : xdata
28193 load : function(graphtype,xdata,opts)
28195 this.raphael.clear();
28197 graphtype = this.graphtype;
28202 var r = this.raphael,
28203 fin = function () {
28204 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28206 fout = function () {
28207 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28209 pfin = function() {
28210 this.sector.stop();
28211 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28214 this.label[0].stop();
28215 this.label[0].attr({ r: 7.5 });
28216 this.label[1].attr({ "font-weight": 800 });
28219 pfout = function() {
28220 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28223 this.label[0].animate({ r: 5 }, 500, "bounce");
28224 this.label[1].attr({ "font-weight": 400 });
28230 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28233 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28236 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28237 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28239 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28246 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28251 setTitle: function(o)
28256 initEvents: function() {
28259 this.el.on('click', this.onClick, this);
28263 onClick : function(e)
28265 Roo.log('img onclick');
28266 this.fireEvent('click', this, e);
28278 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28281 * @class Roo.bootstrap.dash.NumberBox
28282 * @extends Roo.bootstrap.Component
28283 * Bootstrap NumberBox class
28284 * @cfg {String} headline Box headline
28285 * @cfg {String} content Box content
28286 * @cfg {String} icon Box icon
28287 * @cfg {String} footer Footer text
28288 * @cfg {String} fhref Footer href
28291 * Create a new NumberBox
28292 * @param {Object} config The config object
28296 Roo.bootstrap.dash.NumberBox = function(config){
28297 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28301 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28310 getAutoCreate : function(){
28314 cls : 'small-box ',
28322 cls : 'roo-headline',
28323 html : this.headline
28327 cls : 'roo-content',
28328 html : this.content
28342 cls : 'ion ' + this.icon
28351 cls : 'small-box-footer',
28352 href : this.fhref || '#',
28356 cfg.cn.push(footer);
28363 onRender : function(ct,position){
28364 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28371 setHeadline: function (value)
28373 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28376 setFooter: function (value, href)
28378 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28381 this.el.select('a.small-box-footer',true).first().attr('href', href);
28386 setContent: function (value)
28388 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28391 initEvents: function()
28405 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28408 * @class Roo.bootstrap.dash.TabBox
28409 * @extends Roo.bootstrap.Component
28410 * Bootstrap TabBox class
28411 * @cfg {String} title Title of the TabBox
28412 * @cfg {String} icon Icon of the TabBox
28413 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28414 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28417 * Create a new TabBox
28418 * @param {Object} config The config object
28422 Roo.bootstrap.dash.TabBox = function(config){
28423 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28428 * When a pane is added
28429 * @param {Roo.bootstrap.dash.TabPane} pane
28433 * @event activatepane
28434 * When a pane is activated
28435 * @param {Roo.bootstrap.dash.TabPane} pane
28437 "activatepane" : true
28445 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28450 tabScrollable : false,
28452 getChildContainer : function()
28454 return this.el.select('.tab-content', true).first();
28457 getAutoCreate : function(){
28461 cls: 'pull-left header',
28469 cls: 'fa ' + this.icon
28475 cls: 'nav nav-tabs pull-right',
28481 if(this.tabScrollable){
28488 cls: 'nav nav-tabs pull-right',
28499 cls: 'nav-tabs-custom',
28504 cls: 'tab-content no-padding',
28512 initEvents : function()
28514 //Roo.log('add add pane handler');
28515 this.on('addpane', this.onAddPane, this);
28518 * Updates the box title
28519 * @param {String} html to set the title to.
28521 setTitle : function(value)
28523 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28525 onAddPane : function(pane)
28527 this.panes.push(pane);
28528 //Roo.log('addpane');
28530 // tabs are rendere left to right..
28531 if(!this.showtabs){
28535 var ctr = this.el.select('.nav-tabs', true).first();
28538 var existing = ctr.select('.nav-tab',true);
28539 var qty = existing.getCount();;
28542 var tab = ctr.createChild({
28544 cls : 'nav-tab' + (qty ? '' : ' active'),
28552 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28555 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28557 pane.el.addClass('active');
28562 onTabClick : function(ev,un,ob,pane)
28564 //Roo.log('tab - prev default');
28565 ev.preventDefault();
28568 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28569 pane.tab.addClass('active');
28570 //Roo.log(pane.title);
28571 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28572 // technically we should have a deactivate event.. but maybe add later.
28573 // and it should not de-activate the selected tab...
28574 this.fireEvent('activatepane', pane);
28575 pane.el.addClass('active');
28576 pane.fireEvent('activate');
28581 getActivePane : function()
28584 Roo.each(this.panes, function(p) {
28585 if(p.el.hasClass('active')){
28606 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28608 * @class Roo.bootstrap.TabPane
28609 * @extends Roo.bootstrap.Component
28610 * Bootstrap TabPane class
28611 * @cfg {Boolean} active (false | true) Default false
28612 * @cfg {String} title title of panel
28616 * Create a new TabPane
28617 * @param {Object} config The config object
28620 Roo.bootstrap.dash.TabPane = function(config){
28621 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28627 * When a pane is activated
28628 * @param {Roo.bootstrap.dash.TabPane} pane
28635 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28640 // the tabBox that this is attached to.
28643 getAutoCreate : function()
28651 cfg.cls += ' active';
28656 initEvents : function()
28658 //Roo.log('trigger add pane handler');
28659 this.parent().fireEvent('addpane', this)
28663 * Updates the tab title
28664 * @param {String} html to set the title to.
28666 setTitle: function(str)
28672 this.tab.select('a', true).first().dom.innerHTML = str;
28689 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28692 * @class Roo.bootstrap.menu.Menu
28693 * @extends Roo.bootstrap.Component
28694 * Bootstrap Menu class - container for Menu
28695 * @cfg {String} html Text of the menu
28696 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28697 * @cfg {String} icon Font awesome icon
28698 * @cfg {String} pos Menu align to (top | bottom) default bottom
28702 * Create a new Menu
28703 * @param {Object} config The config object
28707 Roo.bootstrap.menu.Menu = function(config){
28708 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28712 * @event beforeshow
28713 * Fires before this menu is displayed
28714 * @param {Roo.bootstrap.menu.Menu} this
28718 * @event beforehide
28719 * Fires before this menu is hidden
28720 * @param {Roo.bootstrap.menu.Menu} this
28725 * Fires after this menu is displayed
28726 * @param {Roo.bootstrap.menu.Menu} this
28731 * Fires after this menu is hidden
28732 * @param {Roo.bootstrap.menu.Menu} this
28737 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28738 * @param {Roo.bootstrap.menu.Menu} this
28739 * @param {Roo.EventObject} e
28746 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28750 weight : 'default',
28755 getChildContainer : function() {
28756 if(this.isSubMenu){
28760 return this.el.select('ul.dropdown-menu', true).first();
28763 getAutoCreate : function()
28768 cls : 'roo-menu-text',
28776 cls : 'fa ' + this.icon
28787 cls : 'dropdown-button btn btn-' + this.weight,
28792 cls : 'dropdown-toggle btn btn-' + this.weight,
28802 cls : 'dropdown-menu'
28808 if(this.pos == 'top'){
28809 cfg.cls += ' dropup';
28812 if(this.isSubMenu){
28815 cls : 'dropdown-menu'
28822 onRender : function(ct, position)
28824 this.isSubMenu = ct.hasClass('dropdown-submenu');
28826 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28829 initEvents : function()
28831 if(this.isSubMenu){
28835 this.hidden = true;
28837 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28838 this.triggerEl.on('click', this.onTriggerPress, this);
28840 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28841 this.buttonEl.on('click', this.onClick, this);
28847 if(this.isSubMenu){
28851 return this.el.select('ul.dropdown-menu', true).first();
28854 onClick : function(e)
28856 this.fireEvent("click", this, e);
28859 onTriggerPress : function(e)
28861 if (this.isVisible()) {
28868 isVisible : function(){
28869 return !this.hidden;
28874 this.fireEvent("beforeshow", this);
28876 this.hidden = false;
28877 this.el.addClass('open');
28879 Roo.get(document).on("mouseup", this.onMouseUp, this);
28881 this.fireEvent("show", this);
28888 this.fireEvent("beforehide", this);
28890 this.hidden = true;
28891 this.el.removeClass('open');
28893 Roo.get(document).un("mouseup", this.onMouseUp);
28895 this.fireEvent("hide", this);
28898 onMouseUp : function()
28912 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28915 * @class Roo.bootstrap.menu.Item
28916 * @extends Roo.bootstrap.Component
28917 * Bootstrap MenuItem class
28918 * @cfg {Boolean} submenu (true | false) default false
28919 * @cfg {String} html text of the item
28920 * @cfg {String} href the link
28921 * @cfg {Boolean} disable (true | false) default false
28922 * @cfg {Boolean} preventDefault (true | false) default true
28923 * @cfg {String} icon Font awesome icon
28924 * @cfg {String} pos Submenu align to (left | right) default right
28928 * Create a new Item
28929 * @param {Object} config The config object
28933 Roo.bootstrap.menu.Item = function(config){
28934 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28938 * Fires when the mouse is hovering over this menu
28939 * @param {Roo.bootstrap.menu.Item} this
28940 * @param {Roo.EventObject} e
28945 * Fires when the mouse exits this menu
28946 * @param {Roo.bootstrap.menu.Item} this
28947 * @param {Roo.EventObject} e
28953 * The raw click event for the entire grid.
28954 * @param {Roo.EventObject} e
28960 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28965 preventDefault: true,
28970 getAutoCreate : function()
28975 cls : 'roo-menu-item-text',
28983 cls : 'fa ' + this.icon
28992 href : this.href || '#',
28999 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29003 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29005 if(this.pos == 'left'){
29006 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29013 initEvents : function()
29015 this.el.on('mouseover', this.onMouseOver, this);
29016 this.el.on('mouseout', this.onMouseOut, this);
29018 this.el.select('a', true).first().on('click', this.onClick, this);
29022 onClick : function(e)
29024 if(this.preventDefault){
29025 e.preventDefault();
29028 this.fireEvent("click", this, e);
29031 onMouseOver : function(e)
29033 if(this.submenu && this.pos == 'left'){
29034 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29037 this.fireEvent("mouseover", this, e);
29040 onMouseOut : function(e)
29042 this.fireEvent("mouseout", this, e);
29054 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29057 * @class Roo.bootstrap.menu.Separator
29058 * @extends Roo.bootstrap.Component
29059 * Bootstrap Separator class
29062 * Create a new Separator
29063 * @param {Object} config The config object
29067 Roo.bootstrap.menu.Separator = function(config){
29068 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29071 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29073 getAutoCreate : function(){
29076 cls: 'dropdown-divider divider'
29094 * @class Roo.bootstrap.Tooltip
29095 * Bootstrap Tooltip class
29096 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29097 * to determine which dom element triggers the tooltip.
29099 * It needs to add support for additional attributes like tooltip-position
29102 * Create a new Toolti
29103 * @param {Object} config The config object
29106 Roo.bootstrap.Tooltip = function(config){
29107 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29109 this.alignment = Roo.bootstrap.Tooltip.alignment;
29111 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29112 this.alignment = config.alignment;
29117 Roo.apply(Roo.bootstrap.Tooltip, {
29119 * @function init initialize tooltip monitoring.
29123 currentTip : false,
29124 currentRegion : false,
29130 Roo.get(document).on('mouseover', this.enter ,this);
29131 Roo.get(document).on('mouseout', this.leave, this);
29134 this.currentTip = new Roo.bootstrap.Tooltip();
29137 enter : function(ev)
29139 var dom = ev.getTarget();
29141 //Roo.log(['enter',dom]);
29142 var el = Roo.fly(dom);
29143 if (this.currentEl) {
29145 //Roo.log(this.currentEl);
29146 //Roo.log(this.currentEl.contains(dom));
29147 if (this.currentEl == el) {
29150 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29156 if (this.currentTip.el) {
29157 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29161 if(!el || el.dom == document){
29167 if (!el.attr('tooltip')) {
29168 pel = el.findParent("[tooltip]");
29170 bindEl = Roo.get(pel);
29176 // you can not look for children, as if el is the body.. then everythign is the child..
29177 if (!pel && !el.attr('tooltip')) { //
29178 if (!el.select("[tooltip]").elements.length) {
29181 // is the mouse over this child...?
29182 bindEl = el.select("[tooltip]").first();
29183 var xy = ev.getXY();
29184 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29185 //Roo.log("not in region.");
29188 //Roo.log("child element over..");
29191 this.currentEl = el;
29192 this.currentTip.bind(bindEl);
29193 this.currentRegion = Roo.lib.Region.getRegion(dom);
29194 this.currentTip.enter();
29197 leave : function(ev)
29199 var dom = ev.getTarget();
29200 //Roo.log(['leave',dom]);
29201 if (!this.currentEl) {
29206 if (dom != this.currentEl.dom) {
29209 var xy = ev.getXY();
29210 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29213 // only activate leave if mouse cursor is outside... bounding box..
29218 if (this.currentTip) {
29219 this.currentTip.leave();
29221 //Roo.log('clear currentEl');
29222 this.currentEl = false;
29227 'left' : ['r-l', [-2,0], 'right'],
29228 'right' : ['l-r', [2,0], 'left'],
29229 'bottom' : ['t-b', [0,2], 'top'],
29230 'top' : [ 'b-t', [0,-2], 'bottom']
29236 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29241 delay : null, // can be { show : 300 , hide: 500}
29245 hoverState : null, //???
29247 placement : 'bottom',
29251 getAutoCreate : function(){
29258 cls : 'tooltip-arrow arrow'
29261 cls : 'tooltip-inner'
29268 bind : function(el)
29273 initEvents : function()
29275 this.arrowEl = this.el.select('.arrow', true).first();
29276 this.innerEl = this.el.select('.tooltip-inner', true).first();
29279 enter : function () {
29281 if (this.timeout != null) {
29282 clearTimeout(this.timeout);
29285 this.hoverState = 'in';
29286 //Roo.log("enter - show");
29287 if (!this.delay || !this.delay.show) {
29292 this.timeout = setTimeout(function () {
29293 if (_t.hoverState == 'in') {
29296 }, this.delay.show);
29300 clearTimeout(this.timeout);
29302 this.hoverState = 'out';
29303 if (!this.delay || !this.delay.hide) {
29309 this.timeout = setTimeout(function () {
29310 //Roo.log("leave - timeout");
29312 if (_t.hoverState == 'out') {
29314 Roo.bootstrap.Tooltip.currentEl = false;
29319 show : function (msg)
29322 this.render(document.body);
29325 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29327 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29329 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29331 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29332 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29334 var placement = typeof this.placement == 'function' ?
29335 this.placement.call(this, this.el, on_el) :
29338 var autoToken = /\s?auto?\s?/i;
29339 var autoPlace = autoToken.test(placement);
29341 placement = placement.replace(autoToken, '') || 'top';
29345 //this.el.setXY([0,0]);
29347 //this.el.dom.style.display='block';
29349 //this.el.appendTo(on_el);
29351 var p = this.getPosition();
29352 var box = this.el.getBox();
29358 var align = this.alignment[placement];
29360 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29362 if(placement == 'top' || placement == 'bottom'){
29364 placement = 'right';
29367 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29368 placement = 'left';
29371 var scroll = Roo.select('body', true).first().getScroll();
29373 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29377 align = this.alignment[placement];
29379 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29383 var elems = document.getElementsByTagName('div');
29384 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29385 for (var i = 0; i < elems.length; i++) {
29386 var zindex = Number.parseInt(
29387 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29390 if (zindex > highest) {
29397 this.el.dom.style.zIndex = highest;
29399 this.el.alignTo(this.bindEl, align[0],align[1]);
29400 //var arrow = this.el.select('.arrow',true).first();
29401 //arrow.set(align[2],
29403 this.el.addClass(placement);
29404 this.el.addClass("bs-tooltip-"+ placement);
29406 this.el.addClass('in fade show');
29408 this.hoverState = null;
29410 if (this.el.hasClass('fade')) {
29425 //this.el.setXY([0,0]);
29426 this.el.removeClass(['show', 'in']);
29442 * @class Roo.bootstrap.LocationPicker
29443 * @extends Roo.bootstrap.Component
29444 * Bootstrap LocationPicker class
29445 * @cfg {Number} latitude Position when init default 0
29446 * @cfg {Number} longitude Position when init default 0
29447 * @cfg {Number} zoom default 15
29448 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29449 * @cfg {Boolean} mapTypeControl default false
29450 * @cfg {Boolean} disableDoubleClickZoom default false
29451 * @cfg {Boolean} scrollwheel default true
29452 * @cfg {Boolean} streetViewControl default false
29453 * @cfg {Number} radius default 0
29454 * @cfg {String} locationName
29455 * @cfg {Boolean} draggable default true
29456 * @cfg {Boolean} enableAutocomplete default false
29457 * @cfg {Boolean} enableReverseGeocode default true
29458 * @cfg {String} markerTitle
29461 * Create a new LocationPicker
29462 * @param {Object} config The config object
29466 Roo.bootstrap.LocationPicker = function(config){
29468 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29473 * Fires when the picker initialized.
29474 * @param {Roo.bootstrap.LocationPicker} this
29475 * @param {Google Location} location
29479 * @event positionchanged
29480 * Fires when the picker position changed.
29481 * @param {Roo.bootstrap.LocationPicker} this
29482 * @param {Google Location} location
29484 positionchanged : true,
29487 * Fires when the map resize.
29488 * @param {Roo.bootstrap.LocationPicker} this
29493 * Fires when the map show.
29494 * @param {Roo.bootstrap.LocationPicker} this
29499 * Fires when the map hide.
29500 * @param {Roo.bootstrap.LocationPicker} this
29505 * Fires when click the map.
29506 * @param {Roo.bootstrap.LocationPicker} this
29507 * @param {Map event} e
29511 * @event mapRightClick
29512 * Fires when right click the map.
29513 * @param {Roo.bootstrap.LocationPicker} this
29514 * @param {Map event} e
29516 mapRightClick : true,
29518 * @event markerClick
29519 * Fires when click the marker.
29520 * @param {Roo.bootstrap.LocationPicker} this
29521 * @param {Map event} e
29523 markerClick : true,
29525 * @event markerRightClick
29526 * Fires when right click the marker.
29527 * @param {Roo.bootstrap.LocationPicker} this
29528 * @param {Map event} e
29530 markerRightClick : true,
29532 * @event OverlayViewDraw
29533 * Fires when OverlayView Draw
29534 * @param {Roo.bootstrap.LocationPicker} this
29536 OverlayViewDraw : true,
29538 * @event OverlayViewOnAdd
29539 * Fires when OverlayView Draw
29540 * @param {Roo.bootstrap.LocationPicker} this
29542 OverlayViewOnAdd : true,
29544 * @event OverlayViewOnRemove
29545 * Fires when OverlayView Draw
29546 * @param {Roo.bootstrap.LocationPicker} this
29548 OverlayViewOnRemove : true,
29550 * @event OverlayViewShow
29551 * Fires when OverlayView Draw
29552 * @param {Roo.bootstrap.LocationPicker} this
29553 * @param {Pixel} cpx
29555 OverlayViewShow : true,
29557 * @event OverlayViewHide
29558 * Fires when OverlayView Draw
29559 * @param {Roo.bootstrap.LocationPicker} this
29561 OverlayViewHide : true,
29563 * @event loadexception
29564 * Fires when load google lib failed.
29565 * @param {Roo.bootstrap.LocationPicker} this
29567 loadexception : true
29572 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29574 gMapContext: false,
29580 mapTypeControl: false,
29581 disableDoubleClickZoom: false,
29583 streetViewControl: false,
29587 enableAutocomplete: false,
29588 enableReverseGeocode: true,
29591 getAutoCreate: function()
29596 cls: 'roo-location-picker'
29602 initEvents: function(ct, position)
29604 if(!this.el.getWidth() || this.isApplied()){
29608 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29613 initial: function()
29615 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29616 this.fireEvent('loadexception', this);
29620 if(!this.mapTypeId){
29621 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29624 this.gMapContext = this.GMapContext();
29626 this.initOverlayView();
29628 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29632 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29633 _this.setPosition(_this.gMapContext.marker.position);
29636 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29637 _this.fireEvent('mapClick', this, event);
29641 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29642 _this.fireEvent('mapRightClick', this, event);
29646 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29647 _this.fireEvent('markerClick', this, event);
29651 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29652 _this.fireEvent('markerRightClick', this, event);
29656 this.setPosition(this.gMapContext.location);
29658 this.fireEvent('initial', this, this.gMapContext.location);
29661 initOverlayView: function()
29665 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29669 _this.fireEvent('OverlayViewDraw', _this);
29674 _this.fireEvent('OverlayViewOnAdd', _this);
29677 onRemove: function()
29679 _this.fireEvent('OverlayViewOnRemove', _this);
29682 show: function(cpx)
29684 _this.fireEvent('OverlayViewShow', _this, cpx);
29689 _this.fireEvent('OverlayViewHide', _this);
29695 fromLatLngToContainerPixel: function(event)
29697 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29700 isApplied: function()
29702 return this.getGmapContext() == false ? false : true;
29705 getGmapContext: function()
29707 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29710 GMapContext: function()
29712 var position = new google.maps.LatLng(this.latitude, this.longitude);
29714 var _map = new google.maps.Map(this.el.dom, {
29717 mapTypeId: this.mapTypeId,
29718 mapTypeControl: this.mapTypeControl,
29719 disableDoubleClickZoom: this.disableDoubleClickZoom,
29720 scrollwheel: this.scrollwheel,
29721 streetViewControl: this.streetViewControl,
29722 locationName: this.locationName,
29723 draggable: this.draggable,
29724 enableAutocomplete: this.enableAutocomplete,
29725 enableReverseGeocode: this.enableReverseGeocode
29728 var _marker = new google.maps.Marker({
29729 position: position,
29731 title: this.markerTitle,
29732 draggable: this.draggable
29739 location: position,
29740 radius: this.radius,
29741 locationName: this.locationName,
29742 addressComponents: {
29743 formatted_address: null,
29744 addressLine1: null,
29745 addressLine2: null,
29747 streetNumber: null,
29751 stateOrProvince: null
29754 domContainer: this.el.dom,
29755 geodecoder: new google.maps.Geocoder()
29759 drawCircle: function(center, radius, options)
29761 if (this.gMapContext.circle != null) {
29762 this.gMapContext.circle.setMap(null);
29766 options = Roo.apply({}, options, {
29767 strokeColor: "#0000FF",
29768 strokeOpacity: .35,
29770 fillColor: "#0000FF",
29774 options.map = this.gMapContext.map;
29775 options.radius = radius;
29776 options.center = center;
29777 this.gMapContext.circle = new google.maps.Circle(options);
29778 return this.gMapContext.circle;
29784 setPosition: function(location)
29786 this.gMapContext.location = location;
29787 this.gMapContext.marker.setPosition(location);
29788 this.gMapContext.map.panTo(location);
29789 this.drawCircle(location, this.gMapContext.radius, {});
29793 if (this.gMapContext.settings.enableReverseGeocode) {
29794 this.gMapContext.geodecoder.geocode({
29795 latLng: this.gMapContext.location
29796 }, function(results, status) {
29798 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29799 _this.gMapContext.locationName = results[0].formatted_address;
29800 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29802 _this.fireEvent('positionchanged', this, location);
29809 this.fireEvent('positionchanged', this, location);
29814 google.maps.event.trigger(this.gMapContext.map, "resize");
29816 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29818 this.fireEvent('resize', this);
29821 setPositionByLatLng: function(latitude, longitude)
29823 this.setPosition(new google.maps.LatLng(latitude, longitude));
29826 getCurrentPosition: function()
29829 latitude: this.gMapContext.location.lat(),
29830 longitude: this.gMapContext.location.lng()
29834 getAddressName: function()
29836 return this.gMapContext.locationName;
29839 getAddressComponents: function()
29841 return this.gMapContext.addressComponents;
29844 address_component_from_google_geocode: function(address_components)
29848 for (var i = 0; i < address_components.length; i++) {
29849 var component = address_components[i];
29850 if (component.types.indexOf("postal_code") >= 0) {
29851 result.postalCode = component.short_name;
29852 } else if (component.types.indexOf("street_number") >= 0) {
29853 result.streetNumber = component.short_name;
29854 } else if (component.types.indexOf("route") >= 0) {
29855 result.streetName = component.short_name;
29856 } else if (component.types.indexOf("neighborhood") >= 0) {
29857 result.city = component.short_name;
29858 } else if (component.types.indexOf("locality") >= 0) {
29859 result.city = component.short_name;
29860 } else if (component.types.indexOf("sublocality") >= 0) {
29861 result.district = component.short_name;
29862 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29863 result.stateOrProvince = component.short_name;
29864 } else if (component.types.indexOf("country") >= 0) {
29865 result.country = component.short_name;
29869 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29870 result.addressLine2 = "";
29874 setZoomLevel: function(zoom)
29876 this.gMapContext.map.setZoom(zoom);
29889 this.fireEvent('show', this);
29900 this.fireEvent('hide', this);
29905 Roo.apply(Roo.bootstrap.LocationPicker, {
29907 OverlayView : function(map, options)
29909 options = options || {};
29916 * @class Roo.bootstrap.Alert
29917 * @extends Roo.bootstrap.Component
29918 * Bootstrap Alert class - shows an alert area box
29920 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29921 Enter a valid email address
29924 * @cfg {String} title The title of alert
29925 * @cfg {String} html The content of alert
29926 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29927 * @cfg {String} fa font-awesomeicon
29928 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29929 * @cfg {Boolean} close true to show a x closer
29933 * Create a new alert
29934 * @param {Object} config The config object
29938 Roo.bootstrap.Alert = function(config){
29939 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29943 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29949 faicon: false, // BC
29953 getAutoCreate : function()
29965 style : this.close ? '' : 'display:none'
29969 cls : 'roo-alert-icon'
29974 cls : 'roo-alert-title',
29979 cls : 'roo-alert-text',
29986 cfg.cn[0].cls += ' fa ' + this.faicon;
29989 cfg.cn[0].cls += ' fa ' + this.fa;
29993 cfg.cls += ' alert-' + this.weight;
29999 initEvents: function()
30001 this.el.setVisibilityMode(Roo.Element.DISPLAY);
30002 this.titleEl = this.el.select('.roo-alert-title',true).first();
30003 this.iconEl = this.el.select('.roo-alert-icon',true).first();
30004 this.htmlEl = this.el.select('.roo-alert-text',true).first();
30005 if (this.seconds > 0) {
30006 this.hide.defer(this.seconds, this);
30010 * Set the Title Message HTML
30011 * @param {String} html
30013 setTitle : function(str)
30015 this.titleEl.dom.innerHTML = str;
30019 * Set the Body Message HTML
30020 * @param {String} html
30022 setHtml : function(str)
30024 this.htmlEl.dom.innerHTML = str;
30027 * Set the Weight of the alert
30028 * @param {String} (success|info|warning|danger) weight
30031 setWeight : function(weight)
30034 this.el.removeClass('alert-' + this.weight);
30037 this.weight = weight;
30039 this.el.addClass('alert-' + this.weight);
30042 * Set the Icon of the alert
30043 * @param {String} see fontawsome names (name without the 'fa-' bit)
30045 setIcon : function(icon)
30048 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30051 this.faicon = icon;
30053 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30078 * @class Roo.bootstrap.UploadCropbox
30079 * @extends Roo.bootstrap.Component
30080 * Bootstrap UploadCropbox class
30081 * @cfg {String} emptyText show when image has been loaded
30082 * @cfg {String} rotateNotify show when image too small to rotate
30083 * @cfg {Number} errorTimeout default 3000
30084 * @cfg {Number} minWidth default 300
30085 * @cfg {Number} minHeight default 300
30086 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30087 * @cfg {Boolean} isDocument (true|false) default false
30088 * @cfg {String} url action url
30089 * @cfg {String} paramName default 'imageUpload'
30090 * @cfg {String} method default POST
30091 * @cfg {Boolean} loadMask (true|false) default true
30092 * @cfg {Boolean} loadingText default 'Loading...'
30095 * Create a new UploadCropbox
30096 * @param {Object} config The config object
30099 Roo.bootstrap.UploadCropbox = function(config){
30100 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30104 * @event beforeselectfile
30105 * Fire before select file
30106 * @param {Roo.bootstrap.UploadCropbox} this
30108 "beforeselectfile" : true,
30111 * Fire after initEvent
30112 * @param {Roo.bootstrap.UploadCropbox} this
30117 * Fire after initEvent
30118 * @param {Roo.bootstrap.UploadCropbox} this
30119 * @param {String} data
30124 * Fire when preparing the file data
30125 * @param {Roo.bootstrap.UploadCropbox} this
30126 * @param {Object} file
30131 * Fire when get exception
30132 * @param {Roo.bootstrap.UploadCropbox} this
30133 * @param {XMLHttpRequest} xhr
30135 "exception" : true,
30137 * @event beforeloadcanvas
30138 * Fire before load the canvas
30139 * @param {Roo.bootstrap.UploadCropbox} this
30140 * @param {String} src
30142 "beforeloadcanvas" : true,
30145 * Fire when trash image
30146 * @param {Roo.bootstrap.UploadCropbox} this
30151 * Fire when download the image
30152 * @param {Roo.bootstrap.UploadCropbox} this
30156 * @event footerbuttonclick
30157 * Fire when footerbuttonclick
30158 * @param {Roo.bootstrap.UploadCropbox} this
30159 * @param {String} type
30161 "footerbuttonclick" : true,
30165 * @param {Roo.bootstrap.UploadCropbox} this
30170 * Fire when rotate the image
30171 * @param {Roo.bootstrap.UploadCropbox} this
30172 * @param {String} pos
30177 * Fire when inspect the file
30178 * @param {Roo.bootstrap.UploadCropbox} this
30179 * @param {Object} file
30184 * Fire when xhr upload the file
30185 * @param {Roo.bootstrap.UploadCropbox} this
30186 * @param {Object} data
30191 * Fire when arrange the file data
30192 * @param {Roo.bootstrap.UploadCropbox} this
30193 * @param {Object} formData
30198 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30201 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30203 emptyText : 'Click to upload image',
30204 rotateNotify : 'Image is too small to rotate',
30205 errorTimeout : 3000,
30219 cropType : 'image/jpeg',
30221 canvasLoaded : false,
30222 isDocument : false,
30224 paramName : 'imageUpload',
30226 loadingText : 'Loading...',
30229 getAutoCreate : function()
30233 cls : 'roo-upload-cropbox',
30237 cls : 'roo-upload-cropbox-selector',
30242 cls : 'roo-upload-cropbox-body',
30243 style : 'cursor:pointer',
30247 cls : 'roo-upload-cropbox-preview'
30251 cls : 'roo-upload-cropbox-thumb'
30255 cls : 'roo-upload-cropbox-empty-notify',
30256 html : this.emptyText
30260 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30261 html : this.rotateNotify
30267 cls : 'roo-upload-cropbox-footer',
30270 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30280 onRender : function(ct, position)
30282 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30284 if (this.buttons.length) {
30286 Roo.each(this.buttons, function(bb) {
30288 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30290 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30296 this.maskEl = this.el;
30300 initEvents : function()
30302 this.urlAPI = (window.createObjectURL && window) ||
30303 (window.URL && URL.revokeObjectURL && URL) ||
30304 (window.webkitURL && webkitURL);
30306 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30307 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30309 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30310 this.selectorEl.hide();
30312 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30313 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30315 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30316 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30317 this.thumbEl.hide();
30319 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30320 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30322 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30323 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30324 this.errorEl.hide();
30326 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30327 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30328 this.footerEl.hide();
30330 this.setThumbBoxSize();
30336 this.fireEvent('initial', this);
30343 window.addEventListener("resize", function() { _this.resize(); } );
30345 this.bodyEl.on('click', this.beforeSelectFile, this);
30348 this.bodyEl.on('touchstart', this.onTouchStart, this);
30349 this.bodyEl.on('touchmove', this.onTouchMove, this);
30350 this.bodyEl.on('touchend', this.onTouchEnd, this);
30354 this.bodyEl.on('mousedown', this.onMouseDown, this);
30355 this.bodyEl.on('mousemove', this.onMouseMove, this);
30356 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30357 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30358 Roo.get(document).on('mouseup', this.onMouseUp, this);
30361 this.selectorEl.on('change', this.onFileSelected, this);
30367 this.baseScale = 1;
30369 this.baseRotate = 1;
30370 this.dragable = false;
30371 this.pinching = false;
30374 this.cropData = false;
30375 this.notifyEl.dom.innerHTML = this.emptyText;
30377 this.selectorEl.dom.value = '';
30381 resize : function()
30383 if(this.fireEvent('resize', this) != false){
30384 this.setThumbBoxPosition();
30385 this.setCanvasPosition();
30389 onFooterButtonClick : function(e, el, o, type)
30392 case 'rotate-left' :
30393 this.onRotateLeft(e);
30395 case 'rotate-right' :
30396 this.onRotateRight(e);
30399 this.beforeSelectFile(e);
30414 this.fireEvent('footerbuttonclick', this, type);
30417 beforeSelectFile : function(e)
30419 e.preventDefault();
30421 if(this.fireEvent('beforeselectfile', this) != false){
30422 this.selectorEl.dom.click();
30426 onFileSelected : function(e)
30428 e.preventDefault();
30430 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30434 var file = this.selectorEl.dom.files[0];
30436 if(this.fireEvent('inspect', this, file) != false){
30437 this.prepare(file);
30442 trash : function(e)
30444 this.fireEvent('trash', this);
30447 download : function(e)
30449 this.fireEvent('download', this);
30452 loadCanvas : function(src)
30454 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30458 this.imageEl = document.createElement('img');
30462 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30464 this.imageEl.src = src;
30468 onLoadCanvas : function()
30470 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30471 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30473 this.bodyEl.un('click', this.beforeSelectFile, this);
30475 this.notifyEl.hide();
30476 this.thumbEl.show();
30477 this.footerEl.show();
30479 this.baseRotateLevel();
30481 if(this.isDocument){
30482 this.setThumbBoxSize();
30485 this.setThumbBoxPosition();
30487 this.baseScaleLevel();
30493 this.canvasLoaded = true;
30496 this.maskEl.unmask();
30501 setCanvasPosition : function()
30503 if(!this.canvasEl){
30507 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30508 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30510 this.previewEl.setLeft(pw);
30511 this.previewEl.setTop(ph);
30515 onMouseDown : function(e)
30519 this.dragable = true;
30520 this.pinching = false;
30522 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30523 this.dragable = false;
30527 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30528 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30532 onMouseMove : function(e)
30536 if(!this.canvasLoaded){
30540 if (!this.dragable){
30544 var minX = Math.ceil(this.thumbEl.getLeft(true));
30545 var minY = Math.ceil(this.thumbEl.getTop(true));
30547 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30548 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30550 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30551 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30553 x = x - this.mouseX;
30554 y = y - this.mouseY;
30556 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30557 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30559 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30560 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30562 this.previewEl.setLeft(bgX);
30563 this.previewEl.setTop(bgY);
30565 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30566 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30569 onMouseUp : function(e)
30573 this.dragable = false;
30576 onMouseWheel : function(e)
30580 this.startScale = this.scale;
30582 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30584 if(!this.zoomable()){
30585 this.scale = this.startScale;
30594 zoomable : function()
30596 var minScale = this.thumbEl.getWidth() / this.minWidth;
30598 if(this.minWidth < this.minHeight){
30599 minScale = this.thumbEl.getHeight() / this.minHeight;
30602 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30603 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30607 (this.rotate == 0 || this.rotate == 180) &&
30609 width > this.imageEl.OriginWidth ||
30610 height > this.imageEl.OriginHeight ||
30611 (width < this.minWidth && height < this.minHeight)
30619 (this.rotate == 90 || this.rotate == 270) &&
30621 width > this.imageEl.OriginWidth ||
30622 height > this.imageEl.OriginHeight ||
30623 (width < this.minHeight && height < this.minWidth)
30630 !this.isDocument &&
30631 (this.rotate == 0 || this.rotate == 180) &&
30633 width < this.minWidth ||
30634 width > this.imageEl.OriginWidth ||
30635 height < this.minHeight ||
30636 height > this.imageEl.OriginHeight
30643 !this.isDocument &&
30644 (this.rotate == 90 || this.rotate == 270) &&
30646 width < this.minHeight ||
30647 width > this.imageEl.OriginWidth ||
30648 height < this.minWidth ||
30649 height > this.imageEl.OriginHeight
30659 onRotateLeft : function(e)
30661 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30663 var minScale = this.thumbEl.getWidth() / this.minWidth;
30665 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30666 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30668 this.startScale = this.scale;
30670 while (this.getScaleLevel() < minScale){
30672 this.scale = this.scale + 1;
30674 if(!this.zoomable()){
30679 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30680 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30685 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30692 this.scale = this.startScale;
30694 this.onRotateFail();
30699 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30701 if(this.isDocument){
30702 this.setThumbBoxSize();
30703 this.setThumbBoxPosition();
30704 this.setCanvasPosition();
30709 this.fireEvent('rotate', this, 'left');
30713 onRotateRight : function(e)
30715 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30717 var minScale = this.thumbEl.getWidth() / this.minWidth;
30719 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30720 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30722 this.startScale = this.scale;
30724 while (this.getScaleLevel() < minScale){
30726 this.scale = this.scale + 1;
30728 if(!this.zoomable()){
30733 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30734 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30739 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30746 this.scale = this.startScale;
30748 this.onRotateFail();
30753 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30755 if(this.isDocument){
30756 this.setThumbBoxSize();
30757 this.setThumbBoxPosition();
30758 this.setCanvasPosition();
30763 this.fireEvent('rotate', this, 'right');
30766 onRotateFail : function()
30768 this.errorEl.show(true);
30772 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30777 this.previewEl.dom.innerHTML = '';
30779 var canvasEl = document.createElement("canvas");
30781 var contextEl = canvasEl.getContext("2d");
30783 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30784 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30785 var center = this.imageEl.OriginWidth / 2;
30787 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30788 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30789 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30790 center = this.imageEl.OriginHeight / 2;
30793 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30795 contextEl.translate(center, center);
30796 contextEl.rotate(this.rotate * Math.PI / 180);
30798 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30800 this.canvasEl = document.createElement("canvas");
30802 this.contextEl = this.canvasEl.getContext("2d");
30804 switch (this.rotate) {
30807 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30808 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30810 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30815 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30816 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30818 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30819 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);
30823 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30828 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30829 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30831 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30832 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);
30836 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);
30841 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30842 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30844 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30845 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30849 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);
30856 this.previewEl.appendChild(this.canvasEl);
30858 this.setCanvasPosition();
30863 if(!this.canvasLoaded){
30867 var imageCanvas = document.createElement("canvas");
30869 var imageContext = imageCanvas.getContext("2d");
30871 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30872 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30874 var center = imageCanvas.width / 2;
30876 imageContext.translate(center, center);
30878 imageContext.rotate(this.rotate * Math.PI / 180);
30880 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30882 var canvas = document.createElement("canvas");
30884 var context = canvas.getContext("2d");
30886 canvas.width = this.minWidth;
30887 canvas.height = this.minHeight;
30889 switch (this.rotate) {
30892 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30893 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30895 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30896 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30898 var targetWidth = this.minWidth - 2 * x;
30899 var targetHeight = this.minHeight - 2 * y;
30903 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30904 scale = targetWidth / width;
30907 if(x > 0 && y == 0){
30908 scale = targetHeight / height;
30911 if(x > 0 && y > 0){
30912 scale = targetWidth / width;
30914 if(width < height){
30915 scale = targetHeight / height;
30919 context.scale(scale, scale);
30921 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30922 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30924 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30925 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30927 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30932 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30933 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30935 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30936 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30938 var targetWidth = this.minWidth - 2 * x;
30939 var targetHeight = this.minHeight - 2 * y;
30943 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30944 scale = targetWidth / width;
30947 if(x > 0 && y == 0){
30948 scale = targetHeight / height;
30951 if(x > 0 && y > 0){
30952 scale = targetWidth / width;
30954 if(width < height){
30955 scale = targetHeight / height;
30959 context.scale(scale, scale);
30961 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30962 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30964 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30965 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30967 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30969 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30974 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30975 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30977 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30978 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30980 var targetWidth = this.minWidth - 2 * x;
30981 var targetHeight = this.minHeight - 2 * y;
30985 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30986 scale = targetWidth / width;
30989 if(x > 0 && y == 0){
30990 scale = targetHeight / height;
30993 if(x > 0 && y > 0){
30994 scale = targetWidth / width;
30996 if(width < height){
30997 scale = targetHeight / height;
31001 context.scale(scale, scale);
31003 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31004 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31006 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31007 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31009 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31010 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31012 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31017 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31018 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31020 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31021 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31023 var targetWidth = this.minWidth - 2 * x;
31024 var targetHeight = this.minHeight - 2 * y;
31028 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31029 scale = targetWidth / width;
31032 if(x > 0 && y == 0){
31033 scale = targetHeight / height;
31036 if(x > 0 && y > 0){
31037 scale = targetWidth / width;
31039 if(width < height){
31040 scale = targetHeight / height;
31044 context.scale(scale, scale);
31046 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31047 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31049 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31050 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31052 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31054 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31061 this.cropData = canvas.toDataURL(this.cropType);
31063 if(this.fireEvent('crop', this, this.cropData) !== false){
31064 this.process(this.file, this.cropData);
31071 setThumbBoxSize : function()
31075 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31076 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31077 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31079 this.minWidth = width;
31080 this.minHeight = height;
31082 if(this.rotate == 90 || this.rotate == 270){
31083 this.minWidth = height;
31084 this.minHeight = width;
31089 width = Math.ceil(this.minWidth * height / this.minHeight);
31091 if(this.minWidth > this.minHeight){
31093 height = Math.ceil(this.minHeight * width / this.minWidth);
31096 this.thumbEl.setStyle({
31097 width : width + 'px',
31098 height : height + 'px'
31105 setThumbBoxPosition : function()
31107 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31108 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31110 this.thumbEl.setLeft(x);
31111 this.thumbEl.setTop(y);
31115 baseRotateLevel : function()
31117 this.baseRotate = 1;
31120 typeof(this.exif) != 'undefined' &&
31121 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31122 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31124 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31127 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31131 baseScaleLevel : function()
31135 if(this.isDocument){
31137 if(this.baseRotate == 6 || this.baseRotate == 8){
31139 height = this.thumbEl.getHeight();
31140 this.baseScale = height / this.imageEl.OriginWidth;
31142 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31143 width = this.thumbEl.getWidth();
31144 this.baseScale = width / this.imageEl.OriginHeight;
31150 height = this.thumbEl.getHeight();
31151 this.baseScale = height / this.imageEl.OriginHeight;
31153 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31154 width = this.thumbEl.getWidth();
31155 this.baseScale = width / this.imageEl.OriginWidth;
31161 if(this.baseRotate == 6 || this.baseRotate == 8){
31163 width = this.thumbEl.getHeight();
31164 this.baseScale = width / this.imageEl.OriginHeight;
31166 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31167 height = this.thumbEl.getWidth();
31168 this.baseScale = height / this.imageEl.OriginHeight;
31171 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31172 height = this.thumbEl.getWidth();
31173 this.baseScale = height / this.imageEl.OriginHeight;
31175 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31176 width = this.thumbEl.getHeight();
31177 this.baseScale = width / this.imageEl.OriginWidth;
31184 width = this.thumbEl.getWidth();
31185 this.baseScale = width / this.imageEl.OriginWidth;
31187 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31188 height = this.thumbEl.getHeight();
31189 this.baseScale = height / this.imageEl.OriginHeight;
31192 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31194 height = this.thumbEl.getHeight();
31195 this.baseScale = height / this.imageEl.OriginHeight;
31197 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31198 width = this.thumbEl.getWidth();
31199 this.baseScale = width / this.imageEl.OriginWidth;
31207 getScaleLevel : function()
31209 return this.baseScale * Math.pow(1.1, this.scale);
31212 onTouchStart : function(e)
31214 if(!this.canvasLoaded){
31215 this.beforeSelectFile(e);
31219 var touches = e.browserEvent.touches;
31225 if(touches.length == 1){
31226 this.onMouseDown(e);
31230 if(touches.length != 2){
31236 for(var i = 0, finger; finger = touches[i]; i++){
31237 coords.push(finger.pageX, finger.pageY);
31240 var x = Math.pow(coords[0] - coords[2], 2);
31241 var y = Math.pow(coords[1] - coords[3], 2);
31243 this.startDistance = Math.sqrt(x + y);
31245 this.startScale = this.scale;
31247 this.pinching = true;
31248 this.dragable = false;
31252 onTouchMove : function(e)
31254 if(!this.pinching && !this.dragable){
31258 var touches = e.browserEvent.touches;
31265 this.onMouseMove(e);
31271 for(var i = 0, finger; finger = touches[i]; i++){
31272 coords.push(finger.pageX, finger.pageY);
31275 var x = Math.pow(coords[0] - coords[2], 2);
31276 var y = Math.pow(coords[1] - coords[3], 2);
31278 this.endDistance = Math.sqrt(x + y);
31280 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31282 if(!this.zoomable()){
31283 this.scale = this.startScale;
31291 onTouchEnd : function(e)
31293 this.pinching = false;
31294 this.dragable = false;
31298 process : function(file, crop)
31301 this.maskEl.mask(this.loadingText);
31304 this.xhr = new XMLHttpRequest();
31306 file.xhr = this.xhr;
31308 this.xhr.open(this.method, this.url, true);
31311 "Accept": "application/json",
31312 "Cache-Control": "no-cache",
31313 "X-Requested-With": "XMLHttpRequest"
31316 for (var headerName in headers) {
31317 var headerValue = headers[headerName];
31319 this.xhr.setRequestHeader(headerName, headerValue);
31325 this.xhr.onload = function()
31327 _this.xhrOnLoad(_this.xhr);
31330 this.xhr.onerror = function()
31332 _this.xhrOnError(_this.xhr);
31335 var formData = new FormData();
31337 formData.append('returnHTML', 'NO');
31340 formData.append('crop', crop);
31343 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31344 formData.append(this.paramName, file, file.name);
31347 if(typeof(file.filename) != 'undefined'){
31348 formData.append('filename', file.filename);
31351 if(typeof(file.mimetype) != 'undefined'){
31352 formData.append('mimetype', file.mimetype);
31355 if(this.fireEvent('arrange', this, formData) != false){
31356 this.xhr.send(formData);
31360 xhrOnLoad : function(xhr)
31363 this.maskEl.unmask();
31366 if (xhr.readyState !== 4) {
31367 this.fireEvent('exception', this, xhr);
31371 var response = Roo.decode(xhr.responseText);
31373 if(!response.success){
31374 this.fireEvent('exception', this, xhr);
31378 var response = Roo.decode(xhr.responseText);
31380 this.fireEvent('upload', this, response);
31384 xhrOnError : function()
31387 this.maskEl.unmask();
31390 Roo.log('xhr on error');
31392 var response = Roo.decode(xhr.responseText);
31398 prepare : function(file)
31401 this.maskEl.mask(this.loadingText);
31407 if(typeof(file) === 'string'){
31408 this.loadCanvas(file);
31412 if(!file || !this.urlAPI){
31417 this.cropType = file.type;
31421 if(this.fireEvent('prepare', this, this.file) != false){
31423 var reader = new FileReader();
31425 reader.onload = function (e) {
31426 if (e.target.error) {
31427 Roo.log(e.target.error);
31431 var buffer = e.target.result,
31432 dataView = new DataView(buffer),
31434 maxOffset = dataView.byteLength - 4,
31438 if (dataView.getUint16(0) === 0xffd8) {
31439 while (offset < maxOffset) {
31440 markerBytes = dataView.getUint16(offset);
31442 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31443 markerLength = dataView.getUint16(offset + 2) + 2;
31444 if (offset + markerLength > dataView.byteLength) {
31445 Roo.log('Invalid meta data: Invalid segment size.');
31449 if(markerBytes == 0xffe1){
31450 _this.parseExifData(
31457 offset += markerLength;
31467 var url = _this.urlAPI.createObjectURL(_this.file);
31469 _this.loadCanvas(url);
31474 reader.readAsArrayBuffer(this.file);
31480 parseExifData : function(dataView, offset, length)
31482 var tiffOffset = offset + 10,
31486 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31487 // No Exif data, might be XMP data instead
31491 // Check for the ASCII code for "Exif" (0x45786966):
31492 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31493 // No Exif data, might be XMP data instead
31496 if (tiffOffset + 8 > dataView.byteLength) {
31497 Roo.log('Invalid Exif data: Invalid segment size.');
31500 // Check for the two null bytes:
31501 if (dataView.getUint16(offset + 8) !== 0x0000) {
31502 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31505 // Check the byte alignment:
31506 switch (dataView.getUint16(tiffOffset)) {
31508 littleEndian = true;
31511 littleEndian = false;
31514 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31517 // Check for the TIFF tag marker (0x002A):
31518 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31519 Roo.log('Invalid Exif data: Missing TIFF marker.');
31522 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31523 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31525 this.parseExifTags(
31528 tiffOffset + dirOffset,
31533 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31538 if (dirOffset + 6 > dataView.byteLength) {
31539 Roo.log('Invalid Exif data: Invalid directory offset.');
31542 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31543 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31544 if (dirEndOffset + 4 > dataView.byteLength) {
31545 Roo.log('Invalid Exif data: Invalid directory size.');
31548 for (i = 0; i < tagsNumber; i += 1) {
31552 dirOffset + 2 + 12 * i, // tag offset
31556 // Return the offset to the next directory:
31557 return dataView.getUint32(dirEndOffset, littleEndian);
31560 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31562 var tag = dataView.getUint16(offset, littleEndian);
31564 this.exif[tag] = this.getExifValue(
31568 dataView.getUint16(offset + 2, littleEndian), // tag type
31569 dataView.getUint32(offset + 4, littleEndian), // tag length
31574 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31576 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31585 Roo.log('Invalid Exif data: Invalid tag type.');
31589 tagSize = tagType.size * length;
31590 // Determine if the value is contained in the dataOffset bytes,
31591 // or if the value at the dataOffset is a pointer to the actual data:
31592 dataOffset = tagSize > 4 ?
31593 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31594 if (dataOffset + tagSize > dataView.byteLength) {
31595 Roo.log('Invalid Exif data: Invalid data offset.');
31598 if (length === 1) {
31599 return tagType.getValue(dataView, dataOffset, littleEndian);
31602 for (i = 0; i < length; i += 1) {
31603 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31606 if (tagType.ascii) {
31608 // Concatenate the chars:
31609 for (i = 0; i < values.length; i += 1) {
31611 // Ignore the terminating NULL byte(s):
31612 if (c === '\u0000') {
31624 Roo.apply(Roo.bootstrap.UploadCropbox, {
31626 'Orientation': 0x0112
31630 1: 0, //'top-left',
31632 3: 180, //'bottom-right',
31633 // 4: 'bottom-left',
31635 6: 90, //'right-top',
31636 // 7: 'right-bottom',
31637 8: 270 //'left-bottom'
31641 // byte, 8-bit unsigned int:
31643 getValue: function (dataView, dataOffset) {
31644 return dataView.getUint8(dataOffset);
31648 // ascii, 8-bit byte:
31650 getValue: function (dataView, dataOffset) {
31651 return String.fromCharCode(dataView.getUint8(dataOffset));
31656 // short, 16 bit int:
31658 getValue: function (dataView, dataOffset, littleEndian) {
31659 return dataView.getUint16(dataOffset, littleEndian);
31663 // long, 32 bit int:
31665 getValue: function (dataView, dataOffset, littleEndian) {
31666 return dataView.getUint32(dataOffset, littleEndian);
31670 // rational = two long values, first is numerator, second is denominator:
31672 getValue: function (dataView, dataOffset, littleEndian) {
31673 return dataView.getUint32(dataOffset, littleEndian) /
31674 dataView.getUint32(dataOffset + 4, littleEndian);
31678 // slong, 32 bit signed int:
31680 getValue: function (dataView, dataOffset, littleEndian) {
31681 return dataView.getInt32(dataOffset, littleEndian);
31685 // srational, two slongs, first is numerator, second is denominator:
31687 getValue: function (dataView, dataOffset, littleEndian) {
31688 return dataView.getInt32(dataOffset, littleEndian) /
31689 dataView.getInt32(dataOffset + 4, littleEndian);
31699 cls : 'btn-group roo-upload-cropbox-rotate-left',
31700 action : 'rotate-left',
31704 cls : 'btn btn-default',
31705 html : '<i class="fa fa-undo"></i>'
31711 cls : 'btn-group roo-upload-cropbox-picture',
31712 action : 'picture',
31716 cls : 'btn btn-default',
31717 html : '<i class="fa fa-picture-o"></i>'
31723 cls : 'btn-group roo-upload-cropbox-rotate-right',
31724 action : 'rotate-right',
31728 cls : 'btn btn-default',
31729 html : '<i class="fa fa-repeat"></i>'
31737 cls : 'btn-group roo-upload-cropbox-rotate-left',
31738 action : 'rotate-left',
31742 cls : 'btn btn-default',
31743 html : '<i class="fa fa-undo"></i>'
31749 cls : 'btn-group roo-upload-cropbox-download',
31750 action : 'download',
31754 cls : 'btn btn-default',
31755 html : '<i class="fa fa-download"></i>'
31761 cls : 'btn-group roo-upload-cropbox-crop',
31766 cls : 'btn btn-default',
31767 html : '<i class="fa fa-crop"></i>'
31773 cls : 'btn-group roo-upload-cropbox-trash',
31778 cls : 'btn btn-default',
31779 html : '<i class="fa fa-trash"></i>'
31785 cls : 'btn-group roo-upload-cropbox-rotate-right',
31786 action : 'rotate-right',
31790 cls : 'btn btn-default',
31791 html : '<i class="fa fa-repeat"></i>'
31799 cls : 'btn-group roo-upload-cropbox-rotate-left',
31800 action : 'rotate-left',
31804 cls : 'btn btn-default',
31805 html : '<i class="fa fa-undo"></i>'
31811 cls : 'btn-group roo-upload-cropbox-rotate-right',
31812 action : 'rotate-right',
31816 cls : 'btn btn-default',
31817 html : '<i class="fa fa-repeat"></i>'
31830 * @class Roo.bootstrap.DocumentManager
31831 * @extends Roo.bootstrap.Component
31832 * Bootstrap DocumentManager class
31833 * @cfg {String} paramName default 'imageUpload'
31834 * @cfg {String} toolTipName default 'filename'
31835 * @cfg {String} method default POST
31836 * @cfg {String} url action url
31837 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31838 * @cfg {Boolean} multiple multiple upload default true
31839 * @cfg {Number} thumbSize default 300
31840 * @cfg {String} fieldLabel
31841 * @cfg {Number} labelWidth default 4
31842 * @cfg {String} labelAlign (left|top) default left
31843 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31844 * @cfg {Number} labellg set the width of label (1-12)
31845 * @cfg {Number} labelmd set the width of label (1-12)
31846 * @cfg {Number} labelsm set the width of label (1-12)
31847 * @cfg {Number} labelxs set the width of label (1-12)
31850 * Create a new DocumentManager
31851 * @param {Object} config The config object
31854 Roo.bootstrap.DocumentManager = function(config){
31855 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31858 this.delegates = [];
31863 * Fire when initial the DocumentManager
31864 * @param {Roo.bootstrap.DocumentManager} this
31869 * inspect selected file
31870 * @param {Roo.bootstrap.DocumentManager} this
31871 * @param {File} file
31876 * Fire when xhr load exception
31877 * @param {Roo.bootstrap.DocumentManager} this
31878 * @param {XMLHttpRequest} xhr
31880 "exception" : true,
31882 * @event afterupload
31883 * Fire when xhr load exception
31884 * @param {Roo.bootstrap.DocumentManager} this
31885 * @param {XMLHttpRequest} xhr
31887 "afterupload" : true,
31890 * prepare the form data
31891 * @param {Roo.bootstrap.DocumentManager} this
31892 * @param {Object} formData
31897 * Fire when remove the file
31898 * @param {Roo.bootstrap.DocumentManager} this
31899 * @param {Object} file
31904 * Fire after refresh the file
31905 * @param {Roo.bootstrap.DocumentManager} this
31910 * Fire after click the image
31911 * @param {Roo.bootstrap.DocumentManager} this
31912 * @param {Object} file
31917 * Fire when upload a image and editable set to true
31918 * @param {Roo.bootstrap.DocumentManager} this
31919 * @param {Object} file
31923 * @event beforeselectfile
31924 * Fire before select file
31925 * @param {Roo.bootstrap.DocumentManager} this
31927 "beforeselectfile" : true,
31930 * Fire before process file
31931 * @param {Roo.bootstrap.DocumentManager} this
31932 * @param {Object} file
31936 * @event previewrendered
31937 * Fire when preview rendered
31938 * @param {Roo.bootstrap.DocumentManager} this
31939 * @param {Object} file
31941 "previewrendered" : true,
31944 "previewResize" : true
31949 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31958 paramName : 'imageUpload',
31959 toolTipName : 'filename',
31962 labelAlign : 'left',
31972 getAutoCreate : function()
31974 var managerWidget = {
31976 cls : 'roo-document-manager',
31980 cls : 'roo-document-manager-selector',
31985 cls : 'roo-document-manager-uploader',
31989 cls : 'roo-document-manager-upload-btn',
31990 html : '<i class="fa fa-plus"></i>'
32001 cls : 'column col-md-12',
32006 if(this.fieldLabel.length){
32011 cls : 'column col-md-12',
32012 html : this.fieldLabel
32016 cls : 'column col-md-12',
32021 if(this.labelAlign == 'left'){
32026 html : this.fieldLabel
32035 if(this.labelWidth > 12){
32036 content[0].style = "width: " + this.labelWidth + 'px';
32039 if(this.labelWidth < 13 && this.labelmd == 0){
32040 this.labelmd = this.labelWidth;
32043 if(this.labellg > 0){
32044 content[0].cls += ' col-lg-' + this.labellg;
32045 content[1].cls += ' col-lg-' + (12 - this.labellg);
32048 if(this.labelmd > 0){
32049 content[0].cls += ' col-md-' + this.labelmd;
32050 content[1].cls += ' col-md-' + (12 - this.labelmd);
32053 if(this.labelsm > 0){
32054 content[0].cls += ' col-sm-' + this.labelsm;
32055 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32058 if(this.labelxs > 0){
32059 content[0].cls += ' col-xs-' + this.labelxs;
32060 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32068 cls : 'row clearfix',
32076 initEvents : function()
32078 this.managerEl = this.el.select('.roo-document-manager', true).first();
32079 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32081 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32082 this.selectorEl.hide();
32085 this.selectorEl.attr('multiple', 'multiple');
32088 this.selectorEl.on('change', this.onFileSelected, this);
32090 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32091 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32093 this.uploader.on('click', this.onUploaderClick, this);
32095 this.renderProgressDialog();
32099 window.addEventListener("resize", function() { _this.refresh(); } );
32101 this.fireEvent('initial', this);
32104 renderProgressDialog : function()
32108 this.progressDialog = new Roo.bootstrap.Modal({
32109 cls : 'roo-document-manager-progress-dialog',
32110 allow_close : false,
32121 btnclick : function() {
32122 _this.uploadCancel();
32128 this.progressDialog.render(Roo.get(document.body));
32130 this.progress = new Roo.bootstrap.Progress({
32131 cls : 'roo-document-manager-progress',
32136 this.progress.render(this.progressDialog.getChildContainer());
32138 this.progressBar = new Roo.bootstrap.ProgressBar({
32139 cls : 'roo-document-manager-progress-bar',
32142 aria_valuemax : 12,
32146 this.progressBar.render(this.progress.getChildContainer());
32149 onUploaderClick : function(e)
32151 e.preventDefault();
32153 if(this.fireEvent('beforeselectfile', this) != false){
32154 this.selectorEl.dom.click();
32159 onFileSelected : function(e)
32161 e.preventDefault();
32163 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32167 Roo.each(this.selectorEl.dom.files, function(file){
32168 if(this.fireEvent('inspect', this, file) != false){
32169 this.files.push(file);
32179 this.selectorEl.dom.value = '';
32181 if(!this.files || !this.files.length){
32185 if(this.boxes > 0 && this.files.length > this.boxes){
32186 this.files = this.files.slice(0, this.boxes);
32189 this.uploader.show();
32191 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32192 this.uploader.hide();
32201 Roo.each(this.files, function(file){
32203 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32204 var f = this.renderPreview(file);
32209 if(file.type.indexOf('image') != -1){
32210 this.delegates.push(
32212 _this.process(file);
32213 }).createDelegate(this)
32221 _this.process(file);
32222 }).createDelegate(this)
32227 this.files = files;
32229 this.delegates = this.delegates.concat(docs);
32231 if(!this.delegates.length){
32236 this.progressBar.aria_valuemax = this.delegates.length;
32243 arrange : function()
32245 if(!this.delegates.length){
32246 this.progressDialog.hide();
32251 var delegate = this.delegates.shift();
32253 this.progressDialog.show();
32255 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32257 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32262 refresh : function()
32264 this.uploader.show();
32266 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32267 this.uploader.hide();
32270 Roo.isTouch ? this.closable(false) : this.closable(true);
32272 this.fireEvent('refresh', this);
32275 onRemove : function(e, el, o)
32277 e.preventDefault();
32279 this.fireEvent('remove', this, o);
32283 remove : function(o)
32287 Roo.each(this.files, function(file){
32288 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32297 this.files = files;
32304 Roo.each(this.files, function(file){
32309 file.target.remove();
32318 onClick : function(e, el, o)
32320 e.preventDefault();
32322 this.fireEvent('click', this, o);
32326 closable : function(closable)
32328 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32330 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32342 xhrOnLoad : function(xhr)
32344 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32348 if (xhr.readyState !== 4) {
32350 this.fireEvent('exception', this, xhr);
32354 var response = Roo.decode(xhr.responseText);
32356 if(!response.success){
32358 this.fireEvent('exception', this, xhr);
32362 var file = this.renderPreview(response.data);
32364 this.files.push(file);
32368 this.fireEvent('afterupload', this, xhr);
32372 xhrOnError : function(xhr)
32374 Roo.log('xhr on error');
32376 var response = Roo.decode(xhr.responseText);
32383 process : function(file)
32385 if(this.fireEvent('process', this, file) !== false){
32386 if(this.editable && file.type.indexOf('image') != -1){
32387 this.fireEvent('edit', this, file);
32391 this.uploadStart(file, false);
32398 uploadStart : function(file, crop)
32400 this.xhr = new XMLHttpRequest();
32402 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32407 file.xhr = this.xhr;
32409 this.managerEl.createChild({
32411 cls : 'roo-document-manager-loading',
32415 tooltip : file.name,
32416 cls : 'roo-document-manager-thumb',
32417 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32423 this.xhr.open(this.method, this.url, true);
32426 "Accept": "application/json",
32427 "Cache-Control": "no-cache",
32428 "X-Requested-With": "XMLHttpRequest"
32431 for (var headerName in headers) {
32432 var headerValue = headers[headerName];
32434 this.xhr.setRequestHeader(headerName, headerValue);
32440 this.xhr.onload = function()
32442 _this.xhrOnLoad(_this.xhr);
32445 this.xhr.onerror = function()
32447 _this.xhrOnError(_this.xhr);
32450 var formData = new FormData();
32452 formData.append('returnHTML', 'NO');
32455 formData.append('crop', crop);
32458 formData.append(this.paramName, file, file.name);
32465 if(this.fireEvent('prepare', this, formData, options) != false){
32467 if(options.manually){
32471 this.xhr.send(formData);
32475 this.uploadCancel();
32478 uploadCancel : function()
32484 this.delegates = [];
32486 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32493 renderPreview : function(file)
32495 if(typeof(file.target) != 'undefined' && file.target){
32499 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32501 var previewEl = this.managerEl.createChild({
32503 cls : 'roo-document-manager-preview',
32507 tooltip : file[this.toolTipName],
32508 cls : 'roo-document-manager-thumb',
32509 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32514 html : '<i class="fa fa-times-circle"></i>'
32519 var close = previewEl.select('button.close', true).first();
32521 close.on('click', this.onRemove, this, file);
32523 file.target = previewEl;
32525 var image = previewEl.select('img', true).first();
32529 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32531 image.on('click', this.onClick, this, file);
32533 this.fireEvent('previewrendered', this, file);
32539 onPreviewLoad : function(file, image)
32541 if(typeof(file.target) == 'undefined' || !file.target){
32545 var width = image.dom.naturalWidth || image.dom.width;
32546 var height = image.dom.naturalHeight || image.dom.height;
32548 if(!this.previewResize) {
32552 if(width > height){
32553 file.target.addClass('wide');
32557 file.target.addClass('tall');
32562 uploadFromSource : function(file, crop)
32564 this.xhr = new XMLHttpRequest();
32566 this.managerEl.createChild({
32568 cls : 'roo-document-manager-loading',
32572 tooltip : file.name,
32573 cls : 'roo-document-manager-thumb',
32574 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32580 this.xhr.open(this.method, this.url, true);
32583 "Accept": "application/json",
32584 "Cache-Control": "no-cache",
32585 "X-Requested-With": "XMLHttpRequest"
32588 for (var headerName in headers) {
32589 var headerValue = headers[headerName];
32591 this.xhr.setRequestHeader(headerName, headerValue);
32597 this.xhr.onload = function()
32599 _this.xhrOnLoad(_this.xhr);
32602 this.xhr.onerror = function()
32604 _this.xhrOnError(_this.xhr);
32607 var formData = new FormData();
32609 formData.append('returnHTML', 'NO');
32611 formData.append('crop', crop);
32613 if(typeof(file.filename) != 'undefined'){
32614 formData.append('filename', file.filename);
32617 if(typeof(file.mimetype) != 'undefined'){
32618 formData.append('mimetype', file.mimetype);
32623 if(this.fireEvent('prepare', this, formData) != false){
32624 this.xhr.send(formData);
32634 * @class Roo.bootstrap.DocumentViewer
32635 * @extends Roo.bootstrap.Component
32636 * Bootstrap DocumentViewer class
32637 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32638 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32641 * Create a new DocumentViewer
32642 * @param {Object} config The config object
32645 Roo.bootstrap.DocumentViewer = function(config){
32646 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32651 * Fire after initEvent
32652 * @param {Roo.bootstrap.DocumentViewer} this
32658 * @param {Roo.bootstrap.DocumentViewer} this
32663 * Fire after download button
32664 * @param {Roo.bootstrap.DocumentViewer} this
32669 * Fire after trash button
32670 * @param {Roo.bootstrap.DocumentViewer} this
32677 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32679 showDownload : true,
32683 getAutoCreate : function()
32687 cls : 'roo-document-viewer',
32691 cls : 'roo-document-viewer-body',
32695 cls : 'roo-document-viewer-thumb',
32699 cls : 'roo-document-viewer-image'
32707 cls : 'roo-document-viewer-footer',
32710 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32714 cls : 'btn-group roo-document-viewer-download',
32718 cls : 'btn btn-default',
32719 html : '<i class="fa fa-download"></i>'
32725 cls : 'btn-group roo-document-viewer-trash',
32729 cls : 'btn btn-default',
32730 html : '<i class="fa fa-trash"></i>'
32743 initEvents : function()
32745 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32746 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32748 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32749 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32751 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32752 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32754 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32755 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32757 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32758 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32760 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32761 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32763 this.bodyEl.on('click', this.onClick, this);
32764 this.downloadBtn.on('click', this.onDownload, this);
32765 this.trashBtn.on('click', this.onTrash, this);
32767 this.downloadBtn.hide();
32768 this.trashBtn.hide();
32770 if(this.showDownload){
32771 this.downloadBtn.show();
32774 if(this.showTrash){
32775 this.trashBtn.show();
32778 if(!this.showDownload && !this.showTrash) {
32779 this.footerEl.hide();
32784 initial : function()
32786 this.fireEvent('initial', this);
32790 onClick : function(e)
32792 e.preventDefault();
32794 this.fireEvent('click', this);
32797 onDownload : function(e)
32799 e.preventDefault();
32801 this.fireEvent('download', this);
32804 onTrash : function(e)
32806 e.preventDefault();
32808 this.fireEvent('trash', this);
32820 * @class Roo.bootstrap.NavProgressBar
32821 * @extends Roo.bootstrap.Component
32822 * Bootstrap NavProgressBar class
32825 * Create a new nav progress bar
32826 * @param {Object} config The config object
32829 Roo.bootstrap.NavProgressBar = function(config){
32830 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32832 this.bullets = this.bullets || [];
32834 // Roo.bootstrap.NavProgressBar.register(this);
32838 * Fires when the active item changes
32839 * @param {Roo.bootstrap.NavProgressBar} this
32840 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32841 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32848 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32853 getAutoCreate : function()
32855 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32859 cls : 'roo-navigation-bar-group',
32863 cls : 'roo-navigation-top-bar'
32867 cls : 'roo-navigation-bullets-bar',
32871 cls : 'roo-navigation-bar'
32878 cls : 'roo-navigation-bottom-bar'
32888 initEvents: function()
32893 onRender : function(ct, position)
32895 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32897 if(this.bullets.length){
32898 Roo.each(this.bullets, function(b){
32907 addItem : function(cfg)
32909 var item = new Roo.bootstrap.NavProgressItem(cfg);
32911 item.parentId = this.id;
32912 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32915 var top = new Roo.bootstrap.Element({
32917 cls : 'roo-navigation-bar-text'
32920 var bottom = new Roo.bootstrap.Element({
32922 cls : 'roo-navigation-bar-text'
32925 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32926 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32928 var topText = new Roo.bootstrap.Element({
32930 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32933 var bottomText = new Roo.bootstrap.Element({
32935 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32938 topText.onRender(top.el, null);
32939 bottomText.onRender(bottom.el, null);
32942 item.bottomEl = bottom;
32945 this.barItems.push(item);
32950 getActive : function()
32952 var active = false;
32954 Roo.each(this.barItems, function(v){
32956 if (!v.isActive()) {
32968 setActiveItem : function(item)
32972 Roo.each(this.barItems, function(v){
32973 if (v.rid == item.rid) {
32977 if (v.isActive()) {
32978 v.setActive(false);
32983 item.setActive(true);
32985 this.fireEvent('changed', this, item, prev);
32988 getBarItem: function(rid)
32992 Roo.each(this.barItems, function(e) {
32993 if (e.rid != rid) {
33004 indexOfItem : function(item)
33008 Roo.each(this.barItems, function(v, i){
33010 if (v.rid != item.rid) {
33021 setActiveNext : function()
33023 var i = this.indexOfItem(this.getActive());
33025 if (i > this.barItems.length) {
33029 this.setActiveItem(this.barItems[i+1]);
33032 setActivePrev : function()
33034 var i = this.indexOfItem(this.getActive());
33040 this.setActiveItem(this.barItems[i-1]);
33043 format : function()
33045 if(!this.barItems.length){
33049 var width = 100 / this.barItems.length;
33051 Roo.each(this.barItems, function(i){
33052 i.el.setStyle('width', width + '%');
33053 i.topEl.el.setStyle('width', width + '%');
33054 i.bottomEl.el.setStyle('width', width + '%');
33063 * Nav Progress Item
33068 * @class Roo.bootstrap.NavProgressItem
33069 * @extends Roo.bootstrap.Component
33070 * Bootstrap NavProgressItem class
33071 * @cfg {String} rid the reference id
33072 * @cfg {Boolean} active (true|false) Is item active default false
33073 * @cfg {Boolean} disabled (true|false) Is item active default false
33074 * @cfg {String} html
33075 * @cfg {String} position (top|bottom) text position default bottom
33076 * @cfg {String} icon show icon instead of number
33079 * Create a new NavProgressItem
33080 * @param {Object} config The config object
33082 Roo.bootstrap.NavProgressItem = function(config){
33083 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33088 * The raw click event for the entire grid.
33089 * @param {Roo.bootstrap.NavProgressItem} this
33090 * @param {Roo.EventObject} e
33097 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33103 position : 'bottom',
33106 getAutoCreate : function()
33108 var iconCls = 'roo-navigation-bar-item-icon';
33110 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33114 cls: 'roo-navigation-bar-item',
33124 cfg.cls += ' active';
33127 cfg.cls += ' disabled';
33133 disable : function()
33135 this.setDisabled(true);
33138 enable : function()
33140 this.setDisabled(false);
33143 initEvents: function()
33145 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33147 this.iconEl.on('click', this.onClick, this);
33150 onClick : function(e)
33152 e.preventDefault();
33158 if(this.fireEvent('click', this, e) === false){
33162 this.parent().setActiveItem(this);
33165 isActive: function ()
33167 return this.active;
33170 setActive : function(state)
33172 if(this.active == state){
33176 this.active = state;
33179 this.el.addClass('active');
33183 this.el.removeClass('active');
33188 setDisabled : function(state)
33190 if(this.disabled == state){
33194 this.disabled = state;
33197 this.el.addClass('disabled');
33201 this.el.removeClass('disabled');
33204 tooltipEl : function()
33206 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33219 * @class Roo.bootstrap.FieldLabel
33220 * @extends Roo.bootstrap.Component
33221 * Bootstrap FieldLabel class
33222 * @cfg {String} html contents of the element
33223 * @cfg {String} tag tag of the element default label
33224 * @cfg {String} cls class of the element
33225 * @cfg {String} target label target
33226 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33227 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33228 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33229 * @cfg {String} iconTooltip default "This field is required"
33230 * @cfg {String} indicatorpos (left|right) default left
33233 * Create a new FieldLabel
33234 * @param {Object} config The config object
33237 Roo.bootstrap.FieldLabel = function(config){
33238 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33243 * Fires after the field has been marked as invalid.
33244 * @param {Roo.form.FieldLabel} this
33245 * @param {String} msg The validation message
33250 * Fires after the field has been validated with no errors.
33251 * @param {Roo.form.FieldLabel} this
33257 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33264 invalidClass : 'has-warning',
33265 validClass : 'has-success',
33266 iconTooltip : 'This field is required',
33267 indicatorpos : 'left',
33269 getAutoCreate : function(){
33272 if (!this.allowBlank) {
33278 cls : 'roo-bootstrap-field-label ' + this.cls,
33283 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33284 tooltip : this.iconTooltip
33293 if(this.indicatorpos == 'right'){
33296 cls : 'roo-bootstrap-field-label ' + this.cls,
33305 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33306 tooltip : this.iconTooltip
33315 initEvents: function()
33317 Roo.bootstrap.Element.superclass.initEvents.call(this);
33319 this.indicator = this.indicatorEl();
33321 if(this.indicator){
33322 this.indicator.removeClass('visible');
33323 this.indicator.addClass('invisible');
33326 Roo.bootstrap.FieldLabel.register(this);
33329 indicatorEl : function()
33331 var indicator = this.el.select('i.roo-required-indicator',true).first();
33342 * Mark this field as valid
33344 markValid : function()
33346 if(this.indicator){
33347 this.indicator.removeClass('visible');
33348 this.indicator.addClass('invisible');
33350 if (Roo.bootstrap.version == 3) {
33351 this.el.removeClass(this.invalidClass);
33352 this.el.addClass(this.validClass);
33354 this.el.removeClass('is-invalid');
33355 this.el.addClass('is-valid');
33359 this.fireEvent('valid', this);
33363 * Mark this field as invalid
33364 * @param {String} msg The validation message
33366 markInvalid : function(msg)
33368 if(this.indicator){
33369 this.indicator.removeClass('invisible');
33370 this.indicator.addClass('visible');
33372 if (Roo.bootstrap.version == 3) {
33373 this.el.removeClass(this.validClass);
33374 this.el.addClass(this.invalidClass);
33376 this.el.removeClass('is-valid');
33377 this.el.addClass('is-invalid');
33381 this.fireEvent('invalid', this, msg);
33387 Roo.apply(Roo.bootstrap.FieldLabel, {
33392 * register a FieldLabel Group
33393 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33395 register : function(label)
33397 if(this.groups.hasOwnProperty(label.target)){
33401 this.groups[label.target] = label;
33405 * fetch a FieldLabel Group based on the target
33406 * @param {string} target
33407 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33409 get: function(target) {
33410 if (typeof(this.groups[target]) == 'undefined') {
33414 return this.groups[target] ;
33423 * page DateSplitField.
33429 * @class Roo.bootstrap.DateSplitField
33430 * @extends Roo.bootstrap.Component
33431 * Bootstrap DateSplitField class
33432 * @cfg {string} fieldLabel - the label associated
33433 * @cfg {Number} labelWidth set the width of label (0-12)
33434 * @cfg {String} labelAlign (top|left)
33435 * @cfg {Boolean} dayAllowBlank (true|false) default false
33436 * @cfg {Boolean} monthAllowBlank (true|false) default false
33437 * @cfg {Boolean} yearAllowBlank (true|false) default false
33438 * @cfg {string} dayPlaceholder
33439 * @cfg {string} monthPlaceholder
33440 * @cfg {string} yearPlaceholder
33441 * @cfg {string} dayFormat default 'd'
33442 * @cfg {string} monthFormat default 'm'
33443 * @cfg {string} yearFormat default 'Y'
33444 * @cfg {Number} labellg set the width of label (1-12)
33445 * @cfg {Number} labelmd set the width of label (1-12)
33446 * @cfg {Number} labelsm set the width of label (1-12)
33447 * @cfg {Number} labelxs set the width of label (1-12)
33451 * Create a new DateSplitField
33452 * @param {Object} config The config object
33455 Roo.bootstrap.DateSplitField = function(config){
33456 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33462 * getting the data of years
33463 * @param {Roo.bootstrap.DateSplitField} this
33464 * @param {Object} years
33469 * getting the data of days
33470 * @param {Roo.bootstrap.DateSplitField} this
33471 * @param {Object} days
33476 * Fires after the field has been marked as invalid.
33477 * @param {Roo.form.Field} this
33478 * @param {String} msg The validation message
33483 * Fires after the field has been validated with no errors.
33484 * @param {Roo.form.Field} this
33490 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33493 labelAlign : 'top',
33495 dayAllowBlank : false,
33496 monthAllowBlank : false,
33497 yearAllowBlank : false,
33498 dayPlaceholder : '',
33499 monthPlaceholder : '',
33500 yearPlaceholder : '',
33504 isFormField : true,
33510 getAutoCreate : function()
33514 cls : 'row roo-date-split-field-group',
33519 cls : 'form-hidden-field roo-date-split-field-group-value',
33525 var labelCls = 'col-md-12';
33526 var contentCls = 'col-md-4';
33528 if(this.fieldLabel){
33532 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33536 html : this.fieldLabel
33541 if(this.labelAlign == 'left'){
33543 if(this.labelWidth > 12){
33544 label.style = "width: " + this.labelWidth + 'px';
33547 if(this.labelWidth < 13 && this.labelmd == 0){
33548 this.labelmd = this.labelWidth;
33551 if(this.labellg > 0){
33552 labelCls = ' col-lg-' + this.labellg;
33553 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33556 if(this.labelmd > 0){
33557 labelCls = ' col-md-' + this.labelmd;
33558 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33561 if(this.labelsm > 0){
33562 labelCls = ' col-sm-' + this.labelsm;
33563 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33566 if(this.labelxs > 0){
33567 labelCls = ' col-xs-' + this.labelxs;
33568 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33572 label.cls += ' ' + labelCls;
33574 cfg.cn.push(label);
33577 Roo.each(['day', 'month', 'year'], function(t){
33580 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33587 inputEl: function ()
33589 return this.el.select('.roo-date-split-field-group-value', true).first();
33592 onRender : function(ct, position)
33596 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33598 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33600 this.dayField = new Roo.bootstrap.ComboBox({
33601 allowBlank : this.dayAllowBlank,
33602 alwaysQuery : true,
33603 displayField : 'value',
33606 forceSelection : true,
33608 placeholder : this.dayPlaceholder,
33609 selectOnFocus : true,
33610 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33611 triggerAction : 'all',
33613 valueField : 'value',
33614 store : new Roo.data.SimpleStore({
33615 data : (function() {
33617 _this.fireEvent('days', _this, days);
33620 fields : [ 'value' ]
33623 select : function (_self, record, index)
33625 _this.setValue(_this.getValue());
33630 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33632 this.monthField = new Roo.bootstrap.MonthField({
33633 after : '<i class=\"fa fa-calendar\"></i>',
33634 allowBlank : this.monthAllowBlank,
33635 placeholder : this.monthPlaceholder,
33638 render : function (_self)
33640 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33641 e.preventDefault();
33645 select : function (_self, oldvalue, newvalue)
33647 _this.setValue(_this.getValue());
33652 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33654 this.yearField = new Roo.bootstrap.ComboBox({
33655 allowBlank : this.yearAllowBlank,
33656 alwaysQuery : true,
33657 displayField : 'value',
33660 forceSelection : true,
33662 placeholder : this.yearPlaceholder,
33663 selectOnFocus : true,
33664 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33665 triggerAction : 'all',
33667 valueField : 'value',
33668 store : new Roo.data.SimpleStore({
33669 data : (function() {
33671 _this.fireEvent('years', _this, years);
33674 fields : [ 'value' ]
33677 select : function (_self, record, index)
33679 _this.setValue(_this.getValue());
33684 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33687 setValue : function(v, format)
33689 this.inputEl.dom.value = v;
33691 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33693 var d = Date.parseDate(v, f);
33700 this.setDay(d.format(this.dayFormat));
33701 this.setMonth(d.format(this.monthFormat));
33702 this.setYear(d.format(this.yearFormat));
33709 setDay : function(v)
33711 this.dayField.setValue(v);
33712 this.inputEl.dom.value = this.getValue();
33717 setMonth : function(v)
33719 this.monthField.setValue(v, true);
33720 this.inputEl.dom.value = this.getValue();
33725 setYear : function(v)
33727 this.yearField.setValue(v);
33728 this.inputEl.dom.value = this.getValue();
33733 getDay : function()
33735 return this.dayField.getValue();
33738 getMonth : function()
33740 return this.monthField.getValue();
33743 getYear : function()
33745 return this.yearField.getValue();
33748 getValue : function()
33750 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33752 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33762 this.inputEl.dom.value = '';
33767 validate : function()
33769 var d = this.dayField.validate();
33770 var m = this.monthField.validate();
33771 var y = this.yearField.validate();
33776 (!this.dayAllowBlank && !d) ||
33777 (!this.monthAllowBlank && !m) ||
33778 (!this.yearAllowBlank && !y)
33783 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33792 this.markInvalid();
33797 markValid : function()
33800 var label = this.el.select('label', true).first();
33801 var icon = this.el.select('i.fa-star', true).first();
33807 this.fireEvent('valid', this);
33811 * Mark this field as invalid
33812 * @param {String} msg The validation message
33814 markInvalid : function(msg)
33817 var label = this.el.select('label', true).first();
33818 var icon = this.el.select('i.fa-star', true).first();
33820 if(label && !icon){
33821 this.el.select('.roo-date-split-field-label', true).createChild({
33823 cls : 'text-danger fa fa-lg fa-star',
33824 tooltip : 'This field is required',
33825 style : 'margin-right:5px;'
33829 this.fireEvent('invalid', this, msg);
33832 clearInvalid : function()
33834 var label = this.el.select('label', true).first();
33835 var icon = this.el.select('i.fa-star', true).first();
33841 this.fireEvent('valid', this);
33844 getName: function()
33854 * http://masonry.desandro.com
33856 * The idea is to render all the bricks based on vertical width...
33858 * The original code extends 'outlayer' - we might need to use that....
33864 * @class Roo.bootstrap.LayoutMasonry
33865 * @extends Roo.bootstrap.Component
33866 * Bootstrap Layout Masonry class
33869 * Create a new Element
33870 * @param {Object} config The config object
33873 Roo.bootstrap.LayoutMasonry = function(config){
33875 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33879 Roo.bootstrap.LayoutMasonry.register(this);
33885 * Fire after layout the items
33886 * @param {Roo.bootstrap.LayoutMasonry} this
33887 * @param {Roo.EventObject} e
33894 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33897 * @cfg {Boolean} isLayoutInstant = no animation?
33899 isLayoutInstant : false, // needed?
33902 * @cfg {Number} boxWidth width of the columns
33907 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33912 * @cfg {Number} padWidth padding below box..
33917 * @cfg {Number} gutter gutter width..
33922 * @cfg {Number} maxCols maximum number of columns
33928 * @cfg {Boolean} isAutoInitial defalut true
33930 isAutoInitial : true,
33935 * @cfg {Boolean} isHorizontal defalut false
33937 isHorizontal : false,
33939 currentSize : null,
33945 bricks: null, //CompositeElement
33949 _isLayoutInited : false,
33951 // isAlternative : false, // only use for vertical layout...
33954 * @cfg {Number} alternativePadWidth padding below box..
33956 alternativePadWidth : 50,
33958 selectedBrick : [],
33960 getAutoCreate : function(){
33962 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33966 cls: 'blog-masonary-wrapper ' + this.cls,
33968 cls : 'mas-boxes masonary'
33975 getChildContainer: function( )
33977 if (this.boxesEl) {
33978 return this.boxesEl;
33981 this.boxesEl = this.el.select('.mas-boxes').first();
33983 return this.boxesEl;
33987 initEvents : function()
33991 if(this.isAutoInitial){
33992 Roo.log('hook children rendered');
33993 this.on('childrenrendered', function() {
33994 Roo.log('children rendered');
34000 initial : function()
34002 this.selectedBrick = [];
34004 this.currentSize = this.el.getBox(true);
34006 Roo.EventManager.onWindowResize(this.resize, this);
34008 if(!this.isAutoInitial){
34016 //this.layout.defer(500,this);
34020 resize : function()
34022 var cs = this.el.getBox(true);
34025 this.currentSize.width == cs.width &&
34026 this.currentSize.x == cs.x &&
34027 this.currentSize.height == cs.height &&
34028 this.currentSize.y == cs.y
34030 Roo.log("no change in with or X or Y");
34034 this.currentSize = cs;
34040 layout : function()
34042 this._resetLayout();
34044 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34046 this.layoutItems( isInstant );
34048 this._isLayoutInited = true;
34050 this.fireEvent('layout', this);
34054 _resetLayout : function()
34056 if(this.isHorizontal){
34057 this.horizontalMeasureColumns();
34061 this.verticalMeasureColumns();
34065 verticalMeasureColumns : function()
34067 this.getContainerWidth();
34069 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34070 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34074 var boxWidth = this.boxWidth + this.padWidth;
34076 if(this.containerWidth < this.boxWidth){
34077 boxWidth = this.containerWidth
34080 var containerWidth = this.containerWidth;
34082 var cols = Math.floor(containerWidth / boxWidth);
34084 this.cols = Math.max( cols, 1 );
34086 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34088 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34090 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34092 this.colWidth = boxWidth + avail - this.padWidth;
34094 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34095 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34098 horizontalMeasureColumns : function()
34100 this.getContainerWidth();
34102 var boxWidth = this.boxWidth;
34104 if(this.containerWidth < boxWidth){
34105 boxWidth = this.containerWidth;
34108 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34110 this.el.setHeight(boxWidth);
34114 getContainerWidth : function()
34116 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34119 layoutItems : function( isInstant )
34121 Roo.log(this.bricks);
34123 var items = Roo.apply([], this.bricks);
34125 if(this.isHorizontal){
34126 this._horizontalLayoutItems( items , isInstant );
34130 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34131 // this._verticalAlternativeLayoutItems( items , isInstant );
34135 this._verticalLayoutItems( items , isInstant );
34139 _verticalLayoutItems : function ( items , isInstant)
34141 if ( !items || !items.length ) {
34146 ['xs', 'xs', 'xs', 'tall'],
34147 ['xs', 'xs', 'tall'],
34148 ['xs', 'xs', 'sm'],
34149 ['xs', 'xs', 'xs'],
34155 ['sm', 'xs', 'xs'],
34159 ['tall', 'xs', 'xs', 'xs'],
34160 ['tall', 'xs', 'xs'],
34172 Roo.each(items, function(item, k){
34174 switch (item.size) {
34175 // these layouts take up a full box,
34186 boxes.push([item]);
34209 var filterPattern = function(box, length)
34217 var pattern = box.slice(0, length);
34221 Roo.each(pattern, function(i){
34222 format.push(i.size);
34225 Roo.each(standard, function(s){
34227 if(String(s) != String(format)){
34236 if(!match && length == 1){
34241 filterPattern(box, length - 1);
34245 queue.push(pattern);
34247 box = box.slice(length, box.length);
34249 filterPattern(box, 4);
34255 Roo.each(boxes, function(box, k){
34261 if(box.length == 1){
34266 filterPattern(box, 4);
34270 this._processVerticalLayoutQueue( queue, isInstant );
34274 // _verticalAlternativeLayoutItems : function( items , isInstant )
34276 // if ( !items || !items.length ) {
34280 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34284 _horizontalLayoutItems : function ( items , isInstant)
34286 if ( !items || !items.length || items.length < 3) {
34292 var eItems = items.slice(0, 3);
34294 items = items.slice(3, items.length);
34297 ['xs', 'xs', 'xs', 'wide'],
34298 ['xs', 'xs', 'wide'],
34299 ['xs', 'xs', 'sm'],
34300 ['xs', 'xs', 'xs'],
34306 ['sm', 'xs', 'xs'],
34310 ['wide', 'xs', 'xs', 'xs'],
34311 ['wide', 'xs', 'xs'],
34324 Roo.each(items, function(item, k){
34326 switch (item.size) {
34337 boxes.push([item]);
34361 var filterPattern = function(box, length)
34369 var pattern = box.slice(0, length);
34373 Roo.each(pattern, function(i){
34374 format.push(i.size);
34377 Roo.each(standard, function(s){
34379 if(String(s) != String(format)){
34388 if(!match && length == 1){
34393 filterPattern(box, length - 1);
34397 queue.push(pattern);
34399 box = box.slice(length, box.length);
34401 filterPattern(box, 4);
34407 Roo.each(boxes, function(box, k){
34413 if(box.length == 1){
34418 filterPattern(box, 4);
34425 var pos = this.el.getBox(true);
34429 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34431 var hit_end = false;
34433 Roo.each(queue, function(box){
34437 Roo.each(box, function(b){
34439 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34449 Roo.each(box, function(b){
34451 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34454 mx = Math.max(mx, b.x);
34458 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34462 Roo.each(box, function(b){
34464 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34478 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34481 /** Sets position of item in DOM
34482 * @param {Element} item
34483 * @param {Number} x - horizontal position
34484 * @param {Number} y - vertical position
34485 * @param {Boolean} isInstant - disables transitions
34487 _processVerticalLayoutQueue : function( queue, isInstant )
34489 var pos = this.el.getBox(true);
34494 for (var i = 0; i < this.cols; i++){
34498 Roo.each(queue, function(box, k){
34500 var col = k % this.cols;
34502 Roo.each(box, function(b,kk){
34504 b.el.position('absolute');
34506 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34507 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34509 if(b.size == 'md-left' || b.size == 'md-right'){
34510 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34511 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34514 b.el.setWidth(width);
34515 b.el.setHeight(height);
34517 b.el.select('iframe',true).setSize(width,height);
34521 for (var i = 0; i < this.cols; i++){
34523 if(maxY[i] < maxY[col]){
34528 col = Math.min(col, i);
34532 x = pos.x + col * (this.colWidth + this.padWidth);
34536 var positions = [];
34538 switch (box.length){
34540 positions = this.getVerticalOneBoxColPositions(x, y, box);
34543 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34546 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34549 positions = this.getVerticalFourBoxColPositions(x, y, box);
34555 Roo.each(box, function(b,kk){
34557 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34559 var sz = b.el.getSize();
34561 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34569 for (var i = 0; i < this.cols; i++){
34570 mY = Math.max(mY, maxY[i]);
34573 this.el.setHeight(mY - pos.y);
34577 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34579 // var pos = this.el.getBox(true);
34582 // var maxX = pos.right;
34584 // var maxHeight = 0;
34586 // Roo.each(items, function(item, k){
34590 // item.el.position('absolute');
34592 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34594 // item.el.setWidth(width);
34596 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34598 // item.el.setHeight(height);
34601 // item.el.setXY([x, y], isInstant ? false : true);
34603 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34606 // y = y + height + this.alternativePadWidth;
34608 // maxHeight = maxHeight + height + this.alternativePadWidth;
34612 // this.el.setHeight(maxHeight);
34616 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34618 var pos = this.el.getBox(true);
34623 var maxX = pos.right;
34625 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34627 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34629 Roo.each(queue, function(box, k){
34631 Roo.each(box, function(b, kk){
34633 b.el.position('absolute');
34635 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34636 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34638 if(b.size == 'md-left' || b.size == 'md-right'){
34639 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34640 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34643 b.el.setWidth(width);
34644 b.el.setHeight(height);
34652 var positions = [];
34654 switch (box.length){
34656 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34659 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34662 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34665 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34671 Roo.each(box, function(b,kk){
34673 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34675 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34683 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34685 Roo.each(eItems, function(b,k){
34687 b.size = (k == 0) ? 'sm' : 'xs';
34688 b.x = (k == 0) ? 2 : 1;
34689 b.y = (k == 0) ? 2 : 1;
34691 b.el.position('absolute');
34693 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34695 b.el.setWidth(width);
34697 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34699 b.el.setHeight(height);
34703 var positions = [];
34706 x : maxX - this.unitWidth * 2 - this.gutter,
34711 x : maxX - this.unitWidth,
34712 y : minY + (this.unitWidth + this.gutter) * 2
34716 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34720 Roo.each(eItems, function(b,k){
34722 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34728 getVerticalOneBoxColPositions : function(x, y, box)
34732 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34734 if(box[0].size == 'md-left'){
34738 if(box[0].size == 'md-right'){
34743 x : x + (this.unitWidth + this.gutter) * rand,
34750 getVerticalTwoBoxColPositions : function(x, y, box)
34754 if(box[0].size == 'xs'){
34758 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34762 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34776 x : x + (this.unitWidth + this.gutter) * 2,
34777 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34784 getVerticalThreeBoxColPositions : function(x, y, box)
34788 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34796 x : x + (this.unitWidth + this.gutter) * 1,
34801 x : x + (this.unitWidth + this.gutter) * 2,
34809 if(box[0].size == 'xs' && box[1].size == 'xs'){
34818 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34822 x : x + (this.unitWidth + this.gutter) * 1,
34836 x : x + (this.unitWidth + this.gutter) * 2,
34841 x : x + (this.unitWidth + this.gutter) * 2,
34842 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34849 getVerticalFourBoxColPositions : function(x, y, box)
34853 if(box[0].size == 'xs'){
34862 y : y + (this.unitHeight + this.gutter) * 1
34867 y : y + (this.unitHeight + this.gutter) * 2
34871 x : x + (this.unitWidth + this.gutter) * 1,
34885 x : x + (this.unitWidth + this.gutter) * 2,
34890 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34891 y : y + (this.unitHeight + this.gutter) * 1
34895 x : x + (this.unitWidth + this.gutter) * 2,
34896 y : y + (this.unitWidth + this.gutter) * 2
34903 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34907 if(box[0].size == 'md-left'){
34909 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34916 if(box[0].size == 'md-right'){
34918 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34919 y : minY + (this.unitWidth + this.gutter) * 1
34925 var rand = Math.floor(Math.random() * (4 - box[0].y));
34928 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34929 y : minY + (this.unitWidth + this.gutter) * rand
34936 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34940 if(box[0].size == 'xs'){
34943 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34948 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34949 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34957 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34962 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34963 y : minY + (this.unitWidth + this.gutter) * 2
34970 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34974 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34977 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34982 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34983 y : minY + (this.unitWidth + this.gutter) * 1
34987 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34988 y : minY + (this.unitWidth + this.gutter) * 2
34995 if(box[0].size == 'xs' && box[1].size == 'xs'){
34998 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35003 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35008 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35009 y : minY + (this.unitWidth + this.gutter) * 1
35017 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35022 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35023 y : minY + (this.unitWidth + this.gutter) * 2
35027 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35028 y : minY + (this.unitWidth + this.gutter) * 2
35035 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35039 if(box[0].size == 'xs'){
35042 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35047 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35052 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),
35057 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35058 y : minY + (this.unitWidth + this.gutter) * 1
35066 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35071 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35072 y : minY + (this.unitWidth + this.gutter) * 2
35076 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35077 y : minY + (this.unitWidth + this.gutter) * 2
35081 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),
35082 y : minY + (this.unitWidth + this.gutter) * 2
35090 * remove a Masonry Brick
35091 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35093 removeBrick : function(brick_id)
35099 for (var i = 0; i<this.bricks.length; i++) {
35100 if (this.bricks[i].id == brick_id) {
35101 this.bricks.splice(i,1);
35102 this.el.dom.removeChild(Roo.get(brick_id).dom);
35109 * adds a Masonry Brick
35110 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35112 addBrick : function(cfg)
35114 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35115 //this.register(cn);
35116 cn.parentId = this.id;
35117 cn.render(this.el);
35122 * register a Masonry Brick
35123 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35126 register : function(brick)
35128 this.bricks.push(brick);
35129 brick.masonryId = this.id;
35133 * clear all the Masonry Brick
35135 clearAll : function()
35138 //this.getChildContainer().dom.innerHTML = "";
35139 this.el.dom.innerHTML = '';
35142 getSelected : function()
35144 if (!this.selectedBrick) {
35148 return this.selectedBrick;
35152 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35156 * register a Masonry Layout
35157 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35160 register : function(layout)
35162 this.groups[layout.id] = layout;
35165 * fetch a Masonry Layout based on the masonry layout ID
35166 * @param {string} the masonry layout to add
35167 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35170 get: function(layout_id) {
35171 if (typeof(this.groups[layout_id]) == 'undefined') {
35174 return this.groups[layout_id] ;
35186 * http://masonry.desandro.com
35188 * The idea is to render all the bricks based on vertical width...
35190 * The original code extends 'outlayer' - we might need to use that....
35196 * @class Roo.bootstrap.LayoutMasonryAuto
35197 * @extends Roo.bootstrap.Component
35198 * Bootstrap Layout Masonry class
35201 * Create a new Element
35202 * @param {Object} config The config object
35205 Roo.bootstrap.LayoutMasonryAuto = function(config){
35206 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35209 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35212 * @cfg {Boolean} isFitWidth - resize the width..
35214 isFitWidth : false, // options..
35216 * @cfg {Boolean} isOriginLeft = left align?
35218 isOriginLeft : true,
35220 * @cfg {Boolean} isOriginTop = top align?
35222 isOriginTop : false,
35224 * @cfg {Boolean} isLayoutInstant = no animation?
35226 isLayoutInstant : false, // needed?
35228 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35230 isResizingContainer : true,
35232 * @cfg {Number} columnWidth width of the columns
35238 * @cfg {Number} maxCols maximum number of columns
35243 * @cfg {Number} padHeight padding below box..
35249 * @cfg {Boolean} isAutoInitial defalut true
35252 isAutoInitial : true,
35258 initialColumnWidth : 0,
35259 currentSize : null,
35261 colYs : null, // array.
35268 bricks: null, //CompositeElement
35269 cols : 0, // array?
35270 // element : null, // wrapped now this.el
35271 _isLayoutInited : null,
35274 getAutoCreate : function(){
35278 cls: 'blog-masonary-wrapper ' + this.cls,
35280 cls : 'mas-boxes masonary'
35287 getChildContainer: function( )
35289 if (this.boxesEl) {
35290 return this.boxesEl;
35293 this.boxesEl = this.el.select('.mas-boxes').first();
35295 return this.boxesEl;
35299 initEvents : function()
35303 if(this.isAutoInitial){
35304 Roo.log('hook children rendered');
35305 this.on('childrenrendered', function() {
35306 Roo.log('children rendered');
35313 initial : function()
35315 this.reloadItems();
35317 this.currentSize = this.el.getBox(true);
35319 /// was window resize... - let's see if this works..
35320 Roo.EventManager.onWindowResize(this.resize, this);
35322 if(!this.isAutoInitial){
35327 this.layout.defer(500,this);
35330 reloadItems: function()
35332 this.bricks = this.el.select('.masonry-brick', true);
35334 this.bricks.each(function(b) {
35335 //Roo.log(b.getSize());
35336 if (!b.attr('originalwidth')) {
35337 b.attr('originalwidth', b.getSize().width);
35342 Roo.log(this.bricks.elements.length);
35345 resize : function()
35348 var cs = this.el.getBox(true);
35350 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35351 Roo.log("no change in with or X");
35354 this.currentSize = cs;
35358 layout : function()
35361 this._resetLayout();
35362 //this._manageStamps();
35364 // don't animate first layout
35365 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35366 this.layoutItems( isInstant );
35368 // flag for initalized
35369 this._isLayoutInited = true;
35372 layoutItems : function( isInstant )
35374 //var items = this._getItemsForLayout( this.items );
35375 // original code supports filtering layout items.. we just ignore it..
35377 this._layoutItems( this.bricks , isInstant );
35379 this._postLayout();
35381 _layoutItems : function ( items , isInstant)
35383 //this.fireEvent( 'layout', this, items );
35386 if ( !items || !items.elements.length ) {
35387 // no items, emit event with empty array
35392 items.each(function(item) {
35393 Roo.log("layout item");
35395 // get x/y object from method
35396 var position = this._getItemLayoutPosition( item );
35398 position.item = item;
35399 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35400 queue.push( position );
35403 this._processLayoutQueue( queue );
35405 /** Sets position of item in DOM
35406 * @param {Element} item
35407 * @param {Number} x - horizontal position
35408 * @param {Number} y - vertical position
35409 * @param {Boolean} isInstant - disables transitions
35411 _processLayoutQueue : function( queue )
35413 for ( var i=0, len = queue.length; i < len; i++ ) {
35414 var obj = queue[i];
35415 obj.item.position('absolute');
35416 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35422 * Any logic you want to do after each layout,
35423 * i.e. size the container
35425 _postLayout : function()
35427 this.resizeContainer();
35430 resizeContainer : function()
35432 if ( !this.isResizingContainer ) {
35435 var size = this._getContainerSize();
35437 this.el.setSize(size.width,size.height);
35438 this.boxesEl.setSize(size.width,size.height);
35444 _resetLayout : function()
35446 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35447 this.colWidth = this.el.getWidth();
35448 //this.gutter = this.el.getWidth();
35450 this.measureColumns();
35456 this.colYs.push( 0 );
35462 measureColumns : function()
35464 this.getContainerWidth();
35465 // if columnWidth is 0, default to outerWidth of first item
35466 if ( !this.columnWidth ) {
35467 var firstItem = this.bricks.first();
35468 Roo.log(firstItem);
35469 this.columnWidth = this.containerWidth;
35470 if (firstItem && firstItem.attr('originalwidth') ) {
35471 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35473 // columnWidth fall back to item of first element
35474 Roo.log("set column width?");
35475 this.initialColumnWidth = this.columnWidth ;
35477 // if first elem has no width, default to size of container
35482 if (this.initialColumnWidth) {
35483 this.columnWidth = this.initialColumnWidth;
35488 // column width is fixed at the top - however if container width get's smaller we should
35491 // this bit calcs how man columns..
35493 var columnWidth = this.columnWidth += this.gutter;
35495 // calculate columns
35496 var containerWidth = this.containerWidth + this.gutter;
35498 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35499 // fix rounding errors, typically with gutters
35500 var excess = columnWidth - containerWidth % columnWidth;
35503 // if overshoot is less than a pixel, round up, otherwise floor it
35504 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35505 cols = Math[ mathMethod ]( cols );
35506 this.cols = Math.max( cols, 1 );
35507 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35509 // padding positioning..
35510 var totalColWidth = this.cols * this.columnWidth;
35511 var padavail = this.containerWidth - totalColWidth;
35512 // so for 2 columns - we need 3 'pads'
35514 var padNeeded = (1+this.cols) * this.padWidth;
35516 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35518 this.columnWidth += padExtra
35519 //this.padWidth = Math.floor(padavail / ( this.cols));
35521 // adjust colum width so that padding is fixed??
35523 // we have 3 columns ... total = width * 3
35524 // we have X left over... that should be used by
35526 //if (this.expandC) {
35534 getContainerWidth : function()
35536 /* // container is parent if fit width
35537 var container = this.isFitWidth ? this.element.parentNode : this.element;
35538 // check that this.size and size are there
35539 // IE8 triggers resize on body size change, so they might not be
35541 var size = getSize( container ); //FIXME
35542 this.containerWidth = size && size.innerWidth; //FIXME
35545 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35549 _getItemLayoutPosition : function( item ) // what is item?
35551 // we resize the item to our columnWidth..
35553 item.setWidth(this.columnWidth);
35554 item.autoBoxAdjust = false;
35556 var sz = item.getSize();
35558 // how many columns does this brick span
35559 var remainder = this.containerWidth % this.columnWidth;
35561 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35562 // round if off by 1 pixel, otherwise use ceil
35563 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35564 colSpan = Math.min( colSpan, this.cols );
35566 // normally this should be '1' as we dont' currently allow multi width columns..
35568 var colGroup = this._getColGroup( colSpan );
35569 // get the minimum Y value from the columns
35570 var minimumY = Math.min.apply( Math, colGroup );
35571 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35573 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35575 // position the brick
35577 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35578 y: this.currentSize.y + minimumY + this.padHeight
35582 // apply setHeight to necessary columns
35583 var setHeight = minimumY + sz.height + this.padHeight;
35584 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35586 var setSpan = this.cols + 1 - colGroup.length;
35587 for ( var i = 0; i < setSpan; i++ ) {
35588 this.colYs[ shortColIndex + i ] = setHeight ;
35595 * @param {Number} colSpan - number of columns the element spans
35596 * @returns {Array} colGroup
35598 _getColGroup : function( colSpan )
35600 if ( colSpan < 2 ) {
35601 // if brick spans only one column, use all the column Ys
35606 // how many different places could this brick fit horizontally
35607 var groupCount = this.cols + 1 - colSpan;
35608 // for each group potential horizontal position
35609 for ( var i = 0; i < groupCount; i++ ) {
35610 // make an array of colY values for that one group
35611 var groupColYs = this.colYs.slice( i, i + colSpan );
35612 // and get the max value of the array
35613 colGroup[i] = Math.max.apply( Math, groupColYs );
35618 _manageStamp : function( stamp )
35620 var stampSize = stamp.getSize();
35621 var offset = stamp.getBox();
35622 // get the columns that this stamp affects
35623 var firstX = this.isOriginLeft ? offset.x : offset.right;
35624 var lastX = firstX + stampSize.width;
35625 var firstCol = Math.floor( firstX / this.columnWidth );
35626 firstCol = Math.max( 0, firstCol );
35628 var lastCol = Math.floor( lastX / this.columnWidth );
35629 // lastCol should not go over if multiple of columnWidth #425
35630 lastCol -= lastX % this.columnWidth ? 0 : 1;
35631 lastCol = Math.min( this.cols - 1, lastCol );
35633 // set colYs to bottom of the stamp
35634 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35637 for ( var i = firstCol; i <= lastCol; i++ ) {
35638 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35643 _getContainerSize : function()
35645 this.maxY = Math.max.apply( Math, this.colYs );
35650 if ( this.isFitWidth ) {
35651 size.width = this._getContainerFitWidth();
35657 _getContainerFitWidth : function()
35659 var unusedCols = 0;
35660 // count unused columns
35663 if ( this.colYs[i] !== 0 ) {
35668 // fit container to columns that have been used
35669 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35672 needsResizeLayout : function()
35674 var previousWidth = this.containerWidth;
35675 this.getContainerWidth();
35676 return previousWidth !== this.containerWidth;
35691 * @class Roo.bootstrap.MasonryBrick
35692 * @extends Roo.bootstrap.Component
35693 * Bootstrap MasonryBrick class
35696 * Create a new MasonryBrick
35697 * @param {Object} config The config object
35700 Roo.bootstrap.MasonryBrick = function(config){
35702 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35704 Roo.bootstrap.MasonryBrick.register(this);
35710 * When a MasonryBrick is clcik
35711 * @param {Roo.bootstrap.MasonryBrick} this
35712 * @param {Roo.EventObject} e
35718 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35721 * @cfg {String} title
35725 * @cfg {String} html
35729 * @cfg {String} bgimage
35733 * @cfg {String} videourl
35737 * @cfg {String} cls
35741 * @cfg {String} href
35745 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35750 * @cfg {String} placetitle (center|bottom)
35755 * @cfg {Boolean} isFitContainer defalut true
35757 isFitContainer : true,
35760 * @cfg {Boolean} preventDefault defalut false
35762 preventDefault : false,
35765 * @cfg {Boolean} inverse defalut false
35767 maskInverse : false,
35769 getAutoCreate : function()
35771 if(!this.isFitContainer){
35772 return this.getSplitAutoCreate();
35775 var cls = 'masonry-brick masonry-brick-full';
35777 if(this.href.length){
35778 cls += ' masonry-brick-link';
35781 if(this.bgimage.length){
35782 cls += ' masonry-brick-image';
35785 if(this.maskInverse){
35786 cls += ' mask-inverse';
35789 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35790 cls += ' enable-mask';
35794 cls += ' masonry-' + this.size + '-brick';
35797 if(this.placetitle.length){
35799 switch (this.placetitle) {
35801 cls += ' masonry-center-title';
35804 cls += ' masonry-bottom-title';
35811 if(!this.html.length && !this.bgimage.length){
35812 cls += ' masonry-center-title';
35815 if(!this.html.length && this.bgimage.length){
35816 cls += ' masonry-bottom-title';
35821 cls += ' ' + this.cls;
35825 tag: (this.href.length) ? 'a' : 'div',
35830 cls: 'masonry-brick-mask'
35834 cls: 'masonry-brick-paragraph',
35840 if(this.href.length){
35841 cfg.href = this.href;
35844 var cn = cfg.cn[1].cn;
35846 if(this.title.length){
35849 cls: 'masonry-brick-title',
35854 if(this.html.length){
35857 cls: 'masonry-brick-text',
35862 if (!this.title.length && !this.html.length) {
35863 cfg.cn[1].cls += ' hide';
35866 if(this.bgimage.length){
35869 cls: 'masonry-brick-image-view',
35874 if(this.videourl.length){
35875 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35876 // youtube support only?
35879 cls: 'masonry-brick-image-view',
35882 allowfullscreen : true
35890 getSplitAutoCreate : function()
35892 var cls = 'masonry-brick masonry-brick-split';
35894 if(this.href.length){
35895 cls += ' masonry-brick-link';
35898 if(this.bgimage.length){
35899 cls += ' masonry-brick-image';
35903 cls += ' masonry-' + this.size + '-brick';
35906 switch (this.placetitle) {
35908 cls += ' masonry-center-title';
35911 cls += ' masonry-bottom-title';
35914 if(!this.bgimage.length){
35915 cls += ' masonry-center-title';
35918 if(this.bgimage.length){
35919 cls += ' masonry-bottom-title';
35925 cls += ' ' + this.cls;
35929 tag: (this.href.length) ? 'a' : 'div',
35934 cls: 'masonry-brick-split-head',
35938 cls: 'masonry-brick-paragraph',
35945 cls: 'masonry-brick-split-body',
35951 if(this.href.length){
35952 cfg.href = this.href;
35955 if(this.title.length){
35956 cfg.cn[0].cn[0].cn.push({
35958 cls: 'masonry-brick-title',
35963 if(this.html.length){
35964 cfg.cn[1].cn.push({
35966 cls: 'masonry-brick-text',
35971 if(this.bgimage.length){
35972 cfg.cn[0].cn.push({
35974 cls: 'masonry-brick-image-view',
35979 if(this.videourl.length){
35980 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35981 // youtube support only?
35982 cfg.cn[0].cn.cn.push({
35984 cls: 'masonry-brick-image-view',
35987 allowfullscreen : true
35994 initEvents: function()
35996 switch (this.size) {
36029 this.el.on('touchstart', this.onTouchStart, this);
36030 this.el.on('touchmove', this.onTouchMove, this);
36031 this.el.on('touchend', this.onTouchEnd, this);
36032 this.el.on('contextmenu', this.onContextMenu, this);
36034 this.el.on('mouseenter' ,this.enter, this);
36035 this.el.on('mouseleave', this.leave, this);
36036 this.el.on('click', this.onClick, this);
36039 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36040 this.parent().bricks.push(this);
36045 onClick: function(e, el)
36047 var time = this.endTimer - this.startTimer;
36048 // Roo.log(e.preventDefault());
36051 e.preventDefault();
36056 if(!this.preventDefault){
36060 e.preventDefault();
36062 if (this.activeClass != '') {
36063 this.selectBrick();
36066 this.fireEvent('click', this, e);
36069 enter: function(e, el)
36071 e.preventDefault();
36073 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36077 if(this.bgimage.length && this.html.length){
36078 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36082 leave: function(e, el)
36084 e.preventDefault();
36086 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36090 if(this.bgimage.length && this.html.length){
36091 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36095 onTouchStart: function(e, el)
36097 // e.preventDefault();
36099 this.touchmoved = false;
36101 if(!this.isFitContainer){
36105 if(!this.bgimage.length || !this.html.length){
36109 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36111 this.timer = new Date().getTime();
36115 onTouchMove: function(e, el)
36117 this.touchmoved = true;
36120 onContextMenu : function(e,el)
36122 e.preventDefault();
36123 e.stopPropagation();
36127 onTouchEnd: function(e, el)
36129 // e.preventDefault();
36131 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36138 if(!this.bgimage.length || !this.html.length){
36140 if(this.href.length){
36141 window.location.href = this.href;
36147 if(!this.isFitContainer){
36151 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36153 window.location.href = this.href;
36156 //selection on single brick only
36157 selectBrick : function() {
36159 if (!this.parentId) {
36163 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36164 var index = m.selectedBrick.indexOf(this.id);
36167 m.selectedBrick.splice(index,1);
36168 this.el.removeClass(this.activeClass);
36172 for(var i = 0; i < m.selectedBrick.length; i++) {
36173 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36174 b.el.removeClass(b.activeClass);
36177 m.selectedBrick = [];
36179 m.selectedBrick.push(this.id);
36180 this.el.addClass(this.activeClass);
36184 isSelected : function(){
36185 return this.el.hasClass(this.activeClass);
36190 Roo.apply(Roo.bootstrap.MasonryBrick, {
36193 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36195 * register a Masonry Brick
36196 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36199 register : function(brick)
36201 //this.groups[brick.id] = brick;
36202 this.groups.add(brick.id, brick);
36205 * fetch a masonry brick based on the masonry brick ID
36206 * @param {string} the masonry brick to add
36207 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36210 get: function(brick_id)
36212 // if (typeof(this.groups[brick_id]) == 'undefined') {
36215 // return this.groups[brick_id] ;
36217 if(this.groups.key(brick_id)) {
36218 return this.groups.key(brick_id);
36236 * @class Roo.bootstrap.Brick
36237 * @extends Roo.bootstrap.Component
36238 * Bootstrap Brick class
36241 * Create a new Brick
36242 * @param {Object} config The config object
36245 Roo.bootstrap.Brick = function(config){
36246 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36252 * When a Brick is click
36253 * @param {Roo.bootstrap.Brick} this
36254 * @param {Roo.EventObject} e
36260 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36263 * @cfg {String} title
36267 * @cfg {String} html
36271 * @cfg {String} bgimage
36275 * @cfg {String} cls
36279 * @cfg {String} href
36283 * @cfg {String} video
36287 * @cfg {Boolean} square
36291 getAutoCreate : function()
36293 var cls = 'roo-brick';
36295 if(this.href.length){
36296 cls += ' roo-brick-link';
36299 if(this.bgimage.length){
36300 cls += ' roo-brick-image';
36303 if(!this.html.length && !this.bgimage.length){
36304 cls += ' roo-brick-center-title';
36307 if(!this.html.length && this.bgimage.length){
36308 cls += ' roo-brick-bottom-title';
36312 cls += ' ' + this.cls;
36316 tag: (this.href.length) ? 'a' : 'div',
36321 cls: 'roo-brick-paragraph',
36327 if(this.href.length){
36328 cfg.href = this.href;
36331 var cn = cfg.cn[0].cn;
36333 if(this.title.length){
36336 cls: 'roo-brick-title',
36341 if(this.html.length){
36344 cls: 'roo-brick-text',
36351 if(this.bgimage.length){
36354 cls: 'roo-brick-image-view',
36362 initEvents: function()
36364 if(this.title.length || this.html.length){
36365 this.el.on('mouseenter' ,this.enter, this);
36366 this.el.on('mouseleave', this.leave, this);
36369 Roo.EventManager.onWindowResize(this.resize, this);
36371 if(this.bgimage.length){
36372 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36373 this.imageEl.on('load', this.onImageLoad, this);
36380 onImageLoad : function()
36385 resize : function()
36387 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36389 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36391 if(this.bgimage.length){
36392 var image = this.el.select('.roo-brick-image-view', true).first();
36394 image.setWidth(paragraph.getWidth());
36397 image.setHeight(paragraph.getWidth());
36400 this.el.setHeight(image.getHeight());
36401 paragraph.setHeight(image.getHeight());
36407 enter: function(e, el)
36409 e.preventDefault();
36411 if(this.bgimage.length){
36412 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36413 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36417 leave: function(e, el)
36419 e.preventDefault();
36421 if(this.bgimage.length){
36422 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36423 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36438 * @class Roo.bootstrap.NumberField
36439 * @extends Roo.bootstrap.Input
36440 * Bootstrap NumberField class
36446 * Create a new NumberField
36447 * @param {Object} config The config object
36450 Roo.bootstrap.NumberField = function(config){
36451 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36454 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36457 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36459 allowDecimals : true,
36461 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36463 decimalSeparator : ".",
36465 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36467 decimalPrecision : 2,
36469 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36471 allowNegative : true,
36474 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36478 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36480 minValue : Number.NEGATIVE_INFINITY,
36482 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36484 maxValue : Number.MAX_VALUE,
36486 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36488 minText : "The minimum value for this field is {0}",
36490 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36492 maxText : "The maximum value for this field is {0}",
36494 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36495 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36497 nanText : "{0} is not a valid number",
36499 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36501 thousandsDelimiter : false,
36503 * @cfg {String} valueAlign alignment of value
36505 valueAlign : "left",
36507 getAutoCreate : function()
36509 var hiddenInput = {
36513 cls: 'hidden-number-input'
36517 hiddenInput.name = this.name;
36522 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36524 this.name = hiddenInput.name;
36526 if(cfg.cn.length > 0) {
36527 cfg.cn.push(hiddenInput);
36534 initEvents : function()
36536 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36538 var allowed = "0123456789";
36540 if(this.allowDecimals){
36541 allowed += this.decimalSeparator;
36544 if(this.allowNegative){
36548 if(this.thousandsDelimiter) {
36552 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36554 var keyPress = function(e){
36556 var k = e.getKey();
36558 var c = e.getCharCode();
36561 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36562 allowed.indexOf(String.fromCharCode(c)) === -1
36568 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36572 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36577 this.el.on("keypress", keyPress, this);
36580 validateValue : function(value)
36583 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36587 var num = this.parseValue(value);
36590 this.markInvalid(String.format(this.nanText, value));
36594 if(num < this.minValue){
36595 this.markInvalid(String.format(this.minText, this.minValue));
36599 if(num > this.maxValue){
36600 this.markInvalid(String.format(this.maxText, this.maxValue));
36607 getValue : function()
36609 var v = this.hiddenEl().getValue();
36611 return this.fixPrecision(this.parseValue(v));
36614 parseValue : function(value)
36616 if(this.thousandsDelimiter) {
36618 r = new RegExp(",", "g");
36619 value = value.replace(r, "");
36622 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36623 return isNaN(value) ? '' : value;
36626 fixPrecision : function(value)
36628 if(this.thousandsDelimiter) {
36630 r = new RegExp(",", "g");
36631 value = value.replace(r, "");
36634 var nan = isNaN(value);
36636 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36637 return nan ? '' : value;
36639 return parseFloat(value).toFixed(this.decimalPrecision);
36642 setValue : function(v)
36644 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36650 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36652 this.inputEl().dom.value = (v == '') ? '' :
36653 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36655 if(!this.allowZero && v === '0') {
36656 this.hiddenEl().dom.value = '';
36657 this.inputEl().dom.value = '';
36664 decimalPrecisionFcn : function(v)
36666 return Math.floor(v);
36669 beforeBlur : function()
36671 var v = this.parseValue(this.getRawValue());
36673 if(v || v === 0 || v === ''){
36678 hiddenEl : function()
36680 return this.el.select('input.hidden-number-input',true).first();
36692 * @class Roo.bootstrap.DocumentSlider
36693 * @extends Roo.bootstrap.Component
36694 * Bootstrap DocumentSlider class
36697 * Create a new DocumentViewer
36698 * @param {Object} config The config object
36701 Roo.bootstrap.DocumentSlider = function(config){
36702 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36709 * Fire after initEvent
36710 * @param {Roo.bootstrap.DocumentSlider} this
36715 * Fire after update
36716 * @param {Roo.bootstrap.DocumentSlider} this
36722 * @param {Roo.bootstrap.DocumentSlider} this
36728 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36734 getAutoCreate : function()
36738 cls : 'roo-document-slider',
36742 cls : 'roo-document-slider-header',
36746 cls : 'roo-document-slider-header-title'
36752 cls : 'roo-document-slider-body',
36756 cls : 'roo-document-slider-prev',
36760 cls : 'fa fa-chevron-left'
36766 cls : 'roo-document-slider-thumb',
36770 cls : 'roo-document-slider-image'
36776 cls : 'roo-document-slider-next',
36780 cls : 'fa fa-chevron-right'
36792 initEvents : function()
36794 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36795 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36797 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36798 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36800 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36801 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36803 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36804 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36806 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36807 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36809 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36810 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36812 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36813 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36815 this.thumbEl.on('click', this.onClick, this);
36817 this.prevIndicator.on('click', this.prev, this);
36819 this.nextIndicator.on('click', this.next, this);
36823 initial : function()
36825 if(this.files.length){
36826 this.indicator = 1;
36830 this.fireEvent('initial', this);
36833 update : function()
36835 this.imageEl.attr('src', this.files[this.indicator - 1]);
36837 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36839 this.prevIndicator.show();
36841 if(this.indicator == 1){
36842 this.prevIndicator.hide();
36845 this.nextIndicator.show();
36847 if(this.indicator == this.files.length){
36848 this.nextIndicator.hide();
36851 this.thumbEl.scrollTo('top');
36853 this.fireEvent('update', this);
36856 onClick : function(e)
36858 e.preventDefault();
36860 this.fireEvent('click', this);
36865 e.preventDefault();
36867 this.indicator = Math.max(1, this.indicator - 1);
36874 e.preventDefault();
36876 this.indicator = Math.min(this.files.length, this.indicator + 1);
36890 * @class Roo.bootstrap.RadioSet
36891 * @extends Roo.bootstrap.Input
36892 * Bootstrap RadioSet class
36893 * @cfg {String} indicatorpos (left|right) default left
36894 * @cfg {Boolean} inline (true|false) inline the element (default true)
36895 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36897 * Create a new RadioSet
36898 * @param {Object} config The config object
36901 Roo.bootstrap.RadioSet = function(config){
36903 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36907 Roo.bootstrap.RadioSet.register(this);
36912 * Fires when the element is checked or unchecked.
36913 * @param {Roo.bootstrap.RadioSet} this This radio
36914 * @param {Roo.bootstrap.Radio} item The checked item
36919 * Fires when the element is click.
36920 * @param {Roo.bootstrap.RadioSet} this This radio set
36921 * @param {Roo.bootstrap.Radio} item The checked item
36922 * @param {Roo.EventObject} e The event object
36929 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36937 indicatorpos : 'left',
36939 getAutoCreate : function()
36943 cls : 'roo-radio-set-label',
36947 html : this.fieldLabel
36951 if (Roo.bootstrap.version == 3) {
36954 if(this.indicatorpos == 'left'){
36957 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36958 tooltip : 'This field is required'
36963 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36964 tooltip : 'This field is required'
36970 cls : 'roo-radio-set-items'
36973 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36975 if (align === 'left' && this.fieldLabel.length) {
36978 cls : "roo-radio-set-right",
36984 if(this.labelWidth > 12){
36985 label.style = "width: " + this.labelWidth + 'px';
36988 if(this.labelWidth < 13 && this.labelmd == 0){
36989 this.labelmd = this.labelWidth;
36992 if(this.labellg > 0){
36993 label.cls += ' col-lg-' + this.labellg;
36994 items.cls += ' col-lg-' + (12 - this.labellg);
36997 if(this.labelmd > 0){
36998 label.cls += ' col-md-' + this.labelmd;
36999 items.cls += ' col-md-' + (12 - this.labelmd);
37002 if(this.labelsm > 0){
37003 label.cls += ' col-sm-' + this.labelsm;
37004 items.cls += ' col-sm-' + (12 - this.labelsm);
37007 if(this.labelxs > 0){
37008 label.cls += ' col-xs-' + this.labelxs;
37009 items.cls += ' col-xs-' + (12 - this.labelxs);
37015 cls : 'roo-radio-set',
37019 cls : 'roo-radio-set-input',
37022 value : this.value ? this.value : ''
37029 if(this.weight.length){
37030 cfg.cls += ' roo-radio-' + this.weight;
37034 cfg.cls += ' roo-radio-set-inline';
37038 ['xs','sm','md','lg'].map(function(size){
37039 if (settings[size]) {
37040 cfg.cls += ' col-' + size + '-' + settings[size];
37048 initEvents : function()
37050 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37051 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37053 if(!this.fieldLabel.length){
37054 this.labelEl.hide();
37057 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37058 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37060 this.indicator = this.indicatorEl();
37062 if(this.indicator){
37063 this.indicator.addClass('invisible');
37066 this.originalValue = this.getValue();
37070 inputEl: function ()
37072 return this.el.select('.roo-radio-set-input', true).first();
37075 getChildContainer : function()
37077 return this.itemsEl;
37080 register : function(item)
37082 this.radioes.push(item);
37086 validate : function()
37088 if(this.getVisibilityEl().hasClass('hidden')){
37094 Roo.each(this.radioes, function(i){
37103 if(this.allowBlank) {
37107 if(this.disabled || valid){
37112 this.markInvalid();
37117 markValid : function()
37119 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37120 this.indicatorEl().removeClass('visible');
37121 this.indicatorEl().addClass('invisible');
37125 if (Roo.bootstrap.version == 3) {
37126 this.el.removeClass([this.invalidClass, this.validClass]);
37127 this.el.addClass(this.validClass);
37129 this.el.removeClass(['is-invalid','is-valid']);
37130 this.el.addClass(['is-valid']);
37132 this.fireEvent('valid', this);
37135 markInvalid : function(msg)
37137 if(this.allowBlank || this.disabled){
37141 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37142 this.indicatorEl().removeClass('invisible');
37143 this.indicatorEl().addClass('visible');
37145 if (Roo.bootstrap.version == 3) {
37146 this.el.removeClass([this.invalidClass, this.validClass]);
37147 this.el.addClass(this.invalidClass);
37149 this.el.removeClass(['is-invalid','is-valid']);
37150 this.el.addClass(['is-invalid']);
37153 this.fireEvent('invalid', this, msg);
37157 setValue : function(v, suppressEvent)
37159 if(this.value === v){
37166 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37169 Roo.each(this.radioes, function(i){
37171 i.el.removeClass('checked');
37174 Roo.each(this.radioes, function(i){
37176 if(i.value === v || i.value.toString() === v.toString()){
37178 i.el.addClass('checked');
37180 if(suppressEvent !== true){
37181 this.fireEvent('check', this, i);
37192 clearInvalid : function(){
37194 if(!this.el || this.preventMark){
37198 this.el.removeClass([this.invalidClass]);
37200 this.fireEvent('valid', this);
37205 Roo.apply(Roo.bootstrap.RadioSet, {
37209 register : function(set)
37211 this.groups[set.name] = set;
37214 get: function(name)
37216 if (typeof(this.groups[name]) == 'undefined') {
37220 return this.groups[name] ;
37226 * Ext JS Library 1.1.1
37227 * Copyright(c) 2006-2007, Ext JS, LLC.
37229 * Originally Released Under LGPL - original licence link has changed is not relivant.
37232 * <script type="text/javascript">
37237 * @class Roo.bootstrap.SplitBar
37238 * @extends Roo.util.Observable
37239 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37243 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37244 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37245 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37246 split.minSize = 100;
37247 split.maxSize = 600;
37248 split.animate = true;
37249 split.on('moved', splitterMoved);
37252 * Create a new SplitBar
37253 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37254 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37255 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37256 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37257 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37258 position of the SplitBar).
37260 Roo.bootstrap.SplitBar = function(cfg){
37265 // dragElement : elm
37266 // resizingElement: el,
37268 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37269 // placement : Roo.bootstrap.SplitBar.LEFT ,
37270 // existingProxy ???
37273 this.el = Roo.get(cfg.dragElement, true);
37274 this.el.dom.unselectable = "on";
37276 this.resizingEl = Roo.get(cfg.resizingElement, true);
37280 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37281 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37284 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37287 * The minimum size of the resizing element. (Defaults to 0)
37293 * The maximum size of the resizing element. (Defaults to 2000)
37296 this.maxSize = 2000;
37299 * Whether to animate the transition to the new size
37302 this.animate = false;
37305 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37308 this.useShim = false;
37313 if(!cfg.existingProxy){
37315 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37317 this.proxy = Roo.get(cfg.existingProxy).dom;
37320 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37323 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37326 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37329 this.dragSpecs = {};
37332 * @private The adapter to use to positon and resize elements
37334 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37335 this.adapter.init(this);
37337 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37339 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37340 this.el.addClass("roo-splitbar-h");
37343 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37344 this.el.addClass("roo-splitbar-v");
37350 * Fires when the splitter is moved (alias for {@link #event-moved})
37351 * @param {Roo.bootstrap.SplitBar} this
37352 * @param {Number} newSize the new width or height
37357 * Fires when the splitter is moved
37358 * @param {Roo.bootstrap.SplitBar} this
37359 * @param {Number} newSize the new width or height
37363 * @event beforeresize
37364 * Fires before the splitter is dragged
37365 * @param {Roo.bootstrap.SplitBar} this
37367 "beforeresize" : true,
37369 "beforeapply" : true
37372 Roo.util.Observable.call(this);
37375 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37376 onStartProxyDrag : function(x, y){
37377 this.fireEvent("beforeresize", this);
37379 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37381 o.enableDisplayMode("block");
37382 // all splitbars share the same overlay
37383 Roo.bootstrap.SplitBar.prototype.overlay = o;
37385 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37386 this.overlay.show();
37387 Roo.get(this.proxy).setDisplayed("block");
37388 var size = this.adapter.getElementSize(this);
37389 this.activeMinSize = this.getMinimumSize();;
37390 this.activeMaxSize = this.getMaximumSize();;
37391 var c1 = size - this.activeMinSize;
37392 var c2 = Math.max(this.activeMaxSize - size, 0);
37393 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37394 this.dd.resetConstraints();
37395 this.dd.setXConstraint(
37396 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37397 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37399 this.dd.setYConstraint(0, 0);
37401 this.dd.resetConstraints();
37402 this.dd.setXConstraint(0, 0);
37403 this.dd.setYConstraint(
37404 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37405 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37408 this.dragSpecs.startSize = size;
37409 this.dragSpecs.startPoint = [x, y];
37410 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37414 * @private Called after the drag operation by the DDProxy
37416 onEndProxyDrag : function(e){
37417 Roo.get(this.proxy).setDisplayed(false);
37418 var endPoint = Roo.lib.Event.getXY(e);
37420 this.overlay.hide();
37423 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37424 newSize = this.dragSpecs.startSize +
37425 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37426 endPoint[0] - this.dragSpecs.startPoint[0] :
37427 this.dragSpecs.startPoint[0] - endPoint[0]
37430 newSize = this.dragSpecs.startSize +
37431 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37432 endPoint[1] - this.dragSpecs.startPoint[1] :
37433 this.dragSpecs.startPoint[1] - endPoint[1]
37436 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37437 if(newSize != this.dragSpecs.startSize){
37438 if(this.fireEvent('beforeapply', this, newSize) !== false){
37439 this.adapter.setElementSize(this, newSize);
37440 this.fireEvent("moved", this, newSize);
37441 this.fireEvent("resize", this, newSize);
37447 * Get the adapter this SplitBar uses
37448 * @return The adapter object
37450 getAdapter : function(){
37451 return this.adapter;
37455 * Set the adapter this SplitBar uses
37456 * @param {Object} adapter A SplitBar adapter object
37458 setAdapter : function(adapter){
37459 this.adapter = adapter;
37460 this.adapter.init(this);
37464 * Gets the minimum size for the resizing element
37465 * @return {Number} The minimum size
37467 getMinimumSize : function(){
37468 return this.minSize;
37472 * Sets the minimum size for the resizing element
37473 * @param {Number} minSize The minimum size
37475 setMinimumSize : function(minSize){
37476 this.minSize = minSize;
37480 * Gets the maximum size for the resizing element
37481 * @return {Number} The maximum size
37483 getMaximumSize : function(){
37484 return this.maxSize;
37488 * Sets the maximum size for the resizing element
37489 * @param {Number} maxSize The maximum size
37491 setMaximumSize : function(maxSize){
37492 this.maxSize = maxSize;
37496 * Sets the initialize size for the resizing element
37497 * @param {Number} size The initial size
37499 setCurrentSize : function(size){
37500 var oldAnimate = this.animate;
37501 this.animate = false;
37502 this.adapter.setElementSize(this, size);
37503 this.animate = oldAnimate;
37507 * Destroy this splitbar.
37508 * @param {Boolean} removeEl True to remove the element
37510 destroy : function(removeEl){
37512 this.shim.remove();
37515 this.proxy.parentNode.removeChild(this.proxy);
37523 * @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.
37525 Roo.bootstrap.SplitBar.createProxy = function(dir){
37526 var proxy = new Roo.Element(document.createElement("div"));
37527 proxy.unselectable();
37528 var cls = 'roo-splitbar-proxy';
37529 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37530 document.body.appendChild(proxy.dom);
37535 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37536 * Default Adapter. It assumes the splitter and resizing element are not positioned
37537 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37539 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37542 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37543 // do nothing for now
37544 init : function(s){
37548 * Called before drag operations to get the current size of the resizing element.
37549 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37551 getElementSize : function(s){
37552 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37553 return s.resizingEl.getWidth();
37555 return s.resizingEl.getHeight();
37560 * Called after drag operations to set the size of the resizing element.
37561 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37562 * @param {Number} newSize The new size to set
37563 * @param {Function} onComplete A function to be invoked when resizing is complete
37565 setElementSize : function(s, newSize, onComplete){
37566 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37568 s.resizingEl.setWidth(newSize);
37570 onComplete(s, newSize);
37573 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37578 s.resizingEl.setHeight(newSize);
37580 onComplete(s, newSize);
37583 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37590 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37591 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37592 * Adapter that moves the splitter element to align with the resized sizing element.
37593 * Used with an absolute positioned SplitBar.
37594 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37595 * document.body, make sure you assign an id to the body element.
37597 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37598 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37599 this.container = Roo.get(container);
37602 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37603 init : function(s){
37604 this.basic.init(s);
37607 getElementSize : function(s){
37608 return this.basic.getElementSize(s);
37611 setElementSize : function(s, newSize, onComplete){
37612 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37615 moveSplitter : function(s){
37616 var yes = Roo.bootstrap.SplitBar;
37617 switch(s.placement){
37619 s.el.setX(s.resizingEl.getRight());
37622 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37625 s.el.setY(s.resizingEl.getBottom());
37628 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37635 * Orientation constant - Create a vertical SplitBar
37639 Roo.bootstrap.SplitBar.VERTICAL = 1;
37642 * Orientation constant - Create a horizontal SplitBar
37646 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37649 * Placement constant - The resizing element is to the left of the splitter element
37653 Roo.bootstrap.SplitBar.LEFT = 1;
37656 * Placement constant - The resizing element is to the right of the splitter element
37660 Roo.bootstrap.SplitBar.RIGHT = 2;
37663 * Placement constant - The resizing element is positioned above the splitter element
37667 Roo.bootstrap.SplitBar.TOP = 3;
37670 * Placement constant - The resizing element is positioned under splitter element
37674 Roo.bootstrap.SplitBar.BOTTOM = 4;
37675 Roo.namespace("Roo.bootstrap.layout");/*
37677 * Ext JS Library 1.1.1
37678 * Copyright(c) 2006-2007, Ext JS, LLC.
37680 * Originally Released Under LGPL - original licence link has changed is not relivant.
37683 * <script type="text/javascript">
37687 * @class Roo.bootstrap.layout.Manager
37688 * @extends Roo.bootstrap.Component
37689 * Base class for layout managers.
37691 Roo.bootstrap.layout.Manager = function(config)
37693 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37699 /** false to disable window resize monitoring @type Boolean */
37700 this.monitorWindowResize = true;
37705 * Fires when a layout is performed.
37706 * @param {Roo.LayoutManager} this
37710 * @event regionresized
37711 * Fires when the user resizes a region.
37712 * @param {Roo.LayoutRegion} region The resized region
37713 * @param {Number} newSize The new size (width for east/west, height for north/south)
37715 "regionresized" : true,
37717 * @event regioncollapsed
37718 * Fires when a region is collapsed.
37719 * @param {Roo.LayoutRegion} region The collapsed region
37721 "regioncollapsed" : true,
37723 * @event regionexpanded
37724 * Fires when a region is expanded.
37725 * @param {Roo.LayoutRegion} region The expanded region
37727 "regionexpanded" : true
37729 this.updating = false;
37732 this.el = Roo.get(config.el);
37738 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37743 monitorWindowResize : true,
37749 onRender : function(ct, position)
37752 this.el = Roo.get(ct);
37755 //this.fireEvent('render',this);
37759 initEvents: function()
37763 // ie scrollbar fix
37764 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37765 document.body.scroll = "no";
37766 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37767 this.el.position('relative');
37769 this.id = this.el.id;
37770 this.el.addClass("roo-layout-container");
37771 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37772 if(this.el.dom != document.body ) {
37773 this.el.on('resize', this.layout,this);
37774 this.el.on('show', this.layout,this);
37780 * Returns true if this layout is currently being updated
37781 * @return {Boolean}
37783 isUpdating : function(){
37784 return this.updating;
37788 * Suspend the LayoutManager from doing auto-layouts while
37789 * making multiple add or remove calls
37791 beginUpdate : function(){
37792 this.updating = true;
37796 * Restore auto-layouts and optionally disable the manager from performing a layout
37797 * @param {Boolean} noLayout true to disable a layout update
37799 endUpdate : function(noLayout){
37800 this.updating = false;
37806 layout: function(){
37810 onRegionResized : function(region, newSize){
37811 this.fireEvent("regionresized", region, newSize);
37815 onRegionCollapsed : function(region){
37816 this.fireEvent("regioncollapsed", region);
37819 onRegionExpanded : function(region){
37820 this.fireEvent("regionexpanded", region);
37824 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37825 * performs box-model adjustments.
37826 * @return {Object} The size as an object {width: (the width), height: (the height)}
37828 getViewSize : function()
37831 if(this.el.dom != document.body){
37832 size = this.el.getSize();
37834 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37836 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37837 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37842 * Returns the Element this layout is bound to.
37843 * @return {Roo.Element}
37845 getEl : function(){
37850 * Returns the specified region.
37851 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37852 * @return {Roo.LayoutRegion}
37854 getRegion : function(target){
37855 return this.regions[target.toLowerCase()];
37858 onWindowResize : function(){
37859 if(this.monitorWindowResize){
37866 * Ext JS Library 1.1.1
37867 * Copyright(c) 2006-2007, Ext JS, LLC.
37869 * Originally Released Under LGPL - original licence link has changed is not relivant.
37872 * <script type="text/javascript">
37875 * @class Roo.bootstrap.layout.Border
37876 * @extends Roo.bootstrap.layout.Manager
37877 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37878 * please see: examples/bootstrap/nested.html<br><br>
37880 <b>The container the layout is rendered into can be either the body element or any other element.
37881 If it is not the body element, the container needs to either be an absolute positioned element,
37882 or you will need to add "position:relative" to the css of the container. You will also need to specify
37883 the container size if it is not the body element.</b>
37886 * Create a new Border
37887 * @param {Object} config Configuration options
37889 Roo.bootstrap.layout.Border = function(config){
37890 config = config || {};
37891 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37895 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37896 if(config[region]){
37897 config[region].region = region;
37898 this.addRegion(config[region]);
37904 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37906 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37908 parent : false, // this might point to a 'nest' or a ???
37911 * Creates and adds a new region if it doesn't already exist.
37912 * @param {String} target The target region key (north, south, east, west or center).
37913 * @param {Object} config The regions config object
37914 * @return {BorderLayoutRegion} The new region
37916 addRegion : function(config)
37918 if(!this.regions[config.region]){
37919 var r = this.factory(config);
37920 this.bindRegion(r);
37922 return this.regions[config.region];
37926 bindRegion : function(r){
37927 this.regions[r.config.region] = r;
37929 r.on("visibilitychange", this.layout, this);
37930 r.on("paneladded", this.layout, this);
37931 r.on("panelremoved", this.layout, this);
37932 r.on("invalidated", this.layout, this);
37933 r.on("resized", this.onRegionResized, this);
37934 r.on("collapsed", this.onRegionCollapsed, this);
37935 r.on("expanded", this.onRegionExpanded, this);
37939 * Performs a layout update.
37941 layout : function()
37943 if(this.updating) {
37947 // render all the rebions if they have not been done alreayd?
37948 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37949 if(this.regions[region] && !this.regions[region].bodyEl){
37950 this.regions[region].onRender(this.el)
37954 var size = this.getViewSize();
37955 var w = size.width;
37956 var h = size.height;
37961 //var x = 0, y = 0;
37963 var rs = this.regions;
37964 var north = rs["north"];
37965 var south = rs["south"];
37966 var west = rs["west"];
37967 var east = rs["east"];
37968 var center = rs["center"];
37969 //if(this.hideOnLayout){ // not supported anymore
37970 //c.el.setStyle("display", "none");
37972 if(north && north.isVisible()){
37973 var b = north.getBox();
37974 var m = north.getMargins();
37975 b.width = w - (m.left+m.right);
37978 centerY = b.height + b.y + m.bottom;
37979 centerH -= centerY;
37980 north.updateBox(this.safeBox(b));
37982 if(south && south.isVisible()){
37983 var b = south.getBox();
37984 var m = south.getMargins();
37985 b.width = w - (m.left+m.right);
37987 var totalHeight = (b.height + m.top + m.bottom);
37988 b.y = h - totalHeight + m.top;
37989 centerH -= totalHeight;
37990 south.updateBox(this.safeBox(b));
37992 if(west && west.isVisible()){
37993 var b = west.getBox();
37994 var m = west.getMargins();
37995 b.height = centerH - (m.top+m.bottom);
37997 b.y = centerY + m.top;
37998 var totalWidth = (b.width + m.left + m.right);
37999 centerX += totalWidth;
38000 centerW -= totalWidth;
38001 west.updateBox(this.safeBox(b));
38003 if(east && east.isVisible()){
38004 var b = east.getBox();
38005 var m = east.getMargins();
38006 b.height = centerH - (m.top+m.bottom);
38007 var totalWidth = (b.width + m.left + m.right);
38008 b.x = w - totalWidth + m.left;
38009 b.y = centerY + m.top;
38010 centerW -= totalWidth;
38011 east.updateBox(this.safeBox(b));
38014 var m = center.getMargins();
38016 x: centerX + m.left,
38017 y: centerY + m.top,
38018 width: centerW - (m.left+m.right),
38019 height: centerH - (m.top+m.bottom)
38021 //if(this.hideOnLayout){
38022 //center.el.setStyle("display", "block");
38024 center.updateBox(this.safeBox(centerBox));
38027 this.fireEvent("layout", this);
38031 safeBox : function(box){
38032 box.width = Math.max(0, box.width);
38033 box.height = Math.max(0, box.height);
38038 * Adds a ContentPanel (or subclass) to this layout.
38039 * @param {String} target The target region key (north, south, east, west or center).
38040 * @param {Roo.ContentPanel} panel The panel to add
38041 * @return {Roo.ContentPanel} The added panel
38043 add : function(target, panel){
38045 target = target.toLowerCase();
38046 return this.regions[target].add(panel);
38050 * Remove a ContentPanel (or subclass) to this layout.
38051 * @param {String} target The target region key (north, south, east, west or center).
38052 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38053 * @return {Roo.ContentPanel} The removed panel
38055 remove : function(target, panel){
38056 target = target.toLowerCase();
38057 return this.regions[target].remove(panel);
38061 * Searches all regions for a panel with the specified id
38062 * @param {String} panelId
38063 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38065 findPanel : function(panelId){
38066 var rs = this.regions;
38067 for(var target in rs){
38068 if(typeof rs[target] != "function"){
38069 var p = rs[target].getPanel(panelId);
38079 * Searches all regions for a panel with the specified id and activates (shows) it.
38080 * @param {String/ContentPanel} panelId The panels id or the panel itself
38081 * @return {Roo.ContentPanel} The shown panel or null
38083 showPanel : function(panelId) {
38084 var rs = this.regions;
38085 for(var target in rs){
38086 var r = rs[target];
38087 if(typeof r != "function"){
38088 if(r.hasPanel(panelId)){
38089 return r.showPanel(panelId);
38097 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38098 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38101 restoreState : function(provider){
38103 provider = Roo.state.Manager;
38105 var sm = new Roo.LayoutStateManager();
38106 sm.init(this, provider);
38112 * Adds a xtype elements to the layout.
38116 xtype : 'ContentPanel',
38123 xtype : 'NestedLayoutPanel',
38129 items : [ ... list of content panels or nested layout panels.. ]
38133 * @param {Object} cfg Xtype definition of item to add.
38135 addxtype : function(cfg)
38137 // basically accepts a pannel...
38138 // can accept a layout region..!?!?
38139 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38142 // theory? children can only be panels??
38144 //if (!cfg.xtype.match(/Panel$/)) {
38149 if (typeof(cfg.region) == 'undefined') {
38150 Roo.log("Failed to add Panel, region was not set");
38154 var region = cfg.region;
38160 xitems = cfg.items;
38165 if ( region == 'center') {
38166 Roo.log("Center: " + cfg.title);
38172 case 'Content': // ContentPanel (el, cfg)
38173 case 'Scroll': // ContentPanel (el, cfg)
38175 cfg.autoCreate = cfg.autoCreate || true;
38176 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38178 // var el = this.el.createChild();
38179 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38182 this.add(region, ret);
38186 case 'TreePanel': // our new panel!
38187 cfg.el = this.el.createChild();
38188 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38189 this.add(region, ret);
38194 // create a new Layout (which is a Border Layout...
38196 var clayout = cfg.layout;
38197 clayout.el = this.el.createChild();
38198 clayout.items = clayout.items || [];
38202 // replace this exitems with the clayout ones..
38203 xitems = clayout.items;
38205 // force background off if it's in center...
38206 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38207 cfg.background = false;
38209 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38212 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38213 //console.log('adding nested layout panel ' + cfg.toSource());
38214 this.add(region, ret);
38215 nb = {}; /// find first...
38220 // needs grid and region
38222 //var el = this.getRegion(region).el.createChild();
38224 *var el = this.el.createChild();
38225 // create the grid first...
38226 cfg.grid.container = el;
38227 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38230 if (region == 'center' && this.active ) {
38231 cfg.background = false;
38234 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38236 this.add(region, ret);
38238 if (cfg.background) {
38239 // render grid on panel activation (if panel background)
38240 ret.on('activate', function(gp) {
38241 if (!gp.grid.rendered) {
38242 // gp.grid.render(el);
38246 // cfg.grid.render(el);
38252 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38253 // it was the old xcomponent building that caused this before.
38254 // espeically if border is the top element in the tree.
38264 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38266 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38267 this.add(region, ret);
38271 throw "Can not add '" + cfg.xtype + "' to Border";
38277 this.beginUpdate();
38281 Roo.each(xitems, function(i) {
38282 region = nb && i.region ? i.region : false;
38284 var add = ret.addxtype(i);
38287 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38288 if (!i.background) {
38289 abn[region] = nb[region] ;
38296 // make the last non-background panel active..
38297 //if (nb) { Roo.log(abn); }
38300 for(var r in abn) {
38301 region = this.getRegion(r);
38303 // tried using nb[r], but it does not work..
38305 region.showPanel(abn[r]);
38316 factory : function(cfg)
38319 var validRegions = Roo.bootstrap.layout.Border.regions;
38321 var target = cfg.region;
38324 var r = Roo.bootstrap.layout;
38328 return new r.North(cfg);
38330 return new r.South(cfg);
38332 return new r.East(cfg);
38334 return new r.West(cfg);
38336 return new r.Center(cfg);
38338 throw 'Layout region "'+target+'" not supported.';
38345 * Ext JS Library 1.1.1
38346 * Copyright(c) 2006-2007, Ext JS, LLC.
38348 * Originally Released Under LGPL - original licence link has changed is not relivant.
38351 * <script type="text/javascript">
38355 * @class Roo.bootstrap.layout.Basic
38356 * @extends Roo.util.Observable
38357 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38358 * and does not have a titlebar, tabs or any other features. All it does is size and position
38359 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38360 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38361 * @cfg {string} region the region that it inhabits..
38362 * @cfg {bool} skipConfig skip config?
38366 Roo.bootstrap.layout.Basic = function(config){
38368 this.mgr = config.mgr;
38370 this.position = config.region;
38372 var skipConfig = config.skipConfig;
38376 * @scope Roo.BasicLayoutRegion
38380 * @event beforeremove
38381 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38382 * @param {Roo.LayoutRegion} this
38383 * @param {Roo.ContentPanel} panel The panel
38384 * @param {Object} e The cancel event object
38386 "beforeremove" : true,
38388 * @event invalidated
38389 * Fires when the layout for this region is changed.
38390 * @param {Roo.LayoutRegion} this
38392 "invalidated" : true,
38394 * @event visibilitychange
38395 * Fires when this region is shown or hidden
38396 * @param {Roo.LayoutRegion} this
38397 * @param {Boolean} visibility true or false
38399 "visibilitychange" : true,
38401 * @event paneladded
38402 * Fires when a panel is added.
38403 * @param {Roo.LayoutRegion} this
38404 * @param {Roo.ContentPanel} panel The panel
38406 "paneladded" : true,
38408 * @event panelremoved
38409 * Fires when a panel is removed.
38410 * @param {Roo.LayoutRegion} this
38411 * @param {Roo.ContentPanel} panel The panel
38413 "panelremoved" : true,
38415 * @event beforecollapse
38416 * Fires when this region before collapse.
38417 * @param {Roo.LayoutRegion} this
38419 "beforecollapse" : true,
38422 * Fires when this region is collapsed.
38423 * @param {Roo.LayoutRegion} this
38425 "collapsed" : true,
38428 * Fires when this region is expanded.
38429 * @param {Roo.LayoutRegion} this
38434 * Fires when this region is slid into view.
38435 * @param {Roo.LayoutRegion} this
38437 "slideshow" : true,
38440 * Fires when this region slides out of view.
38441 * @param {Roo.LayoutRegion} this
38443 "slidehide" : true,
38445 * @event panelactivated
38446 * Fires when a panel is activated.
38447 * @param {Roo.LayoutRegion} this
38448 * @param {Roo.ContentPanel} panel The activated panel
38450 "panelactivated" : true,
38453 * Fires when the user resizes this region.
38454 * @param {Roo.LayoutRegion} this
38455 * @param {Number} newSize The new size (width for east/west, height for north/south)
38459 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38460 this.panels = new Roo.util.MixedCollection();
38461 this.panels.getKey = this.getPanelId.createDelegate(this);
38463 this.activePanel = null;
38464 // ensure listeners are added...
38466 if (config.listeners || config.events) {
38467 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38468 listeners : config.listeners || {},
38469 events : config.events || {}
38473 if(skipConfig !== true){
38474 this.applyConfig(config);
38478 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38480 getPanelId : function(p){
38484 applyConfig : function(config){
38485 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38486 this.config = config;
38491 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38492 * the width, for horizontal (north, south) the height.
38493 * @param {Number} newSize The new width or height
38495 resizeTo : function(newSize){
38496 var el = this.el ? this.el :
38497 (this.activePanel ? this.activePanel.getEl() : null);
38499 switch(this.position){
38502 el.setWidth(newSize);
38503 this.fireEvent("resized", this, newSize);
38507 el.setHeight(newSize);
38508 this.fireEvent("resized", this, newSize);
38514 getBox : function(){
38515 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38518 getMargins : function(){
38519 return this.margins;
38522 updateBox : function(box){
38524 var el = this.activePanel.getEl();
38525 el.dom.style.left = box.x + "px";
38526 el.dom.style.top = box.y + "px";
38527 this.activePanel.setSize(box.width, box.height);
38531 * Returns the container element for this region.
38532 * @return {Roo.Element}
38534 getEl : function(){
38535 return this.activePanel;
38539 * Returns true if this region is currently visible.
38540 * @return {Boolean}
38542 isVisible : function(){
38543 return this.activePanel ? true : false;
38546 setActivePanel : function(panel){
38547 panel = this.getPanel(panel);
38548 if(this.activePanel && this.activePanel != panel){
38549 this.activePanel.setActiveState(false);
38550 this.activePanel.getEl().setLeftTop(-10000,-10000);
38552 this.activePanel = panel;
38553 panel.setActiveState(true);
38555 panel.setSize(this.box.width, this.box.height);
38557 this.fireEvent("panelactivated", this, panel);
38558 this.fireEvent("invalidated");
38562 * Show the specified panel.
38563 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38564 * @return {Roo.ContentPanel} The shown panel or null
38566 showPanel : function(panel){
38567 panel = this.getPanel(panel);
38569 this.setActivePanel(panel);
38575 * Get the active panel for this region.
38576 * @return {Roo.ContentPanel} The active panel or null
38578 getActivePanel : function(){
38579 return this.activePanel;
38583 * Add the passed ContentPanel(s)
38584 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38585 * @return {Roo.ContentPanel} The panel added (if only one was added)
38587 add : function(panel){
38588 if(arguments.length > 1){
38589 for(var i = 0, len = arguments.length; i < len; i++) {
38590 this.add(arguments[i]);
38594 if(this.hasPanel(panel)){
38595 this.showPanel(panel);
38598 var el = panel.getEl();
38599 if(el.dom.parentNode != this.mgr.el.dom){
38600 this.mgr.el.dom.appendChild(el.dom);
38602 if(panel.setRegion){
38603 panel.setRegion(this);
38605 this.panels.add(panel);
38606 el.setStyle("position", "absolute");
38607 if(!panel.background){
38608 this.setActivePanel(panel);
38609 if(this.config.initialSize && this.panels.getCount()==1){
38610 this.resizeTo(this.config.initialSize);
38613 this.fireEvent("paneladded", this, panel);
38618 * Returns true if the panel is in this region.
38619 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38620 * @return {Boolean}
38622 hasPanel : function(panel){
38623 if(typeof panel == "object"){ // must be panel obj
38624 panel = panel.getId();
38626 return this.getPanel(panel) ? true : false;
38630 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38631 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38632 * @param {Boolean} preservePanel Overrides the config preservePanel option
38633 * @return {Roo.ContentPanel} The panel that was removed
38635 remove : function(panel, preservePanel){
38636 panel = this.getPanel(panel);
38641 this.fireEvent("beforeremove", this, panel, e);
38642 if(e.cancel === true){
38645 var panelId = panel.getId();
38646 this.panels.removeKey(panelId);
38651 * Returns the panel specified or null if it's not in this region.
38652 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38653 * @return {Roo.ContentPanel}
38655 getPanel : function(id){
38656 if(typeof id == "object"){ // must be panel obj
38659 return this.panels.get(id);
38663 * Returns this regions position (north/south/east/west/center).
38666 getPosition: function(){
38667 return this.position;
38671 * Ext JS Library 1.1.1
38672 * Copyright(c) 2006-2007, Ext JS, LLC.
38674 * Originally Released Under LGPL - original licence link has changed is not relivant.
38677 * <script type="text/javascript">
38681 * @class Roo.bootstrap.layout.Region
38682 * @extends Roo.bootstrap.layout.Basic
38683 * This class represents a region in a layout manager.
38685 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38686 * @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})
38687 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38688 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38689 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38690 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38691 * @cfg {String} title The title for the region (overrides panel titles)
38692 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38693 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38694 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38695 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38696 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38697 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38698 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38699 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38700 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38701 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38703 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38704 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38705 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38706 * @cfg {Number} width For East/West panels
38707 * @cfg {Number} height For North/South panels
38708 * @cfg {Boolean} split To show the splitter
38709 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38711 * @cfg {string} cls Extra CSS classes to add to region
38713 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38714 * @cfg {string} region the region that it inhabits..
38717 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38718 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38720 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38721 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38722 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38724 Roo.bootstrap.layout.Region = function(config)
38726 this.applyConfig(config);
38728 var mgr = config.mgr;
38729 var pos = config.region;
38730 config.skipConfig = true;
38731 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38734 this.onRender(mgr.el);
38737 this.visible = true;
38738 this.collapsed = false;
38739 this.unrendered_panels = [];
38742 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38744 position: '', // set by wrapper (eg. north/south etc..)
38745 unrendered_panels : null, // unrendered panels.
38747 tabPosition : false,
38749 mgr: false, // points to 'Border'
38752 createBody : function(){
38753 /** This region's body element
38754 * @type Roo.Element */
38755 this.bodyEl = this.el.createChild({
38757 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38761 onRender: function(ctr, pos)
38763 var dh = Roo.DomHelper;
38764 /** This region's container element
38765 * @type Roo.Element */
38766 this.el = dh.append(ctr.dom, {
38768 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38770 /** This region's title element
38771 * @type Roo.Element */
38773 this.titleEl = dh.append(this.el.dom, {
38775 unselectable: "on",
38776 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38778 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38779 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38783 this.titleEl.enableDisplayMode();
38784 /** This region's title text element
38785 * @type HTMLElement */
38786 this.titleTextEl = this.titleEl.dom.firstChild;
38787 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38789 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38790 this.closeBtn.enableDisplayMode();
38791 this.closeBtn.on("click", this.closeClicked, this);
38792 this.closeBtn.hide();
38794 this.createBody(this.config);
38795 if(this.config.hideWhenEmpty){
38797 this.on("paneladded", this.validateVisibility, this);
38798 this.on("panelremoved", this.validateVisibility, this);
38800 if(this.autoScroll){
38801 this.bodyEl.setStyle("overflow", "auto");
38803 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38805 //if(c.titlebar !== false){
38806 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38807 this.titleEl.hide();
38809 this.titleEl.show();
38810 if(this.config.title){
38811 this.titleTextEl.innerHTML = this.config.title;
38815 if(this.config.collapsed){
38816 this.collapse(true);
38818 if(this.config.hidden){
38822 if (this.unrendered_panels && this.unrendered_panels.length) {
38823 for (var i =0;i< this.unrendered_panels.length; i++) {
38824 this.add(this.unrendered_panels[i]);
38826 this.unrendered_panels = null;
38832 applyConfig : function(c)
38835 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38836 var dh = Roo.DomHelper;
38837 if(c.titlebar !== false){
38838 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38839 this.collapseBtn.on("click", this.collapse, this);
38840 this.collapseBtn.enableDisplayMode();
38842 if(c.showPin === true || this.showPin){
38843 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38844 this.stickBtn.enableDisplayMode();
38845 this.stickBtn.on("click", this.expand, this);
38846 this.stickBtn.hide();
38851 /** This region's collapsed element
38852 * @type Roo.Element */
38855 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38856 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38859 if(c.floatable !== false){
38860 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38861 this.collapsedEl.on("click", this.collapseClick, this);
38864 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38865 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38866 id: "message", unselectable: "on", style:{"float":"left"}});
38867 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38869 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38870 this.expandBtn.on("click", this.expand, this);
38874 if(this.collapseBtn){
38875 this.collapseBtn.setVisible(c.collapsible == true);
38878 this.cmargins = c.cmargins || this.cmargins ||
38879 (this.position == "west" || this.position == "east" ?
38880 {top: 0, left: 2, right:2, bottom: 0} :
38881 {top: 2, left: 0, right:0, bottom: 2});
38883 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38886 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38888 this.autoScroll = c.autoScroll || false;
38893 this.duration = c.duration || .30;
38894 this.slideDuration = c.slideDuration || .45;
38899 * Returns true if this region is currently visible.
38900 * @return {Boolean}
38902 isVisible : function(){
38903 return this.visible;
38907 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38908 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38910 //setCollapsedTitle : function(title){
38911 // title = title || " ";
38912 // if(this.collapsedTitleTextEl){
38913 // this.collapsedTitleTextEl.innerHTML = title;
38917 getBox : function(){
38919 // if(!this.collapsed){
38920 b = this.el.getBox(false, true);
38922 // b = this.collapsedEl.getBox(false, true);
38927 getMargins : function(){
38928 return this.margins;
38929 //return this.collapsed ? this.cmargins : this.margins;
38932 highlight : function(){
38933 this.el.addClass("x-layout-panel-dragover");
38936 unhighlight : function(){
38937 this.el.removeClass("x-layout-panel-dragover");
38940 updateBox : function(box)
38942 if (!this.bodyEl) {
38943 return; // not rendered yet..
38947 if(!this.collapsed){
38948 this.el.dom.style.left = box.x + "px";
38949 this.el.dom.style.top = box.y + "px";
38950 this.updateBody(box.width, box.height);
38952 this.collapsedEl.dom.style.left = box.x + "px";
38953 this.collapsedEl.dom.style.top = box.y + "px";
38954 this.collapsedEl.setSize(box.width, box.height);
38957 this.tabs.autoSizeTabs();
38961 updateBody : function(w, h)
38964 this.el.setWidth(w);
38965 w -= this.el.getBorderWidth("rl");
38966 if(this.config.adjustments){
38967 w += this.config.adjustments[0];
38970 if(h !== null && h > 0){
38971 this.el.setHeight(h);
38972 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38973 h -= this.el.getBorderWidth("tb");
38974 if(this.config.adjustments){
38975 h += this.config.adjustments[1];
38977 this.bodyEl.setHeight(h);
38979 h = this.tabs.syncHeight(h);
38982 if(this.panelSize){
38983 w = w !== null ? w : this.panelSize.width;
38984 h = h !== null ? h : this.panelSize.height;
38986 if(this.activePanel){
38987 var el = this.activePanel.getEl();
38988 w = w !== null ? w : el.getWidth();
38989 h = h !== null ? h : el.getHeight();
38990 this.panelSize = {width: w, height: h};
38991 this.activePanel.setSize(w, h);
38993 if(Roo.isIE && this.tabs){
38994 this.tabs.el.repaint();
38999 * Returns the container element for this region.
39000 * @return {Roo.Element}
39002 getEl : function(){
39007 * Hides this region.
39010 //if(!this.collapsed){
39011 this.el.dom.style.left = "-2000px";
39014 // this.collapsedEl.dom.style.left = "-2000px";
39015 // this.collapsedEl.hide();
39017 this.visible = false;
39018 this.fireEvent("visibilitychange", this, false);
39022 * Shows this region if it was previously hidden.
39025 //if(!this.collapsed){
39028 // this.collapsedEl.show();
39030 this.visible = true;
39031 this.fireEvent("visibilitychange", this, true);
39034 closeClicked : function(){
39035 if(this.activePanel){
39036 this.remove(this.activePanel);
39040 collapseClick : function(e){
39042 e.stopPropagation();
39045 e.stopPropagation();
39051 * Collapses this region.
39052 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39055 collapse : function(skipAnim, skipCheck = false){
39056 if(this.collapsed) {
39060 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39062 this.collapsed = true;
39064 this.split.el.hide();
39066 if(this.config.animate && skipAnim !== true){
39067 this.fireEvent("invalidated", this);
39068 this.animateCollapse();
39070 this.el.setLocation(-20000,-20000);
39072 this.collapsedEl.show();
39073 this.fireEvent("collapsed", this);
39074 this.fireEvent("invalidated", this);
39080 animateCollapse : function(){
39085 * Expands this region if it was previously collapsed.
39086 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39087 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39090 expand : function(e, skipAnim){
39092 e.stopPropagation();
39094 if(!this.collapsed || this.el.hasActiveFx()) {
39098 this.afterSlideIn();
39101 this.collapsed = false;
39102 if(this.config.animate && skipAnim !== true){
39103 this.animateExpand();
39107 this.split.el.show();
39109 this.collapsedEl.setLocation(-2000,-2000);
39110 this.collapsedEl.hide();
39111 this.fireEvent("invalidated", this);
39112 this.fireEvent("expanded", this);
39116 animateExpand : function(){
39120 initTabs : function()
39122 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39124 var ts = new Roo.bootstrap.panel.Tabs({
39125 el: this.bodyEl.dom,
39127 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39128 disableTooltips: this.config.disableTabTips,
39129 toolbar : this.config.toolbar
39132 if(this.config.hideTabs){
39133 ts.stripWrap.setDisplayed(false);
39136 ts.resizeTabs = this.config.resizeTabs === true;
39137 ts.minTabWidth = this.config.minTabWidth || 40;
39138 ts.maxTabWidth = this.config.maxTabWidth || 250;
39139 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39140 ts.monitorResize = false;
39141 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39142 ts.bodyEl.addClass('roo-layout-tabs-body');
39143 this.panels.each(this.initPanelAsTab, this);
39146 initPanelAsTab : function(panel){
39147 var ti = this.tabs.addTab(
39151 this.config.closeOnTab && panel.isClosable(),
39154 if(panel.tabTip !== undefined){
39155 ti.setTooltip(panel.tabTip);
39157 ti.on("activate", function(){
39158 this.setActivePanel(panel);
39161 if(this.config.closeOnTab){
39162 ti.on("beforeclose", function(t, e){
39164 this.remove(panel);
39168 panel.tabItem = ti;
39173 updatePanelTitle : function(panel, title)
39175 if(this.activePanel == panel){
39176 this.updateTitle(title);
39179 var ti = this.tabs.getTab(panel.getEl().id);
39181 if(panel.tabTip !== undefined){
39182 ti.setTooltip(panel.tabTip);
39187 updateTitle : function(title){
39188 if(this.titleTextEl && !this.config.title){
39189 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39193 setActivePanel : function(panel)
39195 panel = this.getPanel(panel);
39196 if(this.activePanel && this.activePanel != panel){
39197 if(this.activePanel.setActiveState(false) === false){
39201 this.activePanel = panel;
39202 panel.setActiveState(true);
39203 if(this.panelSize){
39204 panel.setSize(this.panelSize.width, this.panelSize.height);
39207 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39209 this.updateTitle(panel.getTitle());
39211 this.fireEvent("invalidated", this);
39213 this.fireEvent("panelactivated", this, panel);
39217 * Shows the specified panel.
39218 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39219 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39221 showPanel : function(panel)
39223 panel = this.getPanel(panel);
39226 var tab = this.tabs.getTab(panel.getEl().id);
39227 if(tab.isHidden()){
39228 this.tabs.unhideTab(tab.id);
39232 this.setActivePanel(panel);
39239 * Get the active panel for this region.
39240 * @return {Roo.ContentPanel} The active panel or null
39242 getActivePanel : function(){
39243 return this.activePanel;
39246 validateVisibility : function(){
39247 if(this.panels.getCount() < 1){
39248 this.updateTitle(" ");
39249 this.closeBtn.hide();
39252 if(!this.isVisible()){
39259 * Adds the passed ContentPanel(s) to this region.
39260 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39261 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39263 add : function(panel)
39265 if(arguments.length > 1){
39266 for(var i = 0, len = arguments.length; i < len; i++) {
39267 this.add(arguments[i]);
39272 // if we have not been rendered yet, then we can not really do much of this..
39273 if (!this.bodyEl) {
39274 this.unrendered_panels.push(panel);
39281 if(this.hasPanel(panel)){
39282 this.showPanel(panel);
39285 panel.setRegion(this);
39286 this.panels.add(panel);
39287 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39288 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39289 // and hide them... ???
39290 this.bodyEl.dom.appendChild(panel.getEl().dom);
39291 if(panel.background !== true){
39292 this.setActivePanel(panel);
39294 this.fireEvent("paneladded", this, panel);
39301 this.initPanelAsTab(panel);
39305 if(panel.background !== true){
39306 this.tabs.activate(panel.getEl().id);
39308 this.fireEvent("paneladded", this, panel);
39313 * Hides the tab for the specified panel.
39314 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39316 hidePanel : function(panel){
39317 if(this.tabs && (panel = this.getPanel(panel))){
39318 this.tabs.hideTab(panel.getEl().id);
39323 * Unhides the tab for a previously hidden panel.
39324 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39326 unhidePanel : function(panel){
39327 if(this.tabs && (panel = this.getPanel(panel))){
39328 this.tabs.unhideTab(panel.getEl().id);
39332 clearPanels : function(){
39333 while(this.panels.getCount() > 0){
39334 this.remove(this.panels.first());
39339 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39340 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39341 * @param {Boolean} preservePanel Overrides the config preservePanel option
39342 * @return {Roo.ContentPanel} The panel that was removed
39344 remove : function(panel, preservePanel)
39346 panel = this.getPanel(panel);
39351 this.fireEvent("beforeremove", this, panel, e);
39352 if(e.cancel === true){
39355 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39356 var panelId = panel.getId();
39357 this.panels.removeKey(panelId);
39359 document.body.appendChild(panel.getEl().dom);
39362 this.tabs.removeTab(panel.getEl().id);
39363 }else if (!preservePanel){
39364 this.bodyEl.dom.removeChild(panel.getEl().dom);
39366 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39367 var p = this.panels.first();
39368 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39369 tempEl.appendChild(p.getEl().dom);
39370 this.bodyEl.update("");
39371 this.bodyEl.dom.appendChild(p.getEl().dom);
39373 this.updateTitle(p.getTitle());
39375 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39376 this.setActivePanel(p);
39378 panel.setRegion(null);
39379 if(this.activePanel == panel){
39380 this.activePanel = null;
39382 if(this.config.autoDestroy !== false && preservePanel !== true){
39383 try{panel.destroy();}catch(e){}
39385 this.fireEvent("panelremoved", this, panel);
39390 * Returns the TabPanel component used by this region
39391 * @return {Roo.TabPanel}
39393 getTabs : function(){
39397 createTool : function(parentEl, className){
39398 var btn = Roo.DomHelper.append(parentEl, {
39400 cls: "x-layout-tools-button",
39403 cls: "roo-layout-tools-button-inner " + className,
39407 btn.addClassOnOver("roo-layout-tools-button-over");
39412 * Ext JS Library 1.1.1
39413 * Copyright(c) 2006-2007, Ext JS, LLC.
39415 * Originally Released Under LGPL - original licence link has changed is not relivant.
39418 * <script type="text/javascript">
39424 * @class Roo.SplitLayoutRegion
39425 * @extends Roo.LayoutRegion
39426 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39428 Roo.bootstrap.layout.Split = function(config){
39429 this.cursor = config.cursor;
39430 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39433 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39435 splitTip : "Drag to resize.",
39436 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39437 useSplitTips : false,
39439 applyConfig : function(config){
39440 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39443 onRender : function(ctr,pos) {
39445 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39446 if(!this.config.split){
39451 var splitEl = Roo.DomHelper.append(ctr.dom, {
39453 id: this.el.id + "-split",
39454 cls: "roo-layout-split roo-layout-split-"+this.position,
39457 /** The SplitBar for this region
39458 * @type Roo.SplitBar */
39459 // does not exist yet...
39460 Roo.log([this.position, this.orientation]);
39462 this.split = new Roo.bootstrap.SplitBar({
39463 dragElement : splitEl,
39464 resizingElement: this.el,
39465 orientation : this.orientation
39468 this.split.on("moved", this.onSplitMove, this);
39469 this.split.useShim = this.config.useShim === true;
39470 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39471 if(this.useSplitTips){
39472 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39474 //if(config.collapsible){
39475 // this.split.el.on("dblclick", this.collapse, this);
39478 if(typeof this.config.minSize != "undefined"){
39479 this.split.minSize = this.config.minSize;
39481 if(typeof this.config.maxSize != "undefined"){
39482 this.split.maxSize = this.config.maxSize;
39484 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39485 this.hideSplitter();
39490 getHMaxSize : function(){
39491 var cmax = this.config.maxSize || 10000;
39492 var center = this.mgr.getRegion("center");
39493 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39496 getVMaxSize : function(){
39497 var cmax = this.config.maxSize || 10000;
39498 var center = this.mgr.getRegion("center");
39499 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39502 onSplitMove : function(split, newSize){
39503 this.fireEvent("resized", this, newSize);
39507 * Returns the {@link Roo.SplitBar} for this region.
39508 * @return {Roo.SplitBar}
39510 getSplitBar : function(){
39515 this.hideSplitter();
39516 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39519 hideSplitter : function(){
39521 this.split.el.setLocation(-2000,-2000);
39522 this.split.el.hide();
39528 this.split.el.show();
39530 Roo.bootstrap.layout.Split.superclass.show.call(this);
39533 beforeSlide: function(){
39534 if(Roo.isGecko){// firefox overflow auto bug workaround
39535 this.bodyEl.clip();
39537 this.tabs.bodyEl.clip();
39539 if(this.activePanel){
39540 this.activePanel.getEl().clip();
39542 if(this.activePanel.beforeSlide){
39543 this.activePanel.beforeSlide();
39549 afterSlide : function(){
39550 if(Roo.isGecko){// firefox overflow auto bug workaround
39551 this.bodyEl.unclip();
39553 this.tabs.bodyEl.unclip();
39555 if(this.activePanel){
39556 this.activePanel.getEl().unclip();
39557 if(this.activePanel.afterSlide){
39558 this.activePanel.afterSlide();
39564 initAutoHide : function(){
39565 if(this.autoHide !== false){
39566 if(!this.autoHideHd){
39567 var st = new Roo.util.DelayedTask(this.slideIn, this);
39568 this.autoHideHd = {
39569 "mouseout": function(e){
39570 if(!e.within(this.el, true)){
39574 "mouseover" : function(e){
39580 this.el.on(this.autoHideHd);
39584 clearAutoHide : function(){
39585 if(this.autoHide !== false){
39586 this.el.un("mouseout", this.autoHideHd.mouseout);
39587 this.el.un("mouseover", this.autoHideHd.mouseover);
39591 clearMonitor : function(){
39592 Roo.get(document).un("click", this.slideInIf, this);
39595 // these names are backwards but not changed for compat
39596 slideOut : function(){
39597 if(this.isSlid || this.el.hasActiveFx()){
39600 this.isSlid = true;
39601 if(this.collapseBtn){
39602 this.collapseBtn.hide();
39604 this.closeBtnState = this.closeBtn.getStyle('display');
39605 this.closeBtn.hide();
39607 this.stickBtn.show();
39610 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39611 this.beforeSlide();
39612 this.el.setStyle("z-index", 10001);
39613 this.el.slideIn(this.getSlideAnchor(), {
39614 callback: function(){
39616 this.initAutoHide();
39617 Roo.get(document).on("click", this.slideInIf, this);
39618 this.fireEvent("slideshow", this);
39625 afterSlideIn : function(){
39626 this.clearAutoHide();
39627 this.isSlid = false;
39628 this.clearMonitor();
39629 this.el.setStyle("z-index", "");
39630 if(this.collapseBtn){
39631 this.collapseBtn.show();
39633 this.closeBtn.setStyle('display', this.closeBtnState);
39635 this.stickBtn.hide();
39637 this.fireEvent("slidehide", this);
39640 slideIn : function(cb){
39641 if(!this.isSlid || this.el.hasActiveFx()){
39645 this.isSlid = false;
39646 this.beforeSlide();
39647 this.el.slideOut(this.getSlideAnchor(), {
39648 callback: function(){
39649 this.el.setLeftTop(-10000, -10000);
39651 this.afterSlideIn();
39659 slideInIf : function(e){
39660 if(!e.within(this.el)){
39665 animateCollapse : function(){
39666 this.beforeSlide();
39667 this.el.setStyle("z-index", 20000);
39668 var anchor = this.getSlideAnchor();
39669 this.el.slideOut(anchor, {
39670 callback : function(){
39671 this.el.setStyle("z-index", "");
39672 this.collapsedEl.slideIn(anchor, {duration:.3});
39674 this.el.setLocation(-10000,-10000);
39676 this.fireEvent("collapsed", this);
39683 animateExpand : function(){
39684 this.beforeSlide();
39685 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39686 this.el.setStyle("z-index", 20000);
39687 this.collapsedEl.hide({
39690 this.el.slideIn(this.getSlideAnchor(), {
39691 callback : function(){
39692 this.el.setStyle("z-index", "");
39695 this.split.el.show();
39697 this.fireEvent("invalidated", this);
39698 this.fireEvent("expanded", this);
39726 getAnchor : function(){
39727 return this.anchors[this.position];
39730 getCollapseAnchor : function(){
39731 return this.canchors[this.position];
39734 getSlideAnchor : function(){
39735 return this.sanchors[this.position];
39738 getAlignAdj : function(){
39739 var cm = this.cmargins;
39740 switch(this.position){
39756 getExpandAdj : function(){
39757 var c = this.collapsedEl, cm = this.cmargins;
39758 switch(this.position){
39760 return [-(cm.right+c.getWidth()+cm.left), 0];
39763 return [cm.right+c.getWidth()+cm.left, 0];
39766 return [0, -(cm.top+cm.bottom+c.getHeight())];
39769 return [0, cm.top+cm.bottom+c.getHeight()];
39775 * Ext JS Library 1.1.1
39776 * Copyright(c) 2006-2007, Ext JS, LLC.
39778 * Originally Released Under LGPL - original licence link has changed is not relivant.
39781 * <script type="text/javascript">
39784 * These classes are private internal classes
39786 Roo.bootstrap.layout.Center = function(config){
39787 config.region = "center";
39788 Roo.bootstrap.layout.Region.call(this, config);
39789 this.visible = true;
39790 this.minWidth = config.minWidth || 20;
39791 this.minHeight = config.minHeight || 20;
39794 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39796 // center panel can't be hidden
39800 // center panel can't be hidden
39803 getMinWidth: function(){
39804 return this.minWidth;
39807 getMinHeight: function(){
39808 return this.minHeight;
39822 Roo.bootstrap.layout.North = function(config)
39824 config.region = 'north';
39825 config.cursor = 'n-resize';
39827 Roo.bootstrap.layout.Split.call(this, config);
39831 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39832 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39833 this.split.el.addClass("roo-layout-split-v");
39835 //var size = config.initialSize || config.height;
39836 //if(this.el && typeof size != "undefined"){
39837 // this.el.setHeight(size);
39840 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39842 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39845 onRender : function(ctr, pos)
39847 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39848 var size = this.config.initialSize || this.config.height;
39849 if(this.el && typeof size != "undefined"){
39850 this.el.setHeight(size);
39855 getBox : function(){
39856 if(this.collapsed){
39857 return this.collapsedEl.getBox();
39859 var box = this.el.getBox();
39861 box.height += this.split.el.getHeight();
39866 updateBox : function(box){
39867 if(this.split && !this.collapsed){
39868 box.height -= this.split.el.getHeight();
39869 this.split.el.setLeft(box.x);
39870 this.split.el.setTop(box.y+box.height);
39871 this.split.el.setWidth(box.width);
39873 if(this.collapsed){
39874 this.updateBody(box.width, null);
39876 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39884 Roo.bootstrap.layout.South = function(config){
39885 config.region = 'south';
39886 config.cursor = 's-resize';
39887 Roo.bootstrap.layout.Split.call(this, config);
39889 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39890 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39891 this.split.el.addClass("roo-layout-split-v");
39896 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39897 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39899 onRender : function(ctr, pos)
39901 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39902 var size = this.config.initialSize || this.config.height;
39903 if(this.el && typeof size != "undefined"){
39904 this.el.setHeight(size);
39909 getBox : function(){
39910 if(this.collapsed){
39911 return this.collapsedEl.getBox();
39913 var box = this.el.getBox();
39915 var sh = this.split.el.getHeight();
39922 updateBox : function(box){
39923 if(this.split && !this.collapsed){
39924 var sh = this.split.el.getHeight();
39927 this.split.el.setLeft(box.x);
39928 this.split.el.setTop(box.y-sh);
39929 this.split.el.setWidth(box.width);
39931 if(this.collapsed){
39932 this.updateBody(box.width, null);
39934 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39938 Roo.bootstrap.layout.East = function(config){
39939 config.region = "east";
39940 config.cursor = "e-resize";
39941 Roo.bootstrap.layout.Split.call(this, config);
39943 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39944 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39945 this.split.el.addClass("roo-layout-split-h");
39949 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39950 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39952 onRender : function(ctr, pos)
39954 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39955 var size = this.config.initialSize || this.config.width;
39956 if(this.el && typeof size != "undefined"){
39957 this.el.setWidth(size);
39962 getBox : function(){
39963 if(this.collapsed){
39964 return this.collapsedEl.getBox();
39966 var box = this.el.getBox();
39968 var sw = this.split.el.getWidth();
39975 updateBox : function(box){
39976 if(this.split && !this.collapsed){
39977 var sw = this.split.el.getWidth();
39979 this.split.el.setLeft(box.x);
39980 this.split.el.setTop(box.y);
39981 this.split.el.setHeight(box.height);
39984 if(this.collapsed){
39985 this.updateBody(null, box.height);
39987 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39991 Roo.bootstrap.layout.West = function(config){
39992 config.region = "west";
39993 config.cursor = "w-resize";
39995 Roo.bootstrap.layout.Split.call(this, config);
39997 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39998 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39999 this.split.el.addClass("roo-layout-split-h");
40003 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40004 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40006 onRender: function(ctr, pos)
40008 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40009 var size = this.config.initialSize || this.config.width;
40010 if(typeof size != "undefined"){
40011 this.el.setWidth(size);
40015 getBox : function(){
40016 if(this.collapsed){
40017 return this.collapsedEl.getBox();
40019 var box = this.el.getBox();
40020 if (box.width == 0) {
40021 box.width = this.config.width; // kludge?
40024 box.width += this.split.el.getWidth();
40029 updateBox : function(box){
40030 if(this.split && !this.collapsed){
40031 var sw = this.split.el.getWidth();
40033 this.split.el.setLeft(box.x+box.width);
40034 this.split.el.setTop(box.y);
40035 this.split.el.setHeight(box.height);
40037 if(this.collapsed){
40038 this.updateBody(null, box.height);
40040 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40042 });Roo.namespace("Roo.bootstrap.panel");/*
40044 * Ext JS Library 1.1.1
40045 * Copyright(c) 2006-2007, Ext JS, LLC.
40047 * Originally Released Under LGPL - original licence link has changed is not relivant.
40050 * <script type="text/javascript">
40053 * @class Roo.ContentPanel
40054 * @extends Roo.util.Observable
40055 * A basic ContentPanel element.
40056 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40057 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40058 * @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
40059 * @cfg {Boolean} closable True if the panel can be closed/removed
40060 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40061 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40062 * @cfg {Toolbar} toolbar A toolbar for this panel
40063 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40064 * @cfg {String} title The title for this panel
40065 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40066 * @cfg {String} url Calls {@link #setUrl} with this value
40067 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40068 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40069 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40070 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40071 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40072 * @cfg {Boolean} badges render the badges
40073 * @cfg {String} cls extra classes to use
40074 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40077 * Create a new ContentPanel.
40078 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40079 * @param {String/Object} config A string to set only the title or a config object
40080 * @param {String} content (optional) Set the HTML content for this panel
40081 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40083 Roo.bootstrap.panel.Content = function( config){
40085 this.tpl = config.tpl || false;
40087 var el = config.el;
40088 var content = config.content;
40090 if(config.autoCreate){ // xtype is available if this is called from factory
40093 this.el = Roo.get(el);
40094 if(!this.el && config && config.autoCreate){
40095 if(typeof config.autoCreate == "object"){
40096 if(!config.autoCreate.id){
40097 config.autoCreate.id = config.id||el;
40099 this.el = Roo.DomHelper.append(document.body,
40100 config.autoCreate, true);
40104 cls: (config.cls || '') +
40105 (config.background ? ' bg-' + config.background : '') +
40106 " roo-layout-inactive-content",
40109 if (config.iframe) {
40113 style : 'border: 0px',
40114 src : 'about:blank'
40120 elcfg.html = config.html;
40124 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40125 if (config.iframe) {
40126 this.iframeEl = this.el.select('iframe',true).first();
40131 this.closable = false;
40132 this.loaded = false;
40133 this.active = false;
40136 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40138 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40140 this.wrapEl = this.el; //this.el.wrap();
40142 if (config.toolbar.items) {
40143 ti = config.toolbar.items ;
40144 delete config.toolbar.items ;
40148 this.toolbar.render(this.wrapEl, 'before');
40149 for(var i =0;i < ti.length;i++) {
40150 // Roo.log(['add child', items[i]]);
40151 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40153 this.toolbar.items = nitems;
40154 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40155 delete config.toolbar;
40159 // xtype created footer. - not sure if will work as we normally have to render first..
40160 if (this.footer && !this.footer.el && this.footer.xtype) {
40161 if (!this.wrapEl) {
40162 this.wrapEl = this.el.wrap();
40165 this.footer.container = this.wrapEl.createChild();
40167 this.footer = Roo.factory(this.footer, Roo);
40172 if(typeof config == "string"){
40173 this.title = config;
40175 Roo.apply(this, config);
40179 this.resizeEl = Roo.get(this.resizeEl, true);
40181 this.resizeEl = this.el;
40183 // handle view.xtype
40191 * Fires when this panel is activated.
40192 * @param {Roo.ContentPanel} this
40196 * @event deactivate
40197 * Fires when this panel is activated.
40198 * @param {Roo.ContentPanel} this
40200 "deactivate" : true,
40204 * Fires when this panel is resized if fitToFrame is true.
40205 * @param {Roo.ContentPanel} this
40206 * @param {Number} width The width after any component adjustments
40207 * @param {Number} height The height after any component adjustments
40213 * Fires when this tab is created
40214 * @param {Roo.ContentPanel} this
40220 * Fires when this content is scrolled
40221 * @param {Roo.ContentPanel} this
40222 * @param {Event} scrollEvent
40233 if(this.autoScroll && !this.iframe){
40234 this.resizeEl.setStyle("overflow", "auto");
40235 this.resizeEl.on('scroll', this.onScroll, this);
40237 // fix randome scrolling
40238 //this.el.on('scroll', function() {
40239 // Roo.log('fix random scolling');
40240 // this.scrollTo('top',0);
40243 content = content || this.content;
40245 this.setContent(content);
40247 if(config && config.url){
40248 this.setUrl(this.url, this.params, this.loadOnce);
40253 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40255 if (this.view && typeof(this.view.xtype) != 'undefined') {
40256 this.view.el = this.el.appendChild(document.createElement("div"));
40257 this.view = Roo.factory(this.view);
40258 this.view.render && this.view.render(false, '');
40262 this.fireEvent('render', this);
40265 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40275 /* Resize Element - use this to work out scroll etc. */
40278 setRegion : function(region){
40279 this.region = region;
40280 this.setActiveClass(region && !this.background);
40284 setActiveClass: function(state)
40287 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40288 this.el.setStyle('position','relative');
40290 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40291 this.el.setStyle('position', 'absolute');
40296 * Returns the toolbar for this Panel if one was configured.
40297 * @return {Roo.Toolbar}
40299 getToolbar : function(){
40300 return this.toolbar;
40303 setActiveState : function(active)
40305 this.active = active;
40306 this.setActiveClass(active);
40308 if(this.fireEvent("deactivate", this) === false){
40313 this.fireEvent("activate", this);
40317 * Updates this panel's element (not for iframe)
40318 * @param {String} content The new content
40319 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40321 setContent : function(content, loadScripts){
40326 this.el.update(content, loadScripts);
40329 ignoreResize : function(w, h){
40330 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40333 this.lastSize = {width: w, height: h};
40338 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40339 * @return {Roo.UpdateManager} The UpdateManager
40341 getUpdateManager : function(){
40345 return this.el.getUpdateManager();
40348 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40349 * Does not work with IFRAME contents
40350 * @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:
40353 url: "your-url.php",
40354 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40355 callback: yourFunction,
40356 scope: yourObject, //(optional scope)
40359 text: "Loading...",
40365 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40366 * 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.
40367 * @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}
40368 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40369 * @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.
40370 * @return {Roo.ContentPanel} this
40378 var um = this.el.getUpdateManager();
40379 um.update.apply(um, arguments);
40385 * 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.
40386 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40387 * @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)
40388 * @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)
40389 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40391 setUrl : function(url, params, loadOnce){
40393 this.iframeEl.dom.src = url;
40397 if(this.refreshDelegate){
40398 this.removeListener("activate", this.refreshDelegate);
40400 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40401 this.on("activate", this.refreshDelegate);
40402 return this.el.getUpdateManager();
40405 _handleRefresh : function(url, params, loadOnce){
40406 if(!loadOnce || !this.loaded){
40407 var updater = this.el.getUpdateManager();
40408 updater.update(url, params, this._setLoaded.createDelegate(this));
40412 _setLoaded : function(){
40413 this.loaded = true;
40417 * Returns this panel's id
40420 getId : function(){
40425 * Returns this panel's element - used by regiosn to add.
40426 * @return {Roo.Element}
40428 getEl : function(){
40429 return this.wrapEl || this.el;
40434 adjustForComponents : function(width, height)
40436 //Roo.log('adjustForComponents ');
40437 if(this.resizeEl != this.el){
40438 width -= this.el.getFrameWidth('lr');
40439 height -= this.el.getFrameWidth('tb');
40442 var te = this.toolbar.getEl();
40443 te.setWidth(width);
40444 height -= te.getHeight();
40447 var te = this.footer.getEl();
40448 te.setWidth(width);
40449 height -= te.getHeight();
40453 if(this.adjustments){
40454 width += this.adjustments[0];
40455 height += this.adjustments[1];
40457 return {"width": width, "height": height};
40460 setSize : function(width, height){
40461 if(this.fitToFrame && !this.ignoreResize(width, height)){
40462 if(this.fitContainer && this.resizeEl != this.el){
40463 this.el.setSize(width, height);
40465 var size = this.adjustForComponents(width, height);
40467 this.iframeEl.setSize(width,height);
40470 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40471 this.fireEvent('resize', this, size.width, size.height);
40478 * Returns this panel's title
40481 getTitle : function(){
40483 if (typeof(this.title) != 'object') {
40488 for (var k in this.title) {
40489 if (!this.title.hasOwnProperty(k)) {
40493 if (k.indexOf('-') >= 0) {
40494 var s = k.split('-');
40495 for (var i = 0; i<s.length; i++) {
40496 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40499 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40506 * Set this panel's title
40507 * @param {String} title
40509 setTitle : function(title){
40510 this.title = title;
40512 this.region.updatePanelTitle(this, title);
40517 * Returns true is this panel was configured to be closable
40518 * @return {Boolean}
40520 isClosable : function(){
40521 return this.closable;
40524 beforeSlide : function(){
40526 this.resizeEl.clip();
40529 afterSlide : function(){
40531 this.resizeEl.unclip();
40535 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40536 * Will fail silently if the {@link #setUrl} method has not been called.
40537 * This does not activate the panel, just updates its content.
40539 refresh : function(){
40540 if(this.refreshDelegate){
40541 this.loaded = false;
40542 this.refreshDelegate();
40547 * Destroys this panel
40549 destroy : function(){
40550 this.el.removeAllListeners();
40551 var tempEl = document.createElement("span");
40552 tempEl.appendChild(this.el.dom);
40553 tempEl.innerHTML = "";
40559 * form - if the content panel contains a form - this is a reference to it.
40560 * @type {Roo.form.Form}
40564 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40565 * This contains a reference to it.
40571 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40581 * @param {Object} cfg Xtype definition of item to add.
40585 getChildContainer: function () {
40586 return this.getEl();
40590 onScroll : function(e)
40592 this.fireEvent('scroll', this, e);
40597 var ret = new Roo.factory(cfg);
40602 if (cfg.xtype.match(/^Form$/)) {
40605 //if (this.footer) {
40606 // el = this.footer.container.insertSibling(false, 'before');
40608 el = this.el.createChild();
40611 this.form = new Roo.form.Form(cfg);
40614 if ( this.form.allItems.length) {
40615 this.form.render(el.dom);
40619 // should only have one of theses..
40620 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40621 // views.. should not be just added - used named prop 'view''
40623 cfg.el = this.el.appendChild(document.createElement("div"));
40626 var ret = new Roo.factory(cfg);
40628 ret.render && ret.render(false, ''); // render blank..
40638 * @class Roo.bootstrap.panel.Grid
40639 * @extends Roo.bootstrap.panel.Content
40641 * Create a new GridPanel.
40642 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40643 * @param {Object} config A the config object
40649 Roo.bootstrap.panel.Grid = function(config)
40653 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40654 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40656 config.el = this.wrapper;
40657 //this.el = this.wrapper;
40659 if (config.container) {
40660 // ctor'ed from a Border/panel.grid
40663 this.wrapper.setStyle("overflow", "hidden");
40664 this.wrapper.addClass('roo-grid-container');
40669 if(config.toolbar){
40670 var tool_el = this.wrapper.createChild();
40671 this.toolbar = Roo.factory(config.toolbar);
40673 if (config.toolbar.items) {
40674 ti = config.toolbar.items ;
40675 delete config.toolbar.items ;
40679 this.toolbar.render(tool_el);
40680 for(var i =0;i < ti.length;i++) {
40681 // Roo.log(['add child', items[i]]);
40682 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40684 this.toolbar.items = nitems;
40686 delete config.toolbar;
40689 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40690 config.grid.scrollBody = true;;
40691 config.grid.monitorWindowResize = false; // turn off autosizing
40692 config.grid.autoHeight = false;
40693 config.grid.autoWidth = false;
40695 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40697 if (config.background) {
40698 // render grid on panel activation (if panel background)
40699 this.on('activate', function(gp) {
40700 if (!gp.grid.rendered) {
40701 gp.grid.render(this.wrapper);
40702 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40707 this.grid.render(this.wrapper);
40708 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40711 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40712 // ??? needed ??? config.el = this.wrapper;
40717 // xtype created footer. - not sure if will work as we normally have to render first..
40718 if (this.footer && !this.footer.el && this.footer.xtype) {
40720 var ctr = this.grid.getView().getFooterPanel(true);
40721 this.footer.dataSource = this.grid.dataSource;
40722 this.footer = Roo.factory(this.footer, Roo);
40723 this.footer.render(ctr);
40733 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40734 getId : function(){
40735 return this.grid.id;
40739 * Returns the grid for this panel
40740 * @return {Roo.bootstrap.Table}
40742 getGrid : function(){
40746 setSize : function(width, height){
40747 if(!this.ignoreResize(width, height)){
40748 var grid = this.grid;
40749 var size = this.adjustForComponents(width, height);
40750 // tfoot is not a footer?
40753 var gridel = grid.getGridEl();
40754 gridel.setSize(size.width, size.height);
40756 var tbd = grid.getGridEl().select('tbody', true).first();
40757 var thd = grid.getGridEl().select('thead',true).first();
40758 var tbf= grid.getGridEl().select('tfoot', true).first();
40761 size.height -= tbf.getHeight();
40764 size.height -= thd.getHeight();
40767 tbd.setSize(size.width, size.height );
40768 // this is for the account management tab -seems to work there.
40769 var thd = grid.getGridEl().select('thead',true).first();
40771 // tbd.setSize(size.width, size.height - thd.getHeight());
40780 beforeSlide : function(){
40781 this.grid.getView().scroller.clip();
40784 afterSlide : function(){
40785 this.grid.getView().scroller.unclip();
40788 destroy : function(){
40789 this.grid.destroy();
40791 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40796 * @class Roo.bootstrap.panel.Nest
40797 * @extends Roo.bootstrap.panel.Content
40799 * Create a new Panel, that can contain a layout.Border.
40802 * @param {Roo.BorderLayout} layout The layout for this panel
40803 * @param {String/Object} config A string to set only the title or a config object
40805 Roo.bootstrap.panel.Nest = function(config)
40807 // construct with only one argument..
40808 /* FIXME - implement nicer consturctors
40809 if (layout.layout) {
40811 layout = config.layout;
40812 delete config.layout;
40814 if (layout.xtype && !layout.getEl) {
40815 // then layout needs constructing..
40816 layout = Roo.factory(layout, Roo);
40820 config.el = config.layout.getEl();
40822 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40824 config.layout.monitorWindowResize = false; // turn off autosizing
40825 this.layout = config.layout;
40826 this.layout.getEl().addClass("roo-layout-nested-layout");
40827 this.layout.parent = this;
40834 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40836 setSize : function(width, height){
40837 if(!this.ignoreResize(width, height)){
40838 var size = this.adjustForComponents(width, height);
40839 var el = this.layout.getEl();
40840 if (size.height < 1) {
40841 el.setWidth(size.width);
40843 el.setSize(size.width, size.height);
40845 var touch = el.dom.offsetWidth;
40846 this.layout.layout();
40847 // ie requires a double layout on the first pass
40848 if(Roo.isIE && !this.initialized){
40849 this.initialized = true;
40850 this.layout.layout();
40855 // activate all subpanels if not currently active..
40857 setActiveState : function(active){
40858 this.active = active;
40859 this.setActiveClass(active);
40862 this.fireEvent("deactivate", this);
40866 this.fireEvent("activate", this);
40867 // not sure if this should happen before or after..
40868 if (!this.layout) {
40869 return; // should not happen..
40872 for (var r in this.layout.regions) {
40873 reg = this.layout.getRegion(r);
40874 if (reg.getActivePanel()) {
40875 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40876 reg.setActivePanel(reg.getActivePanel());
40879 if (!reg.panels.length) {
40882 reg.showPanel(reg.getPanel(0));
40891 * Returns the nested BorderLayout for this panel
40892 * @return {Roo.BorderLayout}
40894 getLayout : function(){
40895 return this.layout;
40899 * Adds a xtype elements to the layout of the nested panel
40903 xtype : 'ContentPanel',
40910 xtype : 'NestedLayoutPanel',
40916 items : [ ... list of content panels or nested layout panels.. ]
40920 * @param {Object} cfg Xtype definition of item to add.
40922 addxtype : function(cfg) {
40923 return this.layout.addxtype(cfg);
40928 * Ext JS Library 1.1.1
40929 * Copyright(c) 2006-2007, Ext JS, LLC.
40931 * Originally Released Under LGPL - original licence link has changed is not relivant.
40934 * <script type="text/javascript">
40937 * @class Roo.TabPanel
40938 * @extends Roo.util.Observable
40939 * A lightweight tab container.
40943 // basic tabs 1, built from existing content
40944 var tabs = new Roo.TabPanel("tabs1");
40945 tabs.addTab("script", "View Script");
40946 tabs.addTab("markup", "View Markup");
40947 tabs.activate("script");
40949 // more advanced tabs, built from javascript
40950 var jtabs = new Roo.TabPanel("jtabs");
40951 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40953 // set up the UpdateManager
40954 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40955 var updater = tab2.getUpdateManager();
40956 updater.setDefaultUrl("ajax1.htm");
40957 tab2.on('activate', updater.refresh, updater, true);
40959 // Use setUrl for Ajax loading
40960 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40961 tab3.setUrl("ajax2.htm", null, true);
40964 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40967 jtabs.activate("jtabs-1");
40970 * Create a new TabPanel.
40971 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40972 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40974 Roo.bootstrap.panel.Tabs = function(config){
40976 * The container element for this TabPanel.
40977 * @type Roo.Element
40979 this.el = Roo.get(config.el);
40982 if(typeof config == "boolean"){
40983 this.tabPosition = config ? "bottom" : "top";
40985 Roo.apply(this, config);
40989 if(this.tabPosition == "bottom"){
40990 // if tabs are at the bottom = create the body first.
40991 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40992 this.el.addClass("roo-tabs-bottom");
40994 // next create the tabs holders
40996 if (this.tabPosition == "west"){
40998 var reg = this.region; // fake it..
41000 if (!reg.mgr.parent) {
41003 reg = reg.mgr.parent.region;
41005 Roo.log("got nest?");
41007 if (reg.mgr.getRegion('west')) {
41008 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41009 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41010 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41011 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41012 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41020 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41021 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41022 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41023 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41028 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41031 // finally - if tabs are at the top, then create the body last..
41032 if(this.tabPosition != "bottom"){
41033 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41034 * @type Roo.Element
41036 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41037 this.el.addClass("roo-tabs-top");
41041 this.bodyEl.setStyle("position", "relative");
41043 this.active = null;
41044 this.activateDelegate = this.activate.createDelegate(this);
41049 * Fires when the active tab changes
41050 * @param {Roo.TabPanel} this
41051 * @param {Roo.TabPanelItem} activePanel The new active tab
41055 * @event beforetabchange
41056 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41057 * @param {Roo.TabPanel} this
41058 * @param {Object} e Set cancel to true on this object to cancel the tab change
41059 * @param {Roo.TabPanelItem} tab The tab being changed to
41061 "beforetabchange" : true
41064 Roo.EventManager.onWindowResize(this.onResize, this);
41065 this.cpad = this.el.getPadding("lr");
41066 this.hiddenCount = 0;
41069 // toolbar on the tabbar support...
41070 if (this.toolbar) {
41071 alert("no toolbar support yet");
41072 this.toolbar = false;
41074 var tcfg = this.toolbar;
41075 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41076 this.toolbar = new Roo.Toolbar(tcfg);
41077 if (Roo.isSafari) {
41078 var tbl = tcfg.container.child('table', true);
41079 tbl.setAttribute('width', '100%');
41087 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41090 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41092 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41094 tabPosition : "top",
41096 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41098 currentTabWidth : 0,
41100 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41104 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41108 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41110 preferredTabWidth : 175,
41112 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41114 resizeTabs : false,
41116 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41118 monitorResize : true,
41120 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41122 toolbar : false, // set by caller..
41124 region : false, /// set by caller
41126 disableTooltips : true, // not used yet...
41129 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41130 * @param {String} id The id of the div to use <b>or create</b>
41131 * @param {String} text The text for the tab
41132 * @param {String} content (optional) Content to put in the TabPanelItem body
41133 * @param {Boolean} closable (optional) True to create a close icon on the tab
41134 * @return {Roo.TabPanelItem} The created TabPanelItem
41136 addTab : function(id, text, content, closable, tpl)
41138 var item = new Roo.bootstrap.panel.TabItem({
41142 closable : closable,
41145 this.addTabItem(item);
41147 item.setContent(content);
41153 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41154 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41155 * @return {Roo.TabPanelItem}
41157 getTab : function(id){
41158 return this.items[id];
41162 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41163 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41165 hideTab : function(id){
41166 var t = this.items[id];
41169 this.hiddenCount++;
41170 this.autoSizeTabs();
41175 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41176 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41178 unhideTab : function(id){
41179 var t = this.items[id];
41181 t.setHidden(false);
41182 this.hiddenCount--;
41183 this.autoSizeTabs();
41188 * Adds an existing {@link Roo.TabPanelItem}.
41189 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41191 addTabItem : function(item)
41193 this.items[item.id] = item;
41194 this.items.push(item);
41195 this.autoSizeTabs();
41196 // if(this.resizeTabs){
41197 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41198 // this.autoSizeTabs();
41200 // item.autoSize();
41205 * Removes a {@link Roo.TabPanelItem}.
41206 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41208 removeTab : function(id){
41209 var items = this.items;
41210 var tab = items[id];
41211 if(!tab) { return; }
41212 var index = items.indexOf(tab);
41213 if(this.active == tab && items.length > 1){
41214 var newTab = this.getNextAvailable(index);
41219 this.stripEl.dom.removeChild(tab.pnode.dom);
41220 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41221 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41223 items.splice(index, 1);
41224 delete this.items[tab.id];
41225 tab.fireEvent("close", tab);
41226 tab.purgeListeners();
41227 this.autoSizeTabs();
41230 getNextAvailable : function(start){
41231 var items = this.items;
41233 // look for a next tab that will slide over to
41234 // replace the one being removed
41235 while(index < items.length){
41236 var item = items[++index];
41237 if(item && !item.isHidden()){
41241 // if one isn't found select the previous tab (on the left)
41244 var item = items[--index];
41245 if(item && !item.isHidden()){
41253 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41254 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41256 disableTab : function(id){
41257 var tab = this.items[id];
41258 if(tab && this.active != tab){
41264 * Enables a {@link Roo.TabPanelItem} that is disabled.
41265 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41267 enableTab : function(id){
41268 var tab = this.items[id];
41273 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41274 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41275 * @return {Roo.TabPanelItem} The TabPanelItem.
41277 activate : function(id)
41279 //Roo.log('activite:' + id);
41281 var tab = this.items[id];
41285 if(tab == this.active || tab.disabled){
41289 this.fireEvent("beforetabchange", this, e, tab);
41290 if(e.cancel !== true && !tab.disabled){
41292 this.active.hide();
41294 this.active = this.items[id];
41295 this.active.show();
41296 this.fireEvent("tabchange", this, this.active);
41302 * Gets the active {@link Roo.TabPanelItem}.
41303 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41305 getActiveTab : function(){
41306 return this.active;
41310 * Updates the tab body element to fit the height of the container element
41311 * for overflow scrolling
41312 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41314 syncHeight : function(targetHeight){
41315 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41316 var bm = this.bodyEl.getMargins();
41317 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41318 this.bodyEl.setHeight(newHeight);
41322 onResize : function(){
41323 if(this.monitorResize){
41324 this.autoSizeTabs();
41329 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41331 beginUpdate : function(){
41332 this.updating = true;
41336 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41338 endUpdate : function(){
41339 this.updating = false;
41340 this.autoSizeTabs();
41344 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41346 autoSizeTabs : function()
41348 var count = this.items.length;
41349 var vcount = count - this.hiddenCount;
41352 this.stripEl.hide();
41354 this.stripEl.show();
41357 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41362 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41363 var availWidth = Math.floor(w / vcount);
41364 var b = this.stripBody;
41365 if(b.getWidth() > w){
41366 var tabs = this.items;
41367 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41368 if(availWidth < this.minTabWidth){
41369 /*if(!this.sleft){ // incomplete scrolling code
41370 this.createScrollButtons();
41373 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41376 if(this.currentTabWidth < this.preferredTabWidth){
41377 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41383 * Returns the number of tabs in this TabPanel.
41386 getCount : function(){
41387 return this.items.length;
41391 * Resizes all the tabs to the passed width
41392 * @param {Number} The new width
41394 setTabWidth : function(width){
41395 this.currentTabWidth = width;
41396 for(var i = 0, len = this.items.length; i < len; i++) {
41397 if(!this.items[i].isHidden()) {
41398 this.items[i].setWidth(width);
41404 * Destroys this TabPanel
41405 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41407 destroy : function(removeEl){
41408 Roo.EventManager.removeResizeListener(this.onResize, this);
41409 for(var i = 0, len = this.items.length; i < len; i++){
41410 this.items[i].purgeListeners();
41412 if(removeEl === true){
41413 this.el.update("");
41418 createStrip : function(container)
41420 var strip = document.createElement("nav");
41421 strip.className = Roo.bootstrap.version == 4 ?
41422 "navbar-light bg-light" :
41423 "navbar navbar-default"; //"x-tabs-wrap";
41424 container.appendChild(strip);
41428 createStripList : function(strip)
41430 // div wrapper for retard IE
41431 // returns the "tr" element.
41432 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41433 //'<div class="x-tabs-strip-wrap">'+
41434 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41435 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41436 return strip.firstChild; //.firstChild.firstChild.firstChild;
41438 createBody : function(container)
41440 var body = document.createElement("div");
41441 Roo.id(body, "tab-body");
41442 //Roo.fly(body).addClass("x-tabs-body");
41443 Roo.fly(body).addClass("tab-content");
41444 container.appendChild(body);
41447 createItemBody :function(bodyEl, id){
41448 var body = Roo.getDom(id);
41450 body = document.createElement("div");
41453 //Roo.fly(body).addClass("x-tabs-item-body");
41454 Roo.fly(body).addClass("tab-pane");
41455 bodyEl.insertBefore(body, bodyEl.firstChild);
41459 createStripElements : function(stripEl, text, closable, tpl)
41461 var td = document.createElement("li"); // was td..
41462 td.className = 'nav-item';
41464 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41467 stripEl.appendChild(td);
41469 td.className = "x-tabs-closable";
41470 if(!this.closeTpl){
41471 this.closeTpl = new Roo.Template(
41472 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41473 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41474 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41477 var el = this.closeTpl.overwrite(td, {"text": text});
41478 var close = el.getElementsByTagName("div")[0];
41479 var inner = el.getElementsByTagName("em")[0];
41480 return {"el": el, "close": close, "inner": inner};
41483 // not sure what this is..
41484 // if(!this.tabTpl){
41485 //this.tabTpl = new Roo.Template(
41486 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41487 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41489 // this.tabTpl = new Roo.Template(
41490 // '<a href="#">' +
41491 // '<span unselectable="on"' +
41492 // (this.disableTooltips ? '' : ' title="{text}"') +
41493 // ' >{text}</span></a>'
41499 var template = tpl || this.tabTpl || false;
41502 template = new Roo.Template(
41503 Roo.bootstrap.version == 4 ?
41505 '<a class="nav-link" href="#" unselectable="on"' +
41506 (this.disableTooltips ? '' : ' title="{text}"') +
41509 '<a class="nav-link" href="#">' +
41510 '<span unselectable="on"' +
41511 (this.disableTooltips ? '' : ' title="{text}"') +
41512 ' >{text}</span></a>'
41517 switch (typeof(template)) {
41521 template = new Roo.Template(template);
41527 var el = template.overwrite(td, {"text": text});
41529 var inner = el.getElementsByTagName("span")[0];
41531 return {"el": el, "inner": inner};
41539 * @class Roo.TabPanelItem
41540 * @extends Roo.util.Observable
41541 * Represents an individual item (tab plus body) in a TabPanel.
41542 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41543 * @param {String} id The id of this TabPanelItem
41544 * @param {String} text The text for the tab of this TabPanelItem
41545 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41547 Roo.bootstrap.panel.TabItem = function(config){
41549 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41550 * @type Roo.TabPanel
41552 this.tabPanel = config.panel;
41554 * The id for this TabPanelItem
41557 this.id = config.id;
41559 this.disabled = false;
41561 this.text = config.text;
41563 this.loaded = false;
41564 this.closable = config.closable;
41567 * The body element for this TabPanelItem.
41568 * @type Roo.Element
41570 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41571 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41572 this.bodyEl.setStyle("display", "block");
41573 this.bodyEl.setStyle("zoom", "1");
41574 //this.hideAction();
41576 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41578 this.el = Roo.get(els.el);
41579 this.inner = Roo.get(els.inner, true);
41580 this.textEl = Roo.bootstrap.version == 4 ?
41581 this.el : Roo.get(this.el.dom.firstChild, true);
41583 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41584 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41587 // this.el.on("mousedown", this.onTabMouseDown, this);
41588 this.el.on("click", this.onTabClick, this);
41590 if(config.closable){
41591 var c = Roo.get(els.close, true);
41592 c.dom.title = this.closeText;
41593 c.addClassOnOver("close-over");
41594 c.on("click", this.closeClick, this);
41600 * Fires when this tab becomes the active tab.
41601 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41602 * @param {Roo.TabPanelItem} this
41606 * @event beforeclose
41607 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41608 * @param {Roo.TabPanelItem} this
41609 * @param {Object} e Set cancel to true on this object to cancel the close.
41611 "beforeclose": true,
41614 * Fires when this tab is closed.
41615 * @param {Roo.TabPanelItem} this
41619 * @event deactivate
41620 * Fires when this tab is no longer the active tab.
41621 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41622 * @param {Roo.TabPanelItem} this
41624 "deactivate" : true
41626 this.hidden = false;
41628 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41631 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41633 purgeListeners : function(){
41634 Roo.util.Observable.prototype.purgeListeners.call(this);
41635 this.el.removeAllListeners();
41638 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41641 this.status_node.addClass("active");
41644 this.tabPanel.stripWrap.repaint();
41646 this.fireEvent("activate", this.tabPanel, this);
41650 * Returns true if this tab is the active tab.
41651 * @return {Boolean}
41653 isActive : function(){
41654 return this.tabPanel.getActiveTab() == this;
41658 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41661 this.status_node.removeClass("active");
41663 this.fireEvent("deactivate", this.tabPanel, this);
41666 hideAction : function(){
41667 this.bodyEl.hide();
41668 this.bodyEl.setStyle("position", "absolute");
41669 this.bodyEl.setLeft("-20000px");
41670 this.bodyEl.setTop("-20000px");
41673 showAction : function(){
41674 this.bodyEl.setStyle("position", "relative");
41675 this.bodyEl.setTop("");
41676 this.bodyEl.setLeft("");
41677 this.bodyEl.show();
41681 * Set the tooltip for the tab.
41682 * @param {String} tooltip The tab's tooltip
41684 setTooltip : function(text){
41685 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41686 this.textEl.dom.qtip = text;
41687 this.textEl.dom.removeAttribute('title');
41689 this.textEl.dom.title = text;
41693 onTabClick : function(e){
41694 e.preventDefault();
41695 this.tabPanel.activate(this.id);
41698 onTabMouseDown : function(e){
41699 e.preventDefault();
41700 this.tabPanel.activate(this.id);
41703 getWidth : function(){
41704 return this.inner.getWidth();
41707 setWidth : function(width){
41708 var iwidth = width - this.linode.getPadding("lr");
41709 this.inner.setWidth(iwidth);
41710 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41711 this.linode.setWidth(width);
41715 * Show or hide the tab
41716 * @param {Boolean} hidden True to hide or false to show.
41718 setHidden : function(hidden){
41719 this.hidden = hidden;
41720 this.linode.setStyle("display", hidden ? "none" : "");
41724 * Returns true if this tab is "hidden"
41725 * @return {Boolean}
41727 isHidden : function(){
41728 return this.hidden;
41732 * Returns the text for this tab
41735 getText : function(){
41739 autoSize : function(){
41740 //this.el.beginMeasure();
41741 this.textEl.setWidth(1);
41743 * #2804 [new] Tabs in Roojs
41744 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41746 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41747 //this.el.endMeasure();
41751 * Sets the text for the tab (Note: this also sets the tooltip text)
41752 * @param {String} text The tab's text and tooltip
41754 setText : function(text){
41756 this.textEl.update(text);
41757 this.setTooltip(text);
41758 //if(!this.tabPanel.resizeTabs){
41759 // this.autoSize();
41763 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41765 activate : function(){
41766 this.tabPanel.activate(this.id);
41770 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41772 disable : function(){
41773 if(this.tabPanel.active != this){
41774 this.disabled = true;
41775 this.status_node.addClass("disabled");
41780 * Enables this TabPanelItem if it was previously disabled.
41782 enable : function(){
41783 this.disabled = false;
41784 this.status_node.removeClass("disabled");
41788 * Sets the content for this TabPanelItem.
41789 * @param {String} content The content
41790 * @param {Boolean} loadScripts true to look for and load scripts
41792 setContent : function(content, loadScripts){
41793 this.bodyEl.update(content, loadScripts);
41797 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41798 * @return {Roo.UpdateManager} The UpdateManager
41800 getUpdateManager : function(){
41801 return this.bodyEl.getUpdateManager();
41805 * Set a URL to be used to load the content for this TabPanelItem.
41806 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41807 * @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)
41808 * @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)
41809 * @return {Roo.UpdateManager} The UpdateManager
41811 setUrl : function(url, params, loadOnce){
41812 if(this.refreshDelegate){
41813 this.un('activate', this.refreshDelegate);
41815 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41816 this.on("activate", this.refreshDelegate);
41817 return this.bodyEl.getUpdateManager();
41821 _handleRefresh : function(url, params, loadOnce){
41822 if(!loadOnce || !this.loaded){
41823 var updater = this.bodyEl.getUpdateManager();
41824 updater.update(url, params, this._setLoaded.createDelegate(this));
41829 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41830 * Will fail silently if the setUrl method has not been called.
41831 * This does not activate the panel, just updates its content.
41833 refresh : function(){
41834 if(this.refreshDelegate){
41835 this.loaded = false;
41836 this.refreshDelegate();
41841 _setLoaded : function(){
41842 this.loaded = true;
41846 closeClick : function(e){
41849 this.fireEvent("beforeclose", this, o);
41850 if(o.cancel !== true){
41851 this.tabPanel.removeTab(this.id);
41855 * The text displayed in the tooltip for the close icon.
41858 closeText : "Close this tab"
41861 * This script refer to:
41862 * Title: International Telephone Input
41863 * Author: Jack O'Connor
41864 * Code version: v12.1.12
41865 * Availability: https://github.com/jackocnr/intl-tel-input.git
41868 Roo.bootstrap.PhoneInputData = function() {
41871 "Afghanistan (افغانستان)",
41876 "Albania (Shqipëri)",
41881 "Algeria (الجزائر)",
41906 "Antigua and Barbuda",
41916 "Armenia (Հայաստան)",
41932 "Austria (Österreich)",
41937 "Azerbaijan (Azərbaycan)",
41947 "Bahrain (البحرين)",
41952 "Bangladesh (বাংলাদেশ)",
41962 "Belarus (Беларусь)",
41967 "Belgium (België)",
41997 "Bosnia and Herzegovina (Босна и Херцеговина)",
42012 "British Indian Ocean Territory",
42017 "British Virgin Islands",
42027 "Bulgaria (България)",
42037 "Burundi (Uburundi)",
42042 "Cambodia (កម្ពុជា)",
42047 "Cameroon (Cameroun)",
42056 ["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"]
42059 "Cape Verde (Kabu Verdi)",
42064 "Caribbean Netherlands",
42075 "Central African Republic (République centrafricaine)",
42095 "Christmas Island",
42101 "Cocos (Keeling) Islands",
42112 "Comoros (جزر القمر)",
42117 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42122 "Congo (Republic) (Congo-Brazzaville)",
42142 "Croatia (Hrvatska)",
42163 "Czech Republic (Česká republika)",
42168 "Denmark (Danmark)",
42183 "Dominican Republic (República Dominicana)",
42187 ["809", "829", "849"]
42205 "Equatorial Guinea (Guinea Ecuatorial)",
42225 "Falkland Islands (Islas Malvinas)",
42230 "Faroe Islands (Føroyar)",
42251 "French Guiana (Guyane française)",
42256 "French Polynesia (Polynésie française)",
42271 "Georgia (საქართველო)",
42276 "Germany (Deutschland)",
42296 "Greenland (Kalaallit Nunaat)",
42333 "Guinea-Bissau (Guiné Bissau)",
42358 "Hungary (Magyarország)",
42363 "Iceland (Ísland)",
42383 "Iraq (العراق)",
42399 "Israel (ישראל)",
42426 "Jordan (الأردن)",
42431 "Kazakhstan (Казахстан)",
42452 "Kuwait (الكويت)",
42457 "Kyrgyzstan (Кыргызстан)",
42467 "Latvia (Latvija)",
42472 "Lebanon (لبنان)",
42487 "Libya (ليبيا)",
42497 "Lithuania (Lietuva)",
42512 "Macedonia (FYROM) (Македонија)",
42517 "Madagascar (Madagasikara)",
42547 "Marshall Islands",
42557 "Mauritania (موريتانيا)",
42562 "Mauritius (Moris)",
42583 "Moldova (Republica Moldova)",
42593 "Mongolia (Монгол)",
42598 "Montenegro (Crna Gora)",
42608 "Morocco (المغرب)",
42614 "Mozambique (Moçambique)",
42619 "Myanmar (Burma) (မြန်မာ)",
42624 "Namibia (Namibië)",
42639 "Netherlands (Nederland)",
42644 "New Caledonia (Nouvelle-Calédonie)",
42679 "North Korea (조선 민주주의 인민 공화국)",
42684 "Northern Mariana Islands",
42700 "Pakistan (پاکستان)",
42710 "Palestine (فلسطين)",
42720 "Papua New Guinea",
42762 "Réunion (La Réunion)",
42768 "Romania (România)",
42784 "Saint Barthélemy",
42795 "Saint Kitts and Nevis",
42805 "Saint Martin (Saint-Martin (partie française))",
42811 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42816 "Saint Vincent and the Grenadines",
42831 "São Tomé and Príncipe (São Tomé e Príncipe)",
42836 "Saudi Arabia (المملكة العربية السعودية)",
42841 "Senegal (Sénégal)",
42871 "Slovakia (Slovensko)",
42876 "Slovenia (Slovenija)",
42886 "Somalia (Soomaaliya)",
42896 "South Korea (대한민국)",
42901 "South Sudan (جنوب السودان)",
42911 "Sri Lanka (ශ්රී ලංකාව)",
42916 "Sudan (السودان)",
42926 "Svalbard and Jan Mayen",
42937 "Sweden (Sverige)",
42942 "Switzerland (Schweiz)",
42947 "Syria (سوريا)",
42992 "Trinidad and Tobago",
42997 "Tunisia (تونس)",
43002 "Turkey (Türkiye)",
43012 "Turks and Caicos Islands",
43022 "U.S. Virgin Islands",
43032 "Ukraine (Україна)",
43037 "United Arab Emirates (الإمارات العربية المتحدة)",
43059 "Uzbekistan (Oʻzbekiston)",
43069 "Vatican City (Città del Vaticano)",
43080 "Vietnam (Việt Nam)",
43085 "Wallis and Futuna (Wallis-et-Futuna)",
43090 "Western Sahara (الصحراء الغربية)",
43096 "Yemen (اليمن)",
43120 * This script refer to:
43121 * Title: International Telephone Input
43122 * Author: Jack O'Connor
43123 * Code version: v12.1.12
43124 * Availability: https://github.com/jackocnr/intl-tel-input.git
43128 * @class Roo.bootstrap.PhoneInput
43129 * @extends Roo.bootstrap.TriggerField
43130 * An input with International dial-code selection
43132 * @cfg {String} defaultDialCode default '+852'
43133 * @cfg {Array} preferedCountries default []
43136 * Create a new PhoneInput.
43137 * @param {Object} config Configuration options
43140 Roo.bootstrap.PhoneInput = function(config) {
43141 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43144 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43146 listWidth: undefined,
43148 selectedClass: 'active',
43150 invalidClass : "has-warning",
43152 validClass: 'has-success',
43154 allowed: '0123456789',
43159 * @cfg {String} defaultDialCode The default dial code when initializing the input
43161 defaultDialCode: '+852',
43164 * @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
43166 preferedCountries: false,
43168 getAutoCreate : function()
43170 var data = Roo.bootstrap.PhoneInputData();
43171 var align = this.labelAlign || this.parentLabelAlign();
43174 this.allCountries = [];
43175 this.dialCodeMapping = [];
43177 for (var i = 0; i < data.length; i++) {
43179 this.allCountries[i] = {
43183 priority: c[3] || 0,
43184 areaCodes: c[4] || null
43186 this.dialCodeMapping[c[2]] = {
43189 priority: c[3] || 0,
43190 areaCodes: c[4] || null
43202 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43203 maxlength: this.max_length,
43204 cls : 'form-control tel-input',
43205 autocomplete: 'new-password'
43208 var hiddenInput = {
43211 cls: 'hidden-tel-input'
43215 hiddenInput.name = this.name;
43218 if (this.disabled) {
43219 input.disabled = true;
43222 var flag_container = {
43239 cls: this.hasFeedback ? 'has-feedback' : '',
43245 cls: 'dial-code-holder',
43252 cls: 'roo-select2-container input-group',
43259 if (this.fieldLabel.length) {
43262 tooltip: 'This field is required'
43268 cls: 'control-label',
43274 html: this.fieldLabel
43277 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43283 if(this.indicatorpos == 'right') {
43284 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43291 if(align == 'left') {
43299 if(this.labelWidth > 12){
43300 label.style = "width: " + this.labelWidth + 'px';
43302 if(this.labelWidth < 13 && this.labelmd == 0){
43303 this.labelmd = this.labelWidth;
43305 if(this.labellg > 0){
43306 label.cls += ' col-lg-' + this.labellg;
43307 input.cls += ' col-lg-' + (12 - this.labellg);
43309 if(this.labelmd > 0){
43310 label.cls += ' col-md-' + this.labelmd;
43311 container.cls += ' col-md-' + (12 - this.labelmd);
43313 if(this.labelsm > 0){
43314 label.cls += ' col-sm-' + this.labelsm;
43315 container.cls += ' col-sm-' + (12 - this.labelsm);
43317 if(this.labelxs > 0){
43318 label.cls += ' col-xs-' + this.labelxs;
43319 container.cls += ' col-xs-' + (12 - this.labelxs);
43329 var settings = this;
43331 ['xs','sm','md','lg'].map(function(size){
43332 if (settings[size]) {
43333 cfg.cls += ' col-' + size + '-' + settings[size];
43337 this.store = new Roo.data.Store({
43338 proxy : new Roo.data.MemoryProxy({}),
43339 reader : new Roo.data.JsonReader({
43350 'name' : 'dialCode',
43354 'name' : 'priority',
43358 'name' : 'areaCodes',
43365 if(!this.preferedCountries) {
43366 this.preferedCountries = [
43373 var p = this.preferedCountries.reverse();
43376 for (var i = 0; i < p.length; i++) {
43377 for (var j = 0; j < this.allCountries.length; j++) {
43378 if(this.allCountries[j].iso2 == p[i]) {
43379 var t = this.allCountries[j];
43380 this.allCountries.splice(j,1);
43381 this.allCountries.unshift(t);
43387 this.store.proxy.data = {
43389 data: this.allCountries
43395 initEvents : function()
43398 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43400 this.indicator = this.indicatorEl();
43401 this.flag = this.flagEl();
43402 this.dialCodeHolder = this.dialCodeHolderEl();
43404 this.trigger = this.el.select('div.flag-box',true).first();
43405 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43410 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43411 _this.list.setWidth(lw);
43414 this.list.on('mouseover', this.onViewOver, this);
43415 this.list.on('mousemove', this.onViewMove, this);
43416 this.inputEl().on("keyup", this.onKeyUp, this);
43417 this.inputEl().on("keypress", this.onKeyPress, this);
43419 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43421 this.view = new Roo.View(this.list, this.tpl, {
43422 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43425 this.view.on('click', this.onViewClick, this);
43426 this.setValue(this.defaultDialCode);
43429 onTriggerClick : function(e)
43431 Roo.log('trigger click');
43436 if(this.isExpanded()){
43438 this.hasFocus = false;
43440 this.store.load({});
43441 this.hasFocus = true;
43446 isExpanded : function()
43448 return this.list.isVisible();
43451 collapse : function()
43453 if(!this.isExpanded()){
43457 Roo.get(document).un('mousedown', this.collapseIf, this);
43458 Roo.get(document).un('mousewheel', this.collapseIf, this);
43459 this.fireEvent('collapse', this);
43463 expand : function()
43467 if(this.isExpanded() || !this.hasFocus){
43471 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43472 this.list.setWidth(lw);
43475 this.restrictHeight();
43477 Roo.get(document).on('mousedown', this.collapseIf, this);
43478 Roo.get(document).on('mousewheel', this.collapseIf, this);
43480 this.fireEvent('expand', this);
43483 restrictHeight : function()
43485 this.list.alignTo(this.inputEl(), this.listAlign);
43486 this.list.alignTo(this.inputEl(), this.listAlign);
43489 onViewOver : function(e, t)
43491 if(this.inKeyMode){
43494 var item = this.view.findItemFromChild(t);
43497 var index = this.view.indexOf(item);
43498 this.select(index, false);
43503 onViewClick : function(view, doFocus, el, e)
43505 var index = this.view.getSelectedIndexes()[0];
43507 var r = this.store.getAt(index);
43510 this.onSelect(r, index);
43512 if(doFocus !== false && !this.blockFocus){
43513 this.inputEl().focus();
43517 onViewMove : function(e, t)
43519 this.inKeyMode = false;
43522 select : function(index, scrollIntoView)
43524 this.selectedIndex = index;
43525 this.view.select(index);
43526 if(scrollIntoView !== false){
43527 var el = this.view.getNode(index);
43529 this.list.scrollChildIntoView(el, false);
43534 createList : function()
43536 this.list = Roo.get(document.body).createChild({
43538 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43539 style: 'display:none'
43542 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43545 collapseIf : function(e)
43547 var in_combo = e.within(this.el);
43548 var in_list = e.within(this.list);
43549 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43551 if (in_combo || in_list || is_list) {
43557 onSelect : function(record, index)
43559 if(this.fireEvent('beforeselect', this, record, index) !== false){
43561 this.setFlagClass(record.data.iso2);
43562 this.setDialCode(record.data.dialCode);
43563 this.hasFocus = false;
43565 this.fireEvent('select', this, record, index);
43569 flagEl : function()
43571 var flag = this.el.select('div.flag',true).first();
43578 dialCodeHolderEl : function()
43580 var d = this.el.select('input.dial-code-holder',true).first();
43587 setDialCode : function(v)
43589 this.dialCodeHolder.dom.value = '+'+v;
43592 setFlagClass : function(n)
43594 this.flag.dom.className = 'flag '+n;
43597 getValue : function()
43599 var v = this.inputEl().getValue();
43600 if(this.dialCodeHolder) {
43601 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43606 setValue : function(v)
43608 var d = this.getDialCode(v);
43610 //invalid dial code
43611 if(v.length == 0 || !d || d.length == 0) {
43613 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43614 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43620 this.setFlagClass(this.dialCodeMapping[d].iso2);
43621 this.setDialCode(d);
43622 this.inputEl().dom.value = v.replace('+'+d,'');
43623 this.hiddenEl().dom.value = this.getValue();
43628 getDialCode : function(v)
43632 if (v.length == 0) {
43633 return this.dialCodeHolder.dom.value;
43637 if (v.charAt(0) != "+") {
43640 var numericChars = "";
43641 for (var i = 1; i < v.length; i++) {
43642 var c = v.charAt(i);
43645 if (this.dialCodeMapping[numericChars]) {
43646 dialCode = v.substr(1, i);
43648 if (numericChars.length == 4) {
43658 this.setValue(this.defaultDialCode);
43662 hiddenEl : function()
43664 return this.el.select('input.hidden-tel-input',true).first();
43667 // after setting val
43668 onKeyUp : function(e){
43669 this.setValue(this.getValue());
43672 onKeyPress : function(e){
43673 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43680 * @class Roo.bootstrap.MoneyField
43681 * @extends Roo.bootstrap.ComboBox
43682 * Bootstrap MoneyField class
43685 * Create a new MoneyField.
43686 * @param {Object} config Configuration options
43689 Roo.bootstrap.MoneyField = function(config) {
43691 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43695 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43698 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43700 allowDecimals : true,
43702 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43704 decimalSeparator : ".",
43706 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43708 decimalPrecision : 0,
43710 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43712 allowNegative : true,
43714 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43718 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43720 minValue : Number.NEGATIVE_INFINITY,
43722 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43724 maxValue : Number.MAX_VALUE,
43726 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43728 minText : "The minimum value for this field is {0}",
43730 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43732 maxText : "The maximum value for this field is {0}",
43734 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43735 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43737 nanText : "{0} is not a valid number",
43739 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43743 * @cfg {String} defaults currency of the MoneyField
43744 * value should be in lkey
43746 defaultCurrency : false,
43748 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43750 thousandsDelimiter : false,
43752 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43763 getAutoCreate : function()
43765 var align = this.labelAlign || this.parentLabelAlign();
43777 cls : 'form-control roo-money-amount-input',
43778 autocomplete: 'new-password'
43781 var hiddenInput = {
43785 cls: 'hidden-number-input'
43788 if(this.max_length) {
43789 input.maxlength = this.max_length;
43793 hiddenInput.name = this.name;
43796 if (this.disabled) {
43797 input.disabled = true;
43800 var clg = 12 - this.inputlg;
43801 var cmd = 12 - this.inputmd;
43802 var csm = 12 - this.inputsm;
43803 var cxs = 12 - this.inputxs;
43807 cls : 'row roo-money-field',
43811 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43815 cls: 'roo-select2-container input-group',
43819 cls : 'form-control roo-money-currency-input',
43820 autocomplete: 'new-password',
43822 name : this.currencyName
43826 cls : 'input-group-addon',
43840 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43844 cls: this.hasFeedback ? 'has-feedback' : '',
43855 if (this.fieldLabel.length) {
43858 tooltip: 'This field is required'
43864 cls: 'control-label',
43870 html: this.fieldLabel
43873 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43879 if(this.indicatorpos == 'right') {
43880 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43887 if(align == 'left') {
43895 if(this.labelWidth > 12){
43896 label.style = "width: " + this.labelWidth + 'px';
43898 if(this.labelWidth < 13 && this.labelmd == 0){
43899 this.labelmd = this.labelWidth;
43901 if(this.labellg > 0){
43902 label.cls += ' col-lg-' + this.labellg;
43903 input.cls += ' col-lg-' + (12 - this.labellg);
43905 if(this.labelmd > 0){
43906 label.cls += ' col-md-' + this.labelmd;
43907 container.cls += ' col-md-' + (12 - this.labelmd);
43909 if(this.labelsm > 0){
43910 label.cls += ' col-sm-' + this.labelsm;
43911 container.cls += ' col-sm-' + (12 - this.labelsm);
43913 if(this.labelxs > 0){
43914 label.cls += ' col-xs-' + this.labelxs;
43915 container.cls += ' col-xs-' + (12 - this.labelxs);
43926 var settings = this;
43928 ['xs','sm','md','lg'].map(function(size){
43929 if (settings[size]) {
43930 cfg.cls += ' col-' + size + '-' + settings[size];
43937 initEvents : function()
43939 this.indicator = this.indicatorEl();
43941 this.initCurrencyEvent();
43943 this.initNumberEvent();
43946 initCurrencyEvent : function()
43949 throw "can not find store for combo";
43952 this.store = Roo.factory(this.store, Roo.data);
43953 this.store.parent = this;
43957 this.triggerEl = this.el.select('.input-group-addon', true).first();
43959 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43964 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43965 _this.list.setWidth(lw);
43968 this.list.on('mouseover', this.onViewOver, this);
43969 this.list.on('mousemove', this.onViewMove, this);
43970 this.list.on('scroll', this.onViewScroll, this);
43973 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43976 this.view = new Roo.View(this.list, this.tpl, {
43977 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43980 this.view.on('click', this.onViewClick, this);
43982 this.store.on('beforeload', this.onBeforeLoad, this);
43983 this.store.on('load', this.onLoad, this);
43984 this.store.on('loadexception', this.onLoadException, this);
43986 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43987 "up" : function(e){
43988 this.inKeyMode = true;
43992 "down" : function(e){
43993 if(!this.isExpanded()){
43994 this.onTriggerClick();
43996 this.inKeyMode = true;
44001 "enter" : function(e){
44004 if(this.fireEvent("specialkey", this, e)){
44005 this.onViewClick(false);
44011 "esc" : function(e){
44015 "tab" : function(e){
44018 if(this.fireEvent("specialkey", this, e)){
44019 this.onViewClick(false);
44027 doRelay : function(foo, bar, hname){
44028 if(hname == 'down' || this.scope.isExpanded()){
44029 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44037 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44041 initNumberEvent : function(e)
44043 this.inputEl().on("keydown" , this.fireKey, this);
44044 this.inputEl().on("focus", this.onFocus, this);
44045 this.inputEl().on("blur", this.onBlur, this);
44047 this.inputEl().relayEvent('keyup', this);
44049 if(this.indicator){
44050 this.indicator.addClass('invisible');
44053 this.originalValue = this.getValue();
44055 if(this.validationEvent == 'keyup'){
44056 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44057 this.inputEl().on('keyup', this.filterValidation, this);
44059 else if(this.validationEvent !== false){
44060 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44063 if(this.selectOnFocus){
44064 this.on("focus", this.preFocus, this);
44067 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44068 this.inputEl().on("keypress", this.filterKeys, this);
44070 this.inputEl().relayEvent('keypress', this);
44073 var allowed = "0123456789";
44075 if(this.allowDecimals){
44076 allowed += this.decimalSeparator;
44079 if(this.allowNegative){
44083 if(this.thousandsDelimiter) {
44087 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44089 var keyPress = function(e){
44091 var k = e.getKey();
44093 var c = e.getCharCode();
44096 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44097 allowed.indexOf(String.fromCharCode(c)) === -1
44103 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44107 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44112 this.inputEl().on("keypress", keyPress, this);
44116 onTriggerClick : function(e)
44123 this.loadNext = false;
44125 if(this.isExpanded()){
44130 this.hasFocus = true;
44132 if(this.triggerAction == 'all') {
44133 this.doQuery(this.allQuery, true);
44137 this.doQuery(this.getRawValue());
44140 getCurrency : function()
44142 var v = this.currencyEl().getValue();
44147 restrictHeight : function()
44149 this.list.alignTo(this.currencyEl(), this.listAlign);
44150 this.list.alignTo(this.currencyEl(), this.listAlign);
44153 onViewClick : function(view, doFocus, el, e)
44155 var index = this.view.getSelectedIndexes()[0];
44157 var r = this.store.getAt(index);
44160 this.onSelect(r, index);
44164 onSelect : function(record, index){
44166 if(this.fireEvent('beforeselect', this, record, index) !== false){
44168 this.setFromCurrencyData(index > -1 ? record.data : false);
44172 this.fireEvent('select', this, record, index);
44176 setFromCurrencyData : function(o)
44180 this.lastCurrency = o;
44182 if (this.currencyField) {
44183 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44185 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44188 this.lastSelectionText = currency;
44190 //setting default currency
44191 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44192 this.setCurrency(this.defaultCurrency);
44196 this.setCurrency(currency);
44199 setFromData : function(o)
44203 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44205 this.setFromCurrencyData(c);
44210 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44212 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44215 this.setValue(value);
44219 setCurrency : function(v)
44221 this.currencyValue = v;
44224 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44229 setValue : function(v)
44231 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44237 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44239 this.inputEl().dom.value = (v == '') ? '' :
44240 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44242 if(!this.allowZero && v === '0') {
44243 this.hiddenEl().dom.value = '';
44244 this.inputEl().dom.value = '';
44251 getRawValue : function()
44253 var v = this.inputEl().getValue();
44258 getValue : function()
44260 return this.fixPrecision(this.parseValue(this.getRawValue()));
44263 parseValue : function(value)
44265 if(this.thousandsDelimiter) {
44267 r = new RegExp(",", "g");
44268 value = value.replace(r, "");
44271 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44272 return isNaN(value) ? '' : value;
44276 fixPrecision : function(value)
44278 if(this.thousandsDelimiter) {
44280 r = new RegExp(",", "g");
44281 value = value.replace(r, "");
44284 var nan = isNaN(value);
44286 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44287 return nan ? '' : value;
44289 return parseFloat(value).toFixed(this.decimalPrecision);
44292 decimalPrecisionFcn : function(v)
44294 return Math.floor(v);
44297 validateValue : function(value)
44299 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44303 var num = this.parseValue(value);
44306 this.markInvalid(String.format(this.nanText, value));
44310 if(num < this.minValue){
44311 this.markInvalid(String.format(this.minText, this.minValue));
44315 if(num > this.maxValue){
44316 this.markInvalid(String.format(this.maxText, this.maxValue));
44323 validate : function()
44325 if(this.disabled || this.allowBlank){
44330 var currency = this.getCurrency();
44332 if(this.validateValue(this.getRawValue()) && currency.length){
44337 this.markInvalid();
44341 getName: function()
44346 beforeBlur : function()
44352 var v = this.parseValue(this.getRawValue());
44359 onBlur : function()
44363 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44364 //this.el.removeClass(this.focusClass);
44367 this.hasFocus = false;
44369 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44373 var v = this.getValue();
44375 if(String(v) !== String(this.startValue)){
44376 this.fireEvent('change', this, v, this.startValue);
44379 this.fireEvent("blur", this);
44382 inputEl : function()
44384 return this.el.select('.roo-money-amount-input', true).first();
44387 currencyEl : function()
44389 return this.el.select('.roo-money-currency-input', true).first();
44392 hiddenEl : function()
44394 return this.el.select('input.hidden-number-input',true).first();
44398 * @class Roo.bootstrap.BezierSignature
44399 * @extends Roo.bootstrap.Component
44400 * Bootstrap BezierSignature class
44401 * This script refer to:
44402 * Title: Signature Pad
44404 * Availability: https://github.com/szimek/signature_pad
44407 * Create a new BezierSignature
44408 * @param {Object} config The config object
44411 Roo.bootstrap.BezierSignature = function(config){
44412 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44418 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44425 mouse_btn_down: true,
44428 * @cfg {int} canvas height
44430 canvas_height: '200px',
44433 * @cfg {float|function} Radius of a single dot.
44438 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44443 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44448 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44453 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44458 * @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.
44460 bg_color: 'rgba(0, 0, 0, 0)',
44463 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44465 dot_color: 'black',
44468 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44470 velocity_filter_weight: 0.7,
44473 * @cfg {function} Callback when stroke begin.
44478 * @cfg {function} Callback when stroke end.
44482 getAutoCreate : function()
44484 var cls = 'roo-signature column';
44487 cls += ' ' + this.cls;
44497 for(var i = 0; i < col_sizes.length; i++) {
44498 if(this[col_sizes[i]]) {
44499 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44509 cls: 'roo-signature-body',
44513 cls: 'roo-signature-body-canvas',
44514 height: this.canvas_height,
44515 width: this.canvas_width
44522 style: 'display: none'
44530 initEvents: function()
44532 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44534 var canvas = this.canvasEl();
44536 // mouse && touch event swapping...
44537 canvas.dom.style.touchAction = 'none';
44538 canvas.dom.style.msTouchAction = 'none';
44540 this.mouse_btn_down = false;
44541 canvas.on('mousedown', this._handleMouseDown, this);
44542 canvas.on('mousemove', this._handleMouseMove, this);
44543 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44545 if (window.PointerEvent) {
44546 canvas.on('pointerdown', this._handleMouseDown, this);
44547 canvas.on('pointermove', this._handleMouseMove, this);
44548 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44551 if ('ontouchstart' in window) {
44552 canvas.on('touchstart', this._handleTouchStart, this);
44553 canvas.on('touchmove', this._handleTouchMove, this);
44554 canvas.on('touchend', this._handleTouchEnd, this);
44557 Roo.EventManager.onWindowResize(this.resize, this, true);
44559 // file input event
44560 this.fileEl().on('change', this.uploadImage, this);
44567 resize: function(){
44569 var canvas = this.canvasEl().dom;
44570 var ctx = this.canvasElCtx();
44571 var img_data = false;
44573 if(canvas.width > 0) {
44574 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44576 // setting canvas width will clean img data
44579 var style = window.getComputedStyle ?
44580 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44582 var padding_left = parseInt(style.paddingLeft) || 0;
44583 var padding_right = parseInt(style.paddingRight) || 0;
44585 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44588 ctx.putImageData(img_data, 0, 0);
44592 _handleMouseDown: function(e)
44594 if (e.browserEvent.which === 1) {
44595 this.mouse_btn_down = true;
44596 this.strokeBegin(e);
44600 _handleMouseMove: function (e)
44602 if (this.mouse_btn_down) {
44603 this.strokeMoveUpdate(e);
44607 _handleMouseUp: function (e)
44609 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44610 this.mouse_btn_down = false;
44615 _handleTouchStart: function (e) {
44617 e.preventDefault();
44618 if (e.browserEvent.targetTouches.length === 1) {
44619 // var touch = e.browserEvent.changedTouches[0];
44620 // this.strokeBegin(touch);
44622 this.strokeBegin(e); // assume e catching the correct xy...
44626 _handleTouchMove: function (e) {
44627 e.preventDefault();
44628 // var touch = event.targetTouches[0];
44629 // _this._strokeMoveUpdate(touch);
44630 this.strokeMoveUpdate(e);
44633 _handleTouchEnd: function (e) {
44634 var wasCanvasTouched = e.target === this.canvasEl().dom;
44635 if (wasCanvasTouched) {
44636 e.preventDefault();
44637 // var touch = event.changedTouches[0];
44638 // _this._strokeEnd(touch);
44643 reset: function () {
44644 this._lastPoints = [];
44645 this._lastVelocity = 0;
44646 this._lastWidth = (this.min_width + this.max_width) / 2;
44647 this.canvasElCtx().fillStyle = this.dot_color;
44650 strokeMoveUpdate: function(e)
44652 this.strokeUpdate(e);
44654 if (this.throttle) {
44655 this.throttleStroke(this.strokeUpdate, this.throttle);
44658 this.strokeUpdate(e);
44662 strokeBegin: function(e)
44664 var newPointGroup = {
44665 color: this.dot_color,
44669 if (typeof this.onBegin === 'function') {
44673 this.curve_data.push(newPointGroup);
44675 this.strokeUpdate(e);
44678 strokeUpdate: function(e)
44680 var rect = this.canvasEl().dom.getBoundingClientRect();
44681 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44682 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44683 var lastPoints = lastPointGroup.points;
44684 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44685 var isLastPointTooClose = lastPoint
44686 ? point.distanceTo(lastPoint) <= this.min_distance
44688 var color = lastPointGroup.color;
44689 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44690 var curve = this.addPoint(point);
44692 this.drawDot({color: color, point: point});
44695 this.drawCurve({color: color, curve: curve});
44705 strokeEnd: function(e)
44707 this.strokeUpdate(e);
44708 if (typeof this.onEnd === 'function') {
44713 addPoint: function (point) {
44714 var _lastPoints = this._lastPoints;
44715 _lastPoints.push(point);
44716 if (_lastPoints.length > 2) {
44717 if (_lastPoints.length === 3) {
44718 _lastPoints.unshift(_lastPoints[0]);
44720 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44721 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44722 _lastPoints.shift();
44728 calculateCurveWidths: function (startPoint, endPoint) {
44729 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44730 (1 - this.velocity_filter_weight) * this._lastVelocity;
44732 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44735 start: this._lastWidth
44738 this._lastVelocity = velocity;
44739 this._lastWidth = newWidth;
44743 drawDot: function (_a) {
44744 var color = _a.color, point = _a.point;
44745 var ctx = this.canvasElCtx();
44746 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44748 this.drawCurveSegment(point.x, point.y, width);
44750 ctx.fillStyle = color;
44754 drawCurve: function (_a) {
44755 var color = _a.color, curve = _a.curve;
44756 var ctx = this.canvasElCtx();
44757 var widthDelta = curve.endWidth - curve.startWidth;
44758 var drawSteps = Math.floor(curve.length()) * 2;
44760 ctx.fillStyle = color;
44761 for (var i = 0; i < drawSteps; i += 1) {
44762 var t = i / drawSteps;
44768 var x = uuu * curve.startPoint.x;
44769 x += 3 * uu * t * curve.control1.x;
44770 x += 3 * u * tt * curve.control2.x;
44771 x += ttt * curve.endPoint.x;
44772 var y = uuu * curve.startPoint.y;
44773 y += 3 * uu * t * curve.control1.y;
44774 y += 3 * u * tt * curve.control2.y;
44775 y += ttt * curve.endPoint.y;
44776 var width = curve.startWidth + ttt * widthDelta;
44777 this.drawCurveSegment(x, y, width);
44783 drawCurveSegment: function (x, y, width) {
44784 var ctx = this.canvasElCtx();
44786 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44787 this.is_empty = false;
44792 var ctx = this.canvasElCtx();
44793 var canvas = this.canvasEl().dom;
44794 ctx.fillStyle = this.bg_color;
44795 ctx.clearRect(0, 0, canvas.width, canvas.height);
44796 ctx.fillRect(0, 0, canvas.width, canvas.height);
44797 this.curve_data = [];
44799 this.is_empty = true;
44804 return this.el.select('input',true).first();
44807 canvasEl: function()
44809 return this.el.select('canvas',true).first();
44812 canvasElCtx: function()
44814 return this.el.select('canvas',true).first().dom.getContext('2d');
44817 getImage: function(type)
44819 if(this.is_empty) {
44824 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44827 drawFromImage: function(img_src)
44829 var img = new Image();
44831 img.onload = function(){
44832 this.canvasElCtx().drawImage(img, 0, 0);
44837 this.is_empty = false;
44840 selectImage: function()
44842 this.fileEl().dom.click();
44845 uploadImage: function(e)
44847 var reader = new FileReader();
44849 reader.onload = function(e){
44850 var img = new Image();
44851 img.onload = function(){
44853 this.canvasElCtx().drawImage(img, 0, 0);
44855 img.src = e.target.result;
44858 reader.readAsDataURL(e.target.files[0]);
44861 // Bezier Point Constructor
44862 Point: (function () {
44863 function Point(x, y, time) {
44866 this.time = time || Date.now();
44868 Point.prototype.distanceTo = function (start) {
44869 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44871 Point.prototype.equals = function (other) {
44872 return this.x === other.x && this.y === other.y && this.time === other.time;
44874 Point.prototype.velocityFrom = function (start) {
44875 return this.time !== start.time
44876 ? this.distanceTo(start) / (this.time - start.time)
44883 // Bezier Constructor
44884 Bezier: (function () {
44885 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44886 this.startPoint = startPoint;
44887 this.control2 = control2;
44888 this.control1 = control1;
44889 this.endPoint = endPoint;
44890 this.startWidth = startWidth;
44891 this.endWidth = endWidth;
44893 Bezier.fromPoints = function (points, widths, scope) {
44894 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44895 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44896 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44898 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44899 var dx1 = s1.x - s2.x;
44900 var dy1 = s1.y - s2.y;
44901 var dx2 = s2.x - s3.x;
44902 var dy2 = s2.y - s3.y;
44903 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44904 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44905 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44906 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44907 var dxm = m1.x - m2.x;
44908 var dym = m1.y - m2.y;
44909 var k = l2 / (l1 + l2);
44910 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44911 var tx = s2.x - cm.x;
44912 var ty = s2.y - cm.y;
44914 c1: new scope.Point(m1.x + tx, m1.y + ty),
44915 c2: new scope.Point(m2.x + tx, m2.y + ty)
44918 Bezier.prototype.length = function () {
44923 for (var i = 0; i <= steps; i += 1) {
44925 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44926 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44928 var xdiff = cx - px;
44929 var ydiff = cy - py;
44930 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44937 Bezier.prototype.point = function (t, start, c1, c2, end) {
44938 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44939 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44940 + (3.0 * c2 * (1.0 - t) * t * t)
44941 + (end * t * t * t);
44946 throttleStroke: function(fn, wait) {
44947 if (wait === void 0) { wait = 250; }
44949 var timeout = null;
44953 var later = function () {
44954 previous = Date.now();
44956 result = fn.apply(storedContext, storedArgs);
44958 storedContext = null;
44962 return function wrapper() {
44964 for (var _i = 0; _i < arguments.length; _i++) {
44965 args[_i] = arguments[_i];
44967 var now = Date.now();
44968 var remaining = wait - (now - previous);
44969 storedContext = this;
44971 if (remaining <= 0 || remaining > wait) {
44973 clearTimeout(timeout);
44977 result = fn.apply(storedContext, storedArgs);
44979 storedContext = null;
44983 else if (!timeout) {
44984 timeout = window.setTimeout(later, remaining);