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);
7984 * @class Roo.bootstrap.Table
7986 * @extends Roo.bootstrap.Component
7987 * Bootstrap Table class. This class represents the primary interface of a component based grid control.
7988 * Similar to Roo.grid.Grid
7990 var table = Roo.factory({
7992 xns : Roo.bootstrap,
7993 autoSizeColumns: true,
8000 sortInfo : { direction : 'ASC', field: 'name' },
8002 xtype : 'HttpProxy',
8005 url : 'https://example.com/some.data.url.json'
8008 xtype : 'JsonReader',
8010 fields : [ 'id', 'name', whatever' ],
8017 xtype : 'ColumnModel',
8021 dataIndex : 'is_in_group',
8024 renderer : function(v, x , r) {
8026 return String.format("{0}", v)
8032 xtype : 'RowSelectionModel',
8033 xns : Roo.bootstrap.Table
8034 // you can add listeners to catch selection change here....
8040 grid.render(Roo.get("some-div"));
8043 Currently the Table uses multiple headers to try and handle XL / Medium etc... styling
8048 * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8049 * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8050 * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8052 * @cfg {String} cls table class
8055 * @cfg {boolean} striped Should the rows be alternative striped
8056 * @cfg {boolean} bordered Add borders to the table
8057 * @cfg {boolean} hover Add hover highlighting
8058 * @cfg {boolean} condensed Format condensed
8059 * @cfg {boolean} responsive Format condensed
8060 * @cfg {Boolean} loadMask (true|false) default false
8061 * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8062 * @cfg {Boolean} headerShow (true|false) generate thead, default true
8063 * @cfg {Boolean} rowSelection (true|false) default false
8064 * @cfg {Boolean} cellSelection (true|false) default false
8065 * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8066 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
8067 * @cfg {Boolean} lazyLoad auto load data while scrolling to the end (default false)
8068 * @cfg {Boolean} auto_hide_footer auto hide footer if only one page (default false)
8072 * Create a new Table
8073 * @param {Object} config The config object
8076 Roo.bootstrap.Table = function(config)
8078 Roo.bootstrap.Table.superclass.constructor.call(this, config);
8081 this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8082 this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8083 this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8084 this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8086 this.sm = this.sm || {xtype: 'RowSelectionModel'};
8088 this.sm.grid = this;
8089 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8090 this.sm = this.selModel;
8091 this.sm.xmodule = this.xmodule || false;
8094 if (this.cm && typeof(this.cm.config) == 'undefined') {
8095 this.colModel = new Roo.grid.ColumnModel(this.cm);
8096 this.cm = this.colModel;
8097 this.cm.xmodule = this.xmodule || false;
8100 this.store= Roo.factory(this.store, Roo.data);
8101 this.ds = this.store;
8102 this.ds.xmodule = this.xmodule || false;
8105 if (this.footer && this.store) {
8106 this.footer.dataSource = this.ds;
8107 this.footer = Roo.factory(this.footer);
8114 * Fires when a cell is clicked
8115 * @param {Roo.bootstrap.Table} this
8116 * @param {Roo.Element} el
8117 * @param {Number} rowIndex
8118 * @param {Number} columnIndex
8119 * @param {Roo.EventObject} e
8123 * @event celldblclick
8124 * Fires when a cell is double clicked
8125 * @param {Roo.bootstrap.Table} this
8126 * @param {Roo.Element} el
8127 * @param {Number} rowIndex
8128 * @param {Number} columnIndex
8129 * @param {Roo.EventObject} e
8131 "celldblclick" : true,
8134 * Fires when a row is clicked
8135 * @param {Roo.bootstrap.Table} this
8136 * @param {Roo.Element} el
8137 * @param {Number} rowIndex
8138 * @param {Roo.EventObject} e
8142 * @event rowdblclick
8143 * Fires when a row is double clicked
8144 * @param {Roo.bootstrap.Table} this
8145 * @param {Roo.Element} el
8146 * @param {Number} rowIndex
8147 * @param {Roo.EventObject} e
8149 "rowdblclick" : true,
8152 * Fires when a mouseover occur
8153 * @param {Roo.bootstrap.Table} this
8154 * @param {Roo.Element} el
8155 * @param {Number} rowIndex
8156 * @param {Number} columnIndex
8157 * @param {Roo.EventObject} e
8162 * Fires when a mouseout occur
8163 * @param {Roo.bootstrap.Table} this
8164 * @param {Roo.Element} el
8165 * @param {Number} rowIndex
8166 * @param {Number} columnIndex
8167 * @param {Roo.EventObject} e
8172 * Fires when a row is rendered, so you can change add a style to it.
8173 * @param {Roo.bootstrap.Table} this
8174 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
8178 * @event rowsrendered
8179 * Fires when all the rows have been rendered
8180 * @param {Roo.bootstrap.Table} this
8182 'rowsrendered' : true,
8184 * @event contextmenu
8185 * The raw contextmenu event for the entire grid.
8186 * @param {Roo.EventObject} e
8188 "contextmenu" : true,
8190 * @event rowcontextmenu
8191 * Fires when a row is right clicked
8192 * @param {Roo.bootstrap.Table} this
8193 * @param {Number} rowIndex
8194 * @param {Roo.EventObject} e
8196 "rowcontextmenu" : true,
8198 * @event cellcontextmenu
8199 * Fires when a cell is right clicked
8200 * @param {Roo.bootstrap.Table} this
8201 * @param {Number} rowIndex
8202 * @param {Number} cellIndex
8203 * @param {Roo.EventObject} e
8205 "cellcontextmenu" : true,
8207 * @event headercontextmenu
8208 * Fires when a header is right clicked
8209 * @param {Roo.bootstrap.Table} this
8210 * @param {Number} columnIndex
8211 * @param {Roo.EventObject} e
8213 "headercontextmenu" : true
8217 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
8234 rowSelection : false,
8235 cellSelection : false,
8238 // Roo.Element - the tbody
8240 // Roo.Element - thead element
8243 container: false, // used by gridpanel...
8249 auto_hide_footer : false,
8251 getAutoCreate : function()
8253 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8260 // this get's auto added by panel.Grid
8261 if (this.scrollBody) {
8262 cfg.cls += ' table-body-fixed';
8265 cfg.cls += ' table-striped';
8269 cfg.cls += ' table-hover';
8271 if (this.bordered) {
8272 cfg.cls += ' table-bordered';
8274 if (this.condensed) {
8275 cfg.cls += ' table-condensed';
8278 if (this.responsive) {
8279 cfg.cls += ' table-responsive';
8283 cfg.cls+= ' ' +this.cls;
8289 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8292 if(this.store || this.cm){
8293 if(this.headerShow){
8294 cfg.cn.push(this.renderHeader());
8297 cfg.cn.push(this.renderBody());
8299 if(this.footerShow){
8300 cfg.cn.push(this.renderFooter());
8302 // where does this come from?
8303 //cfg.cls+= ' TableGrid';
8306 return { cn : [ cfg ] };
8309 initEvents : function()
8311 if(!this.store || !this.cm){
8314 if (this.selModel) {
8315 this.selModel.initEvents();
8319 //Roo.log('initEvents with ds!!!!');
8321 this.mainBody = this.el.select('tbody', true).first();
8322 this.mainHead = this.el.select('thead', true).first();
8323 this.mainFoot = this.el.select('tfoot', true).first();
8328 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8329 e.on('click', this.sort, this);
8332 this.mainBody.on("click", this.onClick, this);
8333 this.mainBody.on("dblclick", this.onDblClick, this);
8335 // why is this done????? = it breaks dialogs??
8336 //this.parent().el.setStyle('position', 'relative');
8340 this.footer.parentId = this.id;
8341 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8344 this.el.select('tfoot tr td').first().addClass('hide');
8349 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8352 this.store.on('load', this.onLoad, this);
8353 this.store.on('beforeload', this.onBeforeLoad, this);
8354 this.store.on('update', this.onUpdate, this);
8355 this.store.on('add', this.onAdd, this);
8356 this.store.on("clear", this.clear, this);
8358 this.el.on("contextmenu", this.onContextMenu, this);
8360 this.mainBody.on('scroll', this.onBodyScroll, this);
8362 this.cm.on("headerchange", this.onHeaderChange, this);
8364 this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8368 onContextMenu : function(e, t)
8370 this.processEvent("contextmenu", e);
8373 processEvent : function(name, e)
8375 if (name != 'touchstart' ) {
8376 this.fireEvent(name, e);
8379 var t = e.getTarget();
8381 var cell = Roo.get(t);
8387 if(cell.findParent('tfoot', false, true)){
8391 if(cell.findParent('thead', false, true)){
8393 if(e.getTarget().nodeName.toLowerCase() != 'th'){
8394 cell = Roo.get(t).findParent('th', false, true);
8396 Roo.log("failed to find th in thead?");
8397 Roo.log(e.getTarget());
8402 var cellIndex = cell.dom.cellIndex;
8404 var ename = name == 'touchstart' ? 'click' : name;
8405 this.fireEvent("header" + ename, this, cellIndex, e);
8410 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8411 cell = Roo.get(t).findParent('td', false, true);
8413 Roo.log("failed to find th in tbody?");
8414 Roo.log(e.getTarget());
8419 var row = cell.findParent('tr', false, true);
8420 var cellIndex = cell.dom.cellIndex;
8421 var rowIndex = row.dom.rowIndex - 1;
8425 this.fireEvent("row" + name, this, rowIndex, e);
8429 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8435 onMouseover : function(e, el)
8437 var cell = Roo.get(el);
8443 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8444 cell = cell.findParent('td', false, true);
8447 var row = cell.findParent('tr', false, true);
8448 var cellIndex = cell.dom.cellIndex;
8449 var rowIndex = row.dom.rowIndex - 1; // start from 0
8451 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8455 onMouseout : function(e, el)
8457 var cell = Roo.get(el);
8463 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8464 cell = cell.findParent('td', false, true);
8467 var row = cell.findParent('tr', false, true);
8468 var cellIndex = cell.dom.cellIndex;
8469 var rowIndex = row.dom.rowIndex - 1; // start from 0
8471 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8475 onClick : function(e, el)
8477 var cell = Roo.get(el);
8479 if(!cell || (!this.cellSelection && !this.rowSelection)){
8483 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8484 cell = cell.findParent('td', false, true);
8487 if(!cell || typeof(cell) == 'undefined'){
8491 var row = cell.findParent('tr', false, true);
8493 if(!row || typeof(row) == 'undefined'){
8497 var cellIndex = cell.dom.cellIndex;
8498 var rowIndex = this.getRowIndex(row);
8500 // why??? - should these not be based on SelectionModel?
8501 //if(this.cellSelection){
8502 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8505 //if(this.rowSelection){
8506 this.fireEvent('rowclick', this, row, rowIndex, e);
8511 onDblClick : function(e,el)
8513 var cell = Roo.get(el);
8515 if(!cell || (!this.cellSelection && !this.rowSelection)){
8519 if(e.getTarget().nodeName.toLowerCase() != 'td'){
8520 cell = cell.findParent('td', false, true);
8523 if(!cell || typeof(cell) == 'undefined'){
8527 var row = cell.findParent('tr', false, true);
8529 if(!row || typeof(row) == 'undefined'){
8533 var cellIndex = cell.dom.cellIndex;
8534 var rowIndex = this.getRowIndex(row);
8536 if(this.cellSelection){
8537 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8540 if(this.rowSelection){
8541 this.fireEvent('rowdblclick', this, row, rowIndex, e);
8545 sort : function(e,el)
8547 var col = Roo.get(el);
8549 if(!col.hasClass('sortable')){
8553 var sort = col.attr('sort');
8556 if(col.select('i', true).first().hasClass('fa-arrow-up')){
8560 this.store.sortInfo = {field : sort, direction : dir};
8563 Roo.log("calling footer first");
8564 this.footer.onClick('first');
8567 this.store.load({ params : { start : 0 } });
8571 renderHeader : function()
8579 this.totalWidth = 0;
8581 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8583 var config = cm.config[i];
8587 cls : 'x-hcol-' + i,
8590 html: cm.getColumnHeader(i)
8593 var tooltip = cm.getColumnTooltip(i);
8595 c.tooltip = tooltip;
8601 if(typeof(config.sortable) != 'undefined' && config.sortable){
8603 c.html = '<i class="fa"></i>' + c.html;
8606 // could use BS4 hidden-..-down
8608 if(typeof(config.lgHeader) != 'undefined'){
8609 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8612 if(typeof(config.mdHeader) != 'undefined'){
8613 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8616 if(typeof(config.smHeader) != 'undefined'){
8617 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8620 if(typeof(config.xsHeader) != 'undefined'){
8621 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8628 if(typeof(config.tooltip) != 'undefined'){
8629 c.tooltip = config.tooltip;
8632 if(typeof(config.colspan) != 'undefined'){
8633 c.colspan = config.colspan;
8636 if(typeof(config.hidden) != 'undefined' && config.hidden){
8637 c.style += ' display:none;';
8640 if(typeof(config.dataIndex) != 'undefined'){
8641 c.sort = config.dataIndex;
8646 if(typeof(config.align) != 'undefined' && config.align.length){
8647 c.style += ' text-align:' + config.align + ';';
8650 if(typeof(config.width) != 'undefined'){
8651 c.style += ' width:' + config.width + 'px;';
8652 this.totalWidth += config.width;
8654 this.totalWidth += 100; // assume minimum of 100 per column?
8657 if(typeof(config.cls) != 'undefined'){
8658 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8661 ['xs','sm','md','lg'].map(function(size){
8663 if(typeof(config[size]) == 'undefined'){
8667 if (!config[size]) { // 0 = hidden
8668 // BS 4 '0' is treated as hide that column and below.
8669 c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8673 c.cls += ' col-' + size + '-' + config[size] + (
8674 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8686 renderBody : function()
8696 colspan : this.cm.getColumnCount()
8706 renderFooter : function()
8716 colspan : this.cm.getColumnCount()
8730 // Roo.log('ds onload');
8735 var ds = this.store;
8737 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8738 e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8739 if (_this.store.sortInfo) {
8741 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8742 e.select('i', true).addClass(['fa-arrow-up']);
8745 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8746 e.select('i', true).addClass(['fa-arrow-down']);
8751 var tbody = this.mainBody;
8753 if(ds.getCount() > 0){
8754 ds.data.each(function(d,rowIndex){
8755 var row = this.renderRow(cm, ds, rowIndex);
8757 tbody.createChild(row);
8761 if(row.cellObjects.length){
8762 Roo.each(row.cellObjects, function(r){
8763 _this.renderCellObject(r);
8770 var tfoot = this.el.select('tfoot', true).first();
8772 if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8774 this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8776 var total = this.ds.getTotalCount();
8778 if(this.footer.pageSize < total){
8779 this.mainFoot.show();
8783 Roo.each(this.el.select('tbody td', true).elements, function(e){
8784 e.on('mouseover', _this.onMouseover, _this);
8787 Roo.each(this.el.select('tbody td', true).elements, function(e){
8788 e.on('mouseout', _this.onMouseout, _this);
8790 this.fireEvent('rowsrendered', this);
8796 onUpdate : function(ds,record)
8798 this.refreshRow(record);
8802 onRemove : function(ds, record, index, isUpdate){
8803 if(isUpdate !== true){
8804 this.fireEvent("beforerowremoved", this, index, record);
8806 var bt = this.mainBody.dom;
8808 var rows = this.el.select('tbody > tr', true).elements;
8810 if(typeof(rows[index]) != 'undefined'){
8811 bt.removeChild(rows[index].dom);
8814 // if(bt.rows[index]){
8815 // bt.removeChild(bt.rows[index]);
8818 if(isUpdate !== true){
8819 //this.stripeRows(index);
8820 //this.syncRowHeights(index, index);
8822 this.fireEvent("rowremoved", this, index, record);
8826 onAdd : function(ds, records, rowIndex)
8828 //Roo.log('on Add called');
8829 // - note this does not handle multiple adding very well..
8830 var bt = this.mainBody.dom;
8831 for (var i =0 ; i < records.length;i++) {
8832 //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8833 //Roo.log(records[i]);
8834 //Roo.log(this.store.getAt(rowIndex+i));
8835 this.insertRow(this.store, rowIndex + i, false);
8842 refreshRow : function(record){
8843 var ds = this.store, index;
8844 if(typeof record == 'number'){
8846 record = ds.getAt(index);
8848 index = ds.indexOf(record);
8850 return; // should not happen - but seems to
8853 this.insertRow(ds, index, true);
8855 this.onRemove(ds, record, index+1, true);
8857 //this.syncRowHeights(index, index);
8859 this.fireEvent("rowupdated", this, index, record);
8862 insertRow : function(dm, rowIndex, isUpdate){
8865 this.fireEvent("beforerowsinserted", this, rowIndex);
8867 //var s = this.getScrollState();
8868 var row = this.renderRow(this.cm, this.store, rowIndex);
8869 // insert before rowIndex..
8870 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8874 if(row.cellObjects.length){
8875 Roo.each(row.cellObjects, function(r){
8876 _this.renderCellObject(r);
8881 this.fireEvent("rowsinserted", this, rowIndex);
8882 //this.syncRowHeights(firstRow, lastRow);
8883 //this.stripeRows(firstRow);
8890 getRowDom : function(rowIndex)
8892 var rows = this.el.select('tbody > tr', true).elements;
8894 return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8897 // returns the object tree for a tr..
8900 renderRow : function(cm, ds, rowIndex)
8902 var d = ds.getAt(rowIndex);
8906 cls : 'x-row-' + rowIndex,
8910 var cellObjects = [];
8912 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8913 var config = cm.config[i];
8915 var renderer = cm.getRenderer(i);
8919 if(typeof(renderer) !== 'undefined'){
8920 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8922 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8923 // and are rendered into the cells after the row is rendered - using the id for the element.
8925 if(typeof(value) === 'object'){
8935 rowIndex : rowIndex,
8940 this.fireEvent('rowclass', this, rowcfg);
8944 // this might end up displaying HTML?
8945 // this is too messy... - better to only do it on columsn you know are going to be too long
8946 //tooltip : (typeof(value) === 'object') ? '' : value,
8947 cls : rowcfg.rowClass + ' x-col-' + i,
8949 html: (typeof(value) === 'object') ? '' : value
8956 if(typeof(config.colspan) != 'undefined'){
8957 td.colspan = config.colspan;
8960 if(typeof(config.hidden) != 'undefined' && config.hidden){
8961 td.style += ' display:none;';
8964 if(typeof(config.align) != 'undefined' && config.align.length){
8965 td.style += ' text-align:' + config.align + ';';
8967 if(typeof(config.valign) != 'undefined' && config.valign.length){
8968 td.style += ' vertical-align:' + config.valign + ';';
8971 if(typeof(config.width) != 'undefined'){
8972 td.style += ' width:' + config.width + 'px;';
8975 if(typeof(config.cursor) != 'undefined'){
8976 td.style += ' cursor:' + config.cursor + ';';
8979 if(typeof(config.cls) != 'undefined'){
8980 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8983 ['xs','sm','md','lg'].map(function(size){
8985 if(typeof(config[size]) == 'undefined'){
8991 if (!config[size]) { // 0 = hidden
8992 // BS 4 '0' is treated as hide that column and below.
8993 td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8997 td.cls += ' col-' + size + '-' + config[size] + (
8998 size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9008 row.cellObjects = cellObjects;
9016 onBeforeLoad : function()
9025 this.el.select('tbody', true).first().dom.innerHTML = '';
9028 * Show or hide a row.
9029 * @param {Number} rowIndex to show or hide
9030 * @param {Boolean} state hide
9032 setRowVisibility : function(rowIndex, state)
9034 var bt = this.mainBody.dom;
9036 var rows = this.el.select('tbody > tr', true).elements;
9038 if(typeof(rows[rowIndex]) == 'undefined'){
9041 rows[rowIndex].dom.style.display = state ? '' : 'none';
9045 getSelectionModel : function(){
9047 this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9049 return this.selModel;
9052 * Render the Roo.bootstrap object from renderder
9054 renderCellObject : function(r)
9058 r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9060 var t = r.cfg.render(r.container);
9063 Roo.each(r.cfg.cn, function(c){
9065 container: t.getChildContainer(),
9068 _this.renderCellObject(child);
9073 getRowIndex : function(row)
9077 Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9088 * Returns the grid's underlying element = used by panel.Grid
9089 * @return {Element} The element
9091 getGridEl : function(){
9095 * Forces a resize - used by panel.Grid
9096 * @return {Element} The element
9098 autoSize : function()
9100 //var ctr = Roo.get(this.container.dom.parentElement);
9101 var ctr = Roo.get(this.el.dom);
9103 var thd = this.getGridEl().select('thead',true).first();
9104 var tbd = this.getGridEl().select('tbody', true).first();
9105 var tfd = this.getGridEl().select('tfoot', true).first();
9107 var cw = ctr.getWidth();
9108 this.getGridEl().select('tfoot tr, tfoot td',true).setWidth(cw);
9112 tbd.setWidth(ctr.getWidth());
9113 // if the body has a max height - and then scrolls - we should perhaps set up the height here
9114 // this needs fixing for various usage - currently only hydra job advers I think..
9116 // ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9118 var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9121 cw = Math.max(cw, this.totalWidth);
9122 this.getGridEl().select('tbody tr',true).setWidth(cw);
9124 // resize 'expandable coloumn?
9126 return; // we doe not have a view in this design..
9129 onBodyScroll: function()
9131 //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9133 this.mainHead.setStyle({
9134 'position' : 'relative',
9135 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9141 var scrollHeight = this.mainBody.dom.scrollHeight;
9143 var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9145 var height = this.mainBody.getHeight();
9147 if(scrollHeight - height == scrollTop) {
9149 var total = this.ds.getTotalCount();
9151 if(this.footer.cursor + this.footer.pageSize < total){
9153 this.footer.ds.load({
9155 start : this.footer.cursor + this.footer.pageSize,
9156 limit : this.footer.pageSize
9166 onHeaderChange : function()
9168 var header = this.renderHeader();
9169 var table = this.el.select('table', true).first();
9171 this.mainHead.remove();
9172 this.mainHead = table.createChild(header, this.mainBody, false);
9174 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9175 e.on('click', this.sort, this);
9181 onHiddenChange : function(colModel, colIndex, hidden)
9183 var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9184 var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9186 this.CSS.updateRule(thSelector, "display", "");
9187 this.CSS.updateRule(tdSelector, "display", "");
9190 this.CSS.updateRule(thSelector, "display", "none");
9191 this.CSS.updateRule(tdSelector, "display", "none");
9194 this.onHeaderChange();
9198 setColumnWidth: function(col_index, width)
9200 // width = "md-2 xs-2..."
9201 if(!this.colModel.config[col_index]) {
9205 var w = width.split(" ");
9207 var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9209 var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9212 for(var j = 0; j < w.length; j++) {
9218 var size_cls = w[j].split("-");
9220 if(!Number.isInteger(size_cls[1] * 1)) {
9224 if(!this.colModel.config[col_index][size_cls[0]]) {
9228 if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9232 h_row[0].classList.replace(
9233 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9234 "col-"+size_cls[0]+"-"+size_cls[1]
9237 for(var i = 0; i < rows.length; i++) {
9239 var size_cls = w[j].split("-");
9241 if(!Number.isInteger(size_cls[1] * 1)) {
9245 if(!this.colModel.config[col_index][size_cls[0]]) {
9249 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9253 rows[i].classList.replace(
9254 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9255 "col-"+size_cls[0]+"-"+size_cls[1]
9259 this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9269 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9270 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9278 * @class Roo.bootstrap.TableCell
9279 * @extends Roo.bootstrap.Component
9280 * Bootstrap TableCell class
9281 * @cfg {String} html cell contain text
9282 * @cfg {String} cls cell class
9283 * @cfg {String} tag cell tag (td|th) default td
9284 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9285 * @cfg {String} align Aligns the content in a cell
9286 * @cfg {String} axis Categorizes cells
9287 * @cfg {String} bgcolor Specifies the background color of a cell
9288 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9289 * @cfg {Number} colspan Specifies the number of columns a cell should span
9290 * @cfg {String} headers Specifies one or more header cells a cell is related to
9291 * @cfg {Number} height Sets the height of a cell
9292 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9293 * @cfg {Number} rowspan Sets the number of rows a cell should span
9294 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9295 * @cfg {String} valign Vertical aligns the content in a cell
9296 * @cfg {Number} width Specifies the width of a cell
9299 * Create a new TableCell
9300 * @param {Object} config The config object
9303 Roo.bootstrap.TableCell = function(config){
9304 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
9327 getAutoCreate : function(){
9328 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9348 cfg.align=this.align
9354 cfg.bgcolor=this.bgcolor
9357 cfg.charoff=this.charoff
9360 cfg.colspan=this.colspan
9363 cfg.headers=this.headers
9366 cfg.height=this.height
9369 cfg.nowrap=this.nowrap
9372 cfg.rowspan=this.rowspan
9375 cfg.scope=this.scope
9378 cfg.valign=this.valign
9381 cfg.width=this.width
9400 * @class Roo.bootstrap.TableRow
9401 * @extends Roo.bootstrap.Component
9402 * Bootstrap TableRow class
9403 * @cfg {String} cls row class
9404 * @cfg {String} align Aligns the content in a table row
9405 * @cfg {String} bgcolor Specifies a background color for a table row
9406 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9407 * @cfg {String} valign Vertical aligns the content in a table row
9410 * Create a new TableRow
9411 * @param {Object} config The config object
9414 Roo.bootstrap.TableRow = function(config){
9415 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
9426 getAutoCreate : function(){
9427 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9437 cfg.align = this.align;
9440 cfg.bgcolor = this.bgcolor;
9443 cfg.charoff = this.charoff;
9446 cfg.valign = this.valign;
9464 * @class Roo.bootstrap.TableBody
9465 * @extends Roo.bootstrap.Component
9466 * Bootstrap TableBody class
9467 * @cfg {String} cls element class
9468 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9469 * @cfg {String} align Aligns the content inside the element
9470 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9471 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9474 * Create a new TableBody
9475 * @param {Object} config The config object
9478 Roo.bootstrap.TableBody = function(config){
9479 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
9490 getAutoCreate : function(){
9491 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9505 cfg.align = this.align;
9508 cfg.charoff = this.charoff;
9511 cfg.valign = this.valign;
9518 // initEvents : function()
9525 // this.store = Roo.factory(this.store, Roo.data);
9526 // this.store.on('load', this.onLoad, this);
9528 // this.store.load();
9532 // onLoad: function ()
9534 // this.fireEvent('load', this);
9544 * Ext JS Library 1.1.1
9545 * Copyright(c) 2006-2007, Ext JS, LLC.
9547 * Originally Released Under LGPL - original licence link has changed is not relivant.
9550 * <script type="text/javascript">
9553 // as we use this in bootstrap.
9554 Roo.namespace('Roo.form');
9556 * @class Roo.form.Action
9557 * Internal Class used to handle form actions
9559 * @param {Roo.form.BasicForm} el The form element or its id
9560 * @param {Object} config Configuration options
9565 // define the action interface
9566 Roo.form.Action = function(form, options){
9568 this.options = options || {};
9571 * Client Validation Failed
9574 Roo.form.Action.CLIENT_INVALID = 'client';
9576 * Server Validation Failed
9579 Roo.form.Action.SERVER_INVALID = 'server';
9581 * Connect to Server Failed
9584 Roo.form.Action.CONNECT_FAILURE = 'connect';
9586 * Reading Data from Server Failed
9589 Roo.form.Action.LOAD_FAILURE = 'load';
9591 Roo.form.Action.prototype = {
9593 failureType : undefined,
9594 response : undefined,
9598 run : function(options){
9603 success : function(response){
9608 handleResponse : function(response){
9612 // default connection failure
9613 failure : function(response){
9615 this.response = response;
9616 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9617 this.form.afterAction(this, false);
9620 processResponse : function(response){
9621 this.response = response;
9622 if(!response.responseText){
9625 this.result = this.handleResponse(response);
9629 // utility functions used internally
9630 getUrl : function(appendParams){
9631 var url = this.options.url || this.form.url || this.form.el.dom.action;
9633 var p = this.getParams();
9635 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9641 getMethod : function(){
9642 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9645 getParams : function(){
9646 var bp = this.form.baseParams;
9647 var p = this.options.params;
9649 if(typeof p == "object"){
9650 p = Roo.urlEncode(Roo.applyIf(p, bp));
9651 }else if(typeof p == 'string' && bp){
9652 p += '&' + Roo.urlEncode(bp);
9655 p = Roo.urlEncode(bp);
9660 createCallback : function(){
9662 success: this.success,
9663 failure: this.failure,
9665 timeout: (this.form.timeout*1000),
9666 upload: this.form.fileUpload ? this.success : undefined
9671 Roo.form.Action.Submit = function(form, options){
9672 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9678 haveProgress : false,
9679 uploadComplete : false,
9681 // uploadProgress indicator.
9682 uploadProgress : function()
9684 if (!this.form.progressUrl) {
9688 if (!this.haveProgress) {
9689 Roo.MessageBox.progress("Uploading", "Uploading");
9691 if (this.uploadComplete) {
9692 Roo.MessageBox.hide();
9696 this.haveProgress = true;
9698 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9700 var c = new Roo.data.Connection();
9702 url : this.form.progressUrl,
9707 success : function(req){
9708 //console.log(data);
9712 rdata = Roo.decode(req.responseText)
9714 Roo.log("Invalid data from server..");
9718 if (!rdata || !rdata.success) {
9720 Roo.MessageBox.alert(Roo.encode(rdata));
9723 var data = rdata.data;
9725 if (this.uploadComplete) {
9726 Roo.MessageBox.hide();
9731 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9732 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9735 this.uploadProgress.defer(2000,this);
9738 failure: function(data) {
9739 Roo.log('progress url failed ');
9750 // run get Values on the form, so it syncs any secondary forms.
9751 this.form.getValues();
9753 var o = this.options;
9754 var method = this.getMethod();
9755 var isPost = method == 'POST';
9756 if(o.clientValidation === false || this.form.isValid()){
9758 if (this.form.progressUrl) {
9759 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9760 (new Date() * 1) + '' + Math.random());
9765 Roo.Ajax.request(Roo.apply(this.createCallback(), {
9766 form:this.form.el.dom,
9767 url:this.getUrl(!isPost),
9769 params:isPost ? this.getParams() : null,
9770 isUpload: this.form.fileUpload,
9771 formData : this.form.formData
9774 this.uploadProgress();
9776 }else if (o.clientValidation !== false){ // client validation failed
9777 this.failureType = Roo.form.Action.CLIENT_INVALID;
9778 this.form.afterAction(this, false);
9782 success : function(response)
9784 this.uploadComplete= true;
9785 if (this.haveProgress) {
9786 Roo.MessageBox.hide();
9790 var result = this.processResponse(response);
9791 if(result === true || result.success){
9792 this.form.afterAction(this, true);
9796 this.form.markInvalid(result.errors);
9797 this.failureType = Roo.form.Action.SERVER_INVALID;
9799 this.form.afterAction(this, false);
9801 failure : function(response)
9803 this.uploadComplete= true;
9804 if (this.haveProgress) {
9805 Roo.MessageBox.hide();
9808 this.response = response;
9809 this.failureType = Roo.form.Action.CONNECT_FAILURE;
9810 this.form.afterAction(this, false);
9813 handleResponse : function(response){
9814 if(this.form.errorReader){
9815 var rs = this.form.errorReader.read(response);
9818 for(var i = 0, len = rs.records.length; i < len; i++) {
9819 var r = rs.records[i];
9823 if(errors.length < 1){
9827 success : rs.success,
9833 ret = Roo.decode(response.responseText);
9837 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9847 Roo.form.Action.Load = function(form, options){
9848 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9849 this.reader = this.form.reader;
9852 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9857 Roo.Ajax.request(Roo.apply(
9858 this.createCallback(), {
9859 method:this.getMethod(),
9860 url:this.getUrl(false),
9861 params:this.getParams()
9865 success : function(response){
9867 var result = this.processResponse(response);
9868 if(result === true || !result.success || !result.data){
9869 this.failureType = Roo.form.Action.LOAD_FAILURE;
9870 this.form.afterAction(this, false);
9873 this.form.clearInvalid();
9874 this.form.setValues(result.data);
9875 this.form.afterAction(this, true);
9878 handleResponse : function(response){
9879 if(this.form.reader){
9880 var rs = this.form.reader.read(response);
9881 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9883 success : rs.success,
9887 return Roo.decode(response.responseText);
9891 Roo.form.Action.ACTION_TYPES = {
9892 'load' : Roo.form.Action.Load,
9893 'submit' : Roo.form.Action.Submit
9902 * @class Roo.bootstrap.Form
9903 * @extends Roo.bootstrap.Component
9904 * Bootstrap Form class
9905 * @cfg {String} method GET | POST (default POST)
9906 * @cfg {String} labelAlign top | left (default top)
9907 * @cfg {String} align left | right - for navbars
9908 * @cfg {Boolean} loadMask load mask when submit (default true)
9913 * @param {Object} config The config object
9917 Roo.bootstrap.Form = function(config){
9919 Roo.bootstrap.Form.superclass.constructor.call(this, config);
9921 Roo.bootstrap.Form.popover.apply();
9925 * @event clientvalidation
9926 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9927 * @param {Form} this
9928 * @param {Boolean} valid true if the form has passed client-side validation
9930 clientvalidation: true,
9932 * @event beforeaction
9933 * Fires before any action is performed. Return false to cancel the action.
9934 * @param {Form} this
9935 * @param {Action} action The action to be performed
9939 * @event actionfailed
9940 * Fires when an action fails.
9941 * @param {Form} this
9942 * @param {Action} action The action that failed
9944 actionfailed : true,
9946 * @event actioncomplete
9947 * Fires when an action is completed.
9948 * @param {Form} this
9949 * @param {Action} action The action that completed
9951 actioncomplete : true
9955 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
9958 * @cfg {String} method
9959 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9964 * The URL to use for form actions if one isn't supplied in the action options.
9967 * @cfg {Boolean} fileUpload
9968 * Set to true if this form is a file upload.
9972 * @cfg {Object} baseParams
9973 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9977 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9981 * @cfg {Sting} align (left|right) for navbar forms
9986 activeAction : null,
9989 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9990 * element by passing it or its id or mask the form itself by passing in true.
9993 waitMsgTarget : false,
9998 * @cfg {Boolean} errorMask (true|false) default false
10003 * @cfg {Number} maskOffset Default 100
10008 * @cfg {Boolean} maskBody
10012 getAutoCreate : function(){
10016 method : this.method || 'POST',
10017 id : this.id || Roo.id(),
10020 if (this.parent().xtype.match(/^Nav/)) {
10021 cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10025 if (this.labelAlign == 'left' ) {
10026 cfg.cls += ' form-horizontal';
10032 initEvents : function()
10034 this.el.on('submit', this.onSubmit, this);
10035 // this was added as random key presses on the form where triggering form submit.
10036 this.el.on('keypress', function(e) {
10037 if (e.getCharCode() != 13) {
10040 // we might need to allow it for textareas.. and some other items.
10041 // check e.getTarget().
10043 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10047 Roo.log("keypress blocked");
10049 e.preventDefault();
10055 onSubmit : function(e){
10060 * Returns true if client-side validation on the form is successful.
10063 isValid : function(){
10064 var items = this.getItems();
10066 var target = false;
10068 items.each(function(f){
10074 Roo.log('invalid field: ' + f.name);
10078 if(!target && f.el.isVisible(true)){
10084 if(this.errorMask && !valid){
10085 Roo.bootstrap.Form.popover.mask(this, target);
10092 * Returns true if any fields in this form have changed since their original load.
10095 isDirty : function(){
10097 var items = this.getItems();
10098 items.each(function(f){
10108 * Performs a predefined action (submit or load) or custom actions you define on this form.
10109 * @param {String} actionName The name of the action type
10110 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
10111 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10112 * accept other config options):
10114 Property Type Description
10115 ---------------- --------------- ----------------------------------------------------------------------------------
10116 url String The url for the action (defaults to the form's url)
10117 method String The form method to use (defaults to the form's method, or POST if not defined)
10118 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
10119 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
10120 validate the form on the client (defaults to false)
10122 * @return {BasicForm} this
10124 doAction : function(action, options){
10125 if(typeof action == 'string'){
10126 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10128 if(this.fireEvent('beforeaction', this, action) !== false){
10129 this.beforeAction(action);
10130 action.run.defer(100, action);
10136 beforeAction : function(action){
10137 var o = action.options;
10142 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10144 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10147 // not really supported yet.. ??
10149 //if(this.waitMsgTarget === true){
10150 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10151 //}else if(this.waitMsgTarget){
10152 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10153 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10155 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10161 afterAction : function(action, success){
10162 this.activeAction = null;
10163 var o = action.options;
10168 Roo.get(document.body).unmask();
10174 //if(this.waitMsgTarget === true){
10175 // this.el.unmask();
10176 //}else if(this.waitMsgTarget){
10177 // this.waitMsgTarget.unmask();
10179 // Roo.MessageBox.updateProgress(1);
10180 // Roo.MessageBox.hide();
10187 Roo.callback(o.success, o.scope, [this, action]);
10188 this.fireEvent('actioncomplete', this, action);
10192 // failure condition..
10193 // we have a scenario where updates need confirming.
10194 // eg. if a locking scenario exists..
10195 // we look for { errors : { needs_confirm : true }} in the response.
10197 (typeof(action.result) != 'undefined') &&
10198 (typeof(action.result.errors) != 'undefined') &&
10199 (typeof(action.result.errors.needs_confirm) != 'undefined')
10202 Roo.log("not supported yet");
10205 Roo.MessageBox.confirm(
10206 "Change requires confirmation",
10207 action.result.errorMsg,
10212 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
10222 Roo.callback(o.failure, o.scope, [this, action]);
10223 // show an error message if no failed handler is set..
10224 if (!this.hasListener('actionfailed')) {
10225 Roo.log("need to add dialog support");
10227 Roo.MessageBox.alert("Error",
10228 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10229 action.result.errorMsg :
10230 "Saving Failed, please check your entries or try again"
10235 this.fireEvent('actionfailed', this, action);
10240 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10241 * @param {String} id The value to search for
10244 findField : function(id){
10245 var items = this.getItems();
10246 var field = items.get(id);
10248 items.each(function(f){
10249 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10256 return field || null;
10259 * Mark fields in this form invalid in bulk.
10260 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10261 * @return {BasicForm} this
10263 markInvalid : function(errors){
10264 if(errors instanceof Array){
10265 for(var i = 0, len = errors.length; i < len; i++){
10266 var fieldError = errors[i];
10267 var f = this.findField(fieldError.id);
10269 f.markInvalid(fieldError.msg);
10275 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10276 field.markInvalid(errors[id]);
10280 //Roo.each(this.childForms || [], function (f) {
10281 // f.markInvalid(errors);
10288 * Set values for fields in this form in bulk.
10289 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10290 * @return {BasicForm} this
10292 setValues : function(values){
10293 if(values instanceof Array){ // array of objects
10294 for(var i = 0, len = values.length; i < len; i++){
10296 var f = this.findField(v.id);
10298 f.setValue(v.value);
10299 if(this.trackResetOnLoad){
10300 f.originalValue = f.getValue();
10304 }else{ // object hash
10307 if(typeof values[id] != 'function' && (field = this.findField(id))){
10309 if (field.setFromData &&
10310 field.valueField &&
10311 field.displayField &&
10312 // combos' with local stores can
10313 // be queried via setValue()
10314 // to set their value..
10315 (field.store && !field.store.isLocal)
10319 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10320 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10321 field.setFromData(sd);
10323 } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10325 field.setFromData(values);
10328 field.setValue(values[id]);
10332 if(this.trackResetOnLoad){
10333 field.originalValue = field.getValue();
10339 //Roo.each(this.childForms || [], function (f) {
10340 // f.setValues(values);
10347 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10348 * they are returned as an array.
10349 * @param {Boolean} asString
10352 getValues : function(asString){
10353 //if (this.childForms) {
10354 // copy values from the child forms
10355 // Roo.each(this.childForms, function (f) {
10356 // this.setValues(f.getValues());
10362 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10363 if(asString === true){
10366 return Roo.urlDecode(fs);
10370 * Returns the fields in this form as an object with key/value pairs.
10371 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10374 getFieldValues : function(with_hidden)
10376 var items = this.getItems();
10378 items.each(function(f){
10380 if (!f.getName()) {
10384 var v = f.getValue();
10386 if (f.inputType =='radio') {
10387 if (typeof(ret[f.getName()]) == 'undefined') {
10388 ret[f.getName()] = ''; // empty..
10391 if (!f.el.dom.checked) {
10395 v = f.el.dom.value;
10399 if(f.xtype == 'MoneyField'){
10400 ret[f.currencyName] = f.getCurrency();
10403 // not sure if this supported any more..
10404 if ((typeof(v) == 'object') && f.getRawValue) {
10405 v = f.getRawValue() ; // dates..
10407 // combo boxes where name != hiddenName...
10408 if (f.name !== false && f.name != '' && f.name != f.getName()) {
10409 ret[f.name] = f.getRawValue();
10411 ret[f.getName()] = v;
10418 * Clears all invalid messages in this form.
10419 * @return {BasicForm} this
10421 clearInvalid : function(){
10422 var items = this.getItems();
10424 items.each(function(f){
10432 * Resets this form.
10433 * @return {BasicForm} this
10435 reset : function(){
10436 var items = this.getItems();
10437 items.each(function(f){
10441 Roo.each(this.childForms || [], function (f) {
10449 getItems : function()
10451 var r=new Roo.util.MixedCollection(false, function(o){
10452 return o.id || (o.id = Roo.id());
10454 var iter = function(el) {
10461 Roo.each(el.items,function(e) {
10470 hideFields : function(items)
10472 Roo.each(items, function(i){
10474 var f = this.findField(i);
10485 showFields : function(items)
10487 Roo.each(items, function(i){
10489 var f = this.findField(i);
10502 Roo.apply(Roo.bootstrap.Form, {
10518 intervalID : false,
10524 if(this.isApplied){
10529 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10530 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10531 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10532 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10535 this.maskEl.top.enableDisplayMode("block");
10536 this.maskEl.left.enableDisplayMode("block");
10537 this.maskEl.bottom.enableDisplayMode("block");
10538 this.maskEl.right.enableDisplayMode("block");
10540 this.toolTip = new Roo.bootstrap.Tooltip({
10541 cls : 'roo-form-error-popover',
10543 'left' : ['r-l', [-2,0], 'right'],
10544 'right' : ['l-r', [2,0], 'left'],
10545 'bottom' : ['tl-bl', [0,2], 'top'],
10546 'top' : [ 'bl-tl', [0,-2], 'bottom']
10550 this.toolTip.render(Roo.get(document.body));
10552 this.toolTip.el.enableDisplayMode("block");
10554 Roo.get(document.body).on('click', function(){
10558 Roo.get(document.body).on('touchstart', function(){
10562 this.isApplied = true
10565 mask : function(form, target)
10569 this.target = target;
10571 if(!this.form.errorMask || !target.el){
10575 var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10577 Roo.log(scrollable);
10579 var ot = this.target.el.calcOffsetsTo(scrollable);
10581 var scrollTo = ot[1] - this.form.maskOffset;
10583 scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10585 scrollable.scrollTo('top', scrollTo);
10587 var box = this.target.el.getBox();
10589 var zIndex = Roo.bootstrap.Modal.zIndex++;
10592 this.maskEl.top.setStyle('position', 'absolute');
10593 this.maskEl.top.setStyle('z-index', zIndex);
10594 this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10595 this.maskEl.top.setLeft(0);
10596 this.maskEl.top.setTop(0);
10597 this.maskEl.top.show();
10599 this.maskEl.left.setStyle('position', 'absolute');
10600 this.maskEl.left.setStyle('z-index', zIndex);
10601 this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10602 this.maskEl.left.setLeft(0);
10603 this.maskEl.left.setTop(box.y - this.padding);
10604 this.maskEl.left.show();
10606 this.maskEl.bottom.setStyle('position', 'absolute');
10607 this.maskEl.bottom.setStyle('z-index', zIndex);
10608 this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10609 this.maskEl.bottom.setLeft(0);
10610 this.maskEl.bottom.setTop(box.bottom + this.padding);
10611 this.maskEl.bottom.show();
10613 this.maskEl.right.setStyle('position', 'absolute');
10614 this.maskEl.right.setStyle('z-index', zIndex);
10615 this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10616 this.maskEl.right.setLeft(box.right + this.padding);
10617 this.maskEl.right.setTop(box.y - this.padding);
10618 this.maskEl.right.show();
10620 this.toolTip.bindEl = this.target.el;
10622 this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10624 var tip = this.target.blankText;
10626 if(this.target.getValue() !== '' ) {
10628 if (this.target.invalidText.length) {
10629 tip = this.target.invalidText;
10630 } else if (this.target.regexText.length){
10631 tip = this.target.regexText;
10635 this.toolTip.show(tip);
10637 this.intervalID = window.setInterval(function() {
10638 Roo.bootstrap.Form.popover.unmask();
10641 window.onwheel = function(){ return false;};
10643 (function(){ this.isMasked = true; }).defer(500, this);
10647 unmask : function()
10649 if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10653 this.maskEl.top.setStyle('position', 'absolute');
10654 this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10655 this.maskEl.top.hide();
10657 this.maskEl.left.setStyle('position', 'absolute');
10658 this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10659 this.maskEl.left.hide();
10661 this.maskEl.bottom.setStyle('position', 'absolute');
10662 this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10663 this.maskEl.bottom.hide();
10665 this.maskEl.right.setStyle('position', 'absolute');
10666 this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10667 this.maskEl.right.hide();
10669 this.toolTip.hide();
10671 this.toolTip.el.hide();
10673 window.onwheel = function(){ return true;};
10675 if(this.intervalID){
10676 window.clearInterval(this.intervalID);
10677 this.intervalID = false;
10680 this.isMasked = false;
10690 * Ext JS Library 1.1.1
10691 * Copyright(c) 2006-2007, Ext JS, LLC.
10693 * Originally Released Under LGPL - original licence link has changed is not relivant.
10696 * <script type="text/javascript">
10699 * @class Roo.form.VTypes
10700 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10703 Roo.form.VTypes = function(){
10704 // closure these in so they are only created once.
10705 var alpha = /^[a-zA-Z_]+$/;
10706 var alphanum = /^[a-zA-Z0-9_]+$/;
10707 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10708 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10710 // All these messages and functions are configurable
10713 * The function used to validate email addresses
10714 * @param {String} value The email address
10716 'email' : function(v){
10717 return email.test(v);
10720 * The error text to display when the email validation function returns false
10723 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10725 * The keystroke filter mask to be applied on email input
10728 'emailMask' : /[a-z0-9_\.\-@]/i,
10731 * The function used to validate URLs
10732 * @param {String} value The URL
10734 'url' : function(v){
10735 return url.test(v);
10738 * The error text to display when the url validation function returns false
10741 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10744 * The function used to validate alpha values
10745 * @param {String} value The value
10747 'alpha' : function(v){
10748 return alpha.test(v);
10751 * The error text to display when the alpha validation function returns false
10754 'alphaText' : 'This field should only contain letters and _',
10756 * The keystroke filter mask to be applied on alpha input
10759 'alphaMask' : /[a-z_]/i,
10762 * The function used to validate alphanumeric values
10763 * @param {String} value The value
10765 'alphanum' : function(v){
10766 return alphanum.test(v);
10769 * The error text to display when the alphanumeric validation function returns false
10772 'alphanumText' : 'This field should only contain letters, numbers and _',
10774 * The keystroke filter mask to be applied on alphanumeric input
10777 'alphanumMask' : /[a-z0-9_]/i
10787 * @class Roo.bootstrap.Input
10788 * @extends Roo.bootstrap.Component
10789 * Bootstrap Input class
10790 * @cfg {Boolean} disabled is it disabled
10791 * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType
10792 * @cfg {String} name name of the input
10793 * @cfg {string} fieldLabel - the label associated
10794 * @cfg {string} placeholder - placeholder to put in text.
10795 * @cfg {string} before - input group add on before
10796 * @cfg {string} after - input group add on after
10797 * @cfg {string} size - (lg|sm) or leave empty..
10798 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10799 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10800 * @cfg {Number} md colspan out of 12 for computer-sized screens
10801 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10802 * @cfg {string} value default value of the input
10803 * @cfg {Number} labelWidth set the width of label
10804 * @cfg {Number} labellg set the width of label (1-12)
10805 * @cfg {Number} labelmd set the width of label (1-12)
10806 * @cfg {Number} labelsm set the width of label (1-12)
10807 * @cfg {Number} labelxs set the width of label (1-12)
10808 * @cfg {String} labelAlign (top|left)
10809 * @cfg {Boolean} readOnly Specifies that the field should be read-only
10810 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10811 * @cfg {String} indicatorpos (left|right) default left
10812 * @cfg {String} capture (user|camera) use for file input only. (default empty)
10813 * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10814 * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10816 * @cfg {String} align (left|center|right) Default left
10817 * @cfg {Boolean} forceFeedback (true|false) Default false
10820 * Create a new Input
10821 * @param {Object} config The config object
10824 Roo.bootstrap.Input = function(config){
10826 Roo.bootstrap.Input.superclass.constructor.call(this, config);
10831 * Fires when this field receives input focus.
10832 * @param {Roo.form.Field} this
10837 * Fires when this field loses input focus.
10838 * @param {Roo.form.Field} this
10842 * @event specialkey
10843 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
10844 * {@link Roo.EventObject#getKey} to determine which key was pressed.
10845 * @param {Roo.form.Field} this
10846 * @param {Roo.EventObject} e The event object
10851 * Fires just before the field blurs if the field value has changed.
10852 * @param {Roo.form.Field} this
10853 * @param {Mixed} newValue The new value
10854 * @param {Mixed} oldValue The original value
10859 * Fires after the field has been marked as invalid.
10860 * @param {Roo.form.Field} this
10861 * @param {String} msg The validation message
10866 * Fires after the field has been validated with no errors.
10867 * @param {Roo.form.Field} this
10872 * Fires after the key up
10873 * @param {Roo.form.Field} this
10874 * @param {Roo.EventObject} e The event Object
10879 * Fires after the user pastes into input
10880 * @param {Roo.form.Field} this
10881 * @param {Roo.EventObject} e The event Object
10887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
10889 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10890 automatic validation (defaults to "keyup").
10892 validationEvent : "keyup",
10894 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10896 validateOnBlur : true,
10898 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10900 validationDelay : 250,
10902 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10904 focusClass : "x-form-focus", // not needed???
10908 * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10910 invalidClass : "has-warning",
10913 * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10915 validClass : "has-success",
10918 * @cfg {Boolean} hasFeedback (true|false) default true
10920 hasFeedback : true,
10923 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10925 invalidFeedbackClass : "glyphicon-warning-sign",
10928 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10930 validFeedbackClass : "glyphicon-ok",
10933 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10935 selectOnFocus : false,
10938 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10942 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10947 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10949 disableKeyFilter : false,
10952 * @cfg {Boolean} disabled True to disable the field (defaults to false).
10956 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10960 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10962 blankText : "Please complete this mandatory field",
10965 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10969 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10971 maxLength : Number.MAX_VALUE,
10973 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10975 minLengthText : "The minimum length for this field is {0}",
10977 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10979 maxLengthText : "The maximum length for this field is {0}",
10983 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10984 * If available, this function will be called only after the basic validators all return true, and will be passed the
10985 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10989 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10990 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10991 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
10995 * @cfg {String} regexText -- Depricated - use Invalid Text
11000 * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11006 autocomplete: false,
11010 inputType : 'text',
11013 placeholder: false,
11018 preventMark: false,
11019 isFormField : true,
11022 labelAlign : false,
11025 formatedValue : false,
11026 forceFeedback : false,
11028 indicatorpos : 'left',
11038 parentLabelAlign : function()
11041 while (parent.parent()) {
11042 parent = parent.parent();
11043 if (typeof(parent.labelAlign) !='undefined') {
11044 return parent.labelAlign;
11051 getAutoCreate : function()
11053 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11059 if(this.inputType != 'hidden'){
11060 cfg.cls = 'form-group' //input-group
11066 type : this.inputType,
11067 value : this.value,
11068 cls : 'form-control',
11069 placeholder : this.placeholder || '',
11070 autocomplete : this.autocomplete || 'new-password'
11072 if (this.inputType == 'file') {
11073 input.style = 'overflow:hidden'; // why not in CSS?
11076 if(this.capture.length){
11077 input.capture = this.capture;
11080 if(this.accept.length){
11081 input.accept = this.accept + "/*";
11085 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11088 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11089 input.maxLength = this.maxLength;
11092 if (this.disabled) {
11093 input.disabled=true;
11096 if (this.readOnly) {
11097 input.readonly=true;
11101 input.name = this.name;
11105 input.cls += ' input-' + this.size;
11109 ['xs','sm','md','lg'].map(function(size){
11110 if (settings[size]) {
11111 cfg.cls += ' col-' + size + '-' + settings[size];
11115 var inputblock = input;
11119 cls: 'glyphicon form-control-feedback'
11122 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11125 cls : 'has-feedback',
11133 if (this.before || this.after) {
11136 cls : 'input-group',
11140 if (this.before && typeof(this.before) == 'string') {
11142 inputblock.cn.push({
11144 cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11148 if (this.before && typeof(this.before) == 'object') {
11149 this.before = Roo.factory(this.before);
11151 inputblock.cn.push({
11153 cls : 'roo-input-before input-group-prepend input-group-' +
11154 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11158 inputblock.cn.push(input);
11160 if (this.after && typeof(this.after) == 'string') {
11161 inputblock.cn.push({
11163 cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11167 if (this.after && typeof(this.after) == 'object') {
11168 this.after = Roo.factory(this.after);
11170 inputblock.cn.push({
11172 cls : 'roo-input-after input-group-append input-group-' +
11173 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
11177 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11178 inputblock.cls += ' has-feedback';
11179 inputblock.cn.push(feedback);
11184 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11185 tooltip : 'This field is required'
11187 if (this.allowBlank ) {
11188 indicator.style = this.allowBlank ? ' display:none' : '';
11190 if (align ==='left' && this.fieldLabel.length) {
11192 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
11199 cls : 'control-label col-form-label',
11200 html : this.fieldLabel
11211 var labelCfg = cfg.cn[1];
11212 var contentCfg = cfg.cn[2];
11214 if(this.indicatorpos == 'right'){
11219 cls : 'control-label col-form-label',
11223 html : this.fieldLabel
11237 labelCfg = cfg.cn[0];
11238 contentCfg = cfg.cn[1];
11242 if(this.labelWidth > 12){
11243 labelCfg.style = "width: " + this.labelWidth + 'px';
11246 if(this.labelWidth < 13 && this.labelmd == 0){
11247 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11250 if(this.labellg > 0){
11251 labelCfg.cls += ' col-lg-' + this.labellg;
11252 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11255 if(this.labelmd > 0){
11256 labelCfg.cls += ' col-md-' + this.labelmd;
11257 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11260 if(this.labelsm > 0){
11261 labelCfg.cls += ' col-sm-' + this.labelsm;
11262 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11265 if(this.labelxs > 0){
11266 labelCfg.cls += ' col-xs-' + this.labelxs;
11267 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11271 } else if ( this.fieldLabel.length) {
11278 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11279 tooltip : 'This field is required',
11280 style : this.allowBlank ? ' display:none' : ''
11284 //cls : 'input-group-addon',
11285 html : this.fieldLabel
11293 if(this.indicatorpos == 'right'){
11298 //cls : 'input-group-addon',
11299 html : this.fieldLabel
11304 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11305 tooltip : 'This field is required',
11306 style : this.allowBlank ? ' display:none' : ''
11326 if (this.parentType === 'Navbar' && this.parent().bar) {
11327 cfg.cls += ' navbar-form';
11330 if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11331 // on BS4 we do this only if not form
11332 cfg.cls += ' navbar-form';
11340 * return the real input element.
11342 inputEl: function ()
11344 return this.el.select('input.form-control',true).first();
11347 tooltipEl : function()
11349 return this.inputEl();
11352 indicatorEl : function()
11354 if (Roo.bootstrap.version == 4) {
11355 return false; // not enabled in v4 yet.
11358 var indicator = this.el.select('i.roo-required-indicator',true).first();
11368 setDisabled : function(v)
11370 var i = this.inputEl().dom;
11372 i.removeAttribute('disabled');
11376 i.setAttribute('disabled','true');
11378 initEvents : function()
11381 this.inputEl().on("keydown" , this.fireKey, this);
11382 this.inputEl().on("focus", this.onFocus, this);
11383 this.inputEl().on("blur", this.onBlur, this);
11385 this.inputEl().relayEvent('keyup', this);
11386 this.inputEl().relayEvent('paste', this);
11388 this.indicator = this.indicatorEl();
11390 if(this.indicator){
11391 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? -
11394 // reference to original value for reset
11395 this.originalValue = this.getValue();
11396 //Roo.form.TextField.superclass.initEvents.call(this);
11397 if(this.validationEvent == 'keyup'){
11398 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11399 this.inputEl().on('keyup', this.filterValidation, this);
11401 else if(this.validationEvent !== false){
11402 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11405 if(this.selectOnFocus){
11406 this.on("focus", this.preFocus, this);
11409 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11410 this.inputEl().on("keypress", this.filterKeys, this);
11412 this.inputEl().relayEvent('keypress', this);
11415 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
11416 this.el.on("click", this.autoSize, this);
11419 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11420 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11423 if (typeof(this.before) == 'object') {
11424 this.before.render(this.el.select('.roo-input-before',true).first());
11426 if (typeof(this.after) == 'object') {
11427 this.after.render(this.el.select('.roo-input-after',true).first());
11430 this.inputEl().on('change', this.onChange, this);
11433 filterValidation : function(e){
11434 if(!e.isNavKeyPress()){
11435 this.validationTask.delay(this.validationDelay);
11439 * Validates the field value
11440 * @return {Boolean} True if the value is valid, else false
11442 validate : function(){
11443 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11444 if(this.disabled || this.validateValue(this.getRawValue())){
11449 this.markInvalid();
11455 * Validates a value according to the field's validation rules and marks the field as invalid
11456 * if the validation fails
11457 * @param {Mixed} value The value to validate
11458 * @return {Boolean} True if the value is valid, else false
11460 validateValue : function(value)
11462 if(this.getVisibilityEl().hasClass('hidden')){
11466 if(value.length < 1) { // if it's blank
11467 if(this.allowBlank){
11473 if(value.length < this.minLength){
11476 if(value.length > this.maxLength){
11480 var vt = Roo.form.VTypes;
11481 if(!vt[this.vtype](value, this)){
11485 if(typeof this.validator == "function"){
11486 var msg = this.validator(value);
11490 if (typeof(msg) == 'string') {
11491 this.invalidText = msg;
11495 if(this.regex && !this.regex.test(value)){
11503 fireKey : function(e){
11504 //Roo.log('field ' + e.getKey());
11505 if(e.isNavKeyPress()){
11506 this.fireEvent("specialkey", this, e);
11509 focus : function (selectText){
11511 this.inputEl().focus();
11512 if(selectText === true){
11513 this.inputEl().dom.select();
11519 onFocus : function(){
11520 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11521 // this.el.addClass(this.focusClass);
11523 if(!this.hasFocus){
11524 this.hasFocus = true;
11525 this.startValue = this.getValue();
11526 this.fireEvent("focus", this);
11530 beforeBlur : Roo.emptyFn,
11534 onBlur : function(){
11536 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11537 //this.el.removeClass(this.focusClass);
11539 this.hasFocus = false;
11540 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11543 var v = this.getValue();
11544 if(String(v) !== String(this.startValue)){
11545 this.fireEvent('change', this, v, this.startValue);
11547 this.fireEvent("blur", this);
11550 onChange : function(e)
11552 var v = this.getValue();
11553 if(String(v) !== String(this.startValue)){
11554 this.fireEvent('change', this, v, this.startValue);
11560 * Resets the current field value to the originally loaded value and clears any validation messages
11562 reset : function(){
11563 this.setValue(this.originalValue);
11567 * Returns the name of the field
11568 * @return {Mixed} name The name field
11570 getName: function(){
11574 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
11575 * @return {Mixed} value The field value
11577 getValue : function(){
11579 var v = this.inputEl().getValue();
11584 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
11585 * @return {Mixed} value The field value
11587 getRawValue : function(){
11588 var v = this.inputEl().getValue();
11594 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
11595 * @param {Mixed} value The value to set
11597 setRawValue : function(v){
11598 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11601 selectText : function(start, end){
11602 var v = this.getRawValue();
11604 start = start === undefined ? 0 : start;
11605 end = end === undefined ? v.length : end;
11606 var d = this.inputEl().dom;
11607 if(d.setSelectionRange){
11608 d.setSelectionRange(start, end);
11609 }else if(d.createTextRange){
11610 var range = d.createTextRange();
11611 range.moveStart("character", start);
11612 range.moveEnd("character", v.length-end);
11619 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
11620 * @param {Mixed} value The value to set
11622 setValue : function(v){
11625 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11631 processValue : function(value){
11632 if(this.stripCharsRe){
11633 var newValue = value.replace(this.stripCharsRe, '');
11634 if(newValue !== value){
11635 this.setRawValue(newValue);
11642 preFocus : function(){
11644 if(this.selectOnFocus){
11645 this.inputEl().dom.select();
11648 filterKeys : function(e){
11649 var k = e.getKey();
11650 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11653 var c = e.getCharCode(), cc = String.fromCharCode(c);
11654 if(Roo.isIE && (e.isSpecialKey() || !cc)){
11657 if(!this.maskRe.test(cc)){
11662 * Clear any invalid styles/messages for this field
11664 clearInvalid : function(){
11666 if(!this.el || this.preventMark){ // not rendered
11671 this.el.removeClass([this.invalidClass, 'is-invalid']);
11673 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11675 var feedback = this.el.select('.form-control-feedback', true).first();
11678 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11683 if(this.indicator){
11684 this.indicator.removeClass('visible');
11685 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11688 this.fireEvent('valid', this);
11692 * Mark this field as valid
11694 markValid : function()
11696 if(!this.el || this.preventMark){ // not rendered...
11700 this.el.removeClass([this.invalidClass, this.validClass]);
11701 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11703 var feedback = this.el.select('.form-control-feedback', true).first();
11706 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11709 if(this.indicator){
11710 this.indicator.removeClass('visible');
11711 this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11719 if(this.allowBlank && !this.getRawValue().length){
11722 if (Roo.bootstrap.version == 3) {
11723 this.el.addClass(this.validClass);
11725 this.inputEl().addClass('is-valid');
11728 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11730 var feedback = this.el.select('.form-control-feedback', true).first();
11733 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11734 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11739 this.fireEvent('valid', this);
11743 * Mark this field as invalid
11744 * @param {String} msg The validation message
11746 markInvalid : function(msg)
11748 if(!this.el || this.preventMark){ // not rendered
11752 this.el.removeClass([this.invalidClass, this.validClass]);
11753 this.inputEl().removeClass(['is-valid', 'is-invalid']);
11755 var feedback = this.el.select('.form-control-feedback', true).first();
11758 this.el.select('.form-control-feedback', true).first().removeClass(
11759 [this.invalidFeedbackClass, this.validFeedbackClass]);
11766 if(this.allowBlank && !this.getRawValue().length){
11770 if(this.indicator){
11771 this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11772 this.indicator.addClass('visible');
11774 if (Roo.bootstrap.version == 3) {
11775 this.el.addClass(this.invalidClass);
11777 this.inputEl().addClass('is-invalid');
11782 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11784 var feedback = this.el.select('.form-control-feedback', true).first();
11787 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11789 if(this.getValue().length || this.forceFeedback){
11790 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11797 this.fireEvent('invalid', this, msg);
11800 SafariOnKeyDown : function(event)
11802 // this is a workaround for a password hang bug on chrome/ webkit.
11803 if (this.inputEl().dom.type != 'password') {
11807 var isSelectAll = false;
11809 if(this.inputEl().dom.selectionEnd > 0){
11810 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11812 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11813 event.preventDefault();
11818 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11820 event.preventDefault();
11821 // this is very hacky as keydown always get's upper case.
11823 var cc = String.fromCharCode(event.getCharCode());
11824 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
11828 adjustWidth : function(tag, w){
11829 tag = tag.toLowerCase();
11830 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11831 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11832 if(tag == 'input'){
11835 if(tag == 'textarea'){
11838 }else if(Roo.isOpera){
11839 if(tag == 'input'){
11842 if(tag == 'textarea'){
11850 setFieldLabel : function(v)
11852 if(!this.rendered){
11856 if(this.indicatorEl()){
11857 var ar = this.el.select('label > span',true);
11859 if (ar.elements.length) {
11860 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11861 this.fieldLabel = v;
11865 var br = this.el.select('label',true);
11867 if(br.elements.length) {
11868 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11869 this.fieldLabel = v;
11873 Roo.log('Cannot Found any of label > span || label in input');
11877 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11878 this.fieldLabel = v;
11893 * @class Roo.bootstrap.TextArea
11894 * @extends Roo.bootstrap.Input
11895 * Bootstrap TextArea class
11896 * @cfg {Number} cols Specifies the visible width of a text area
11897 * @cfg {Number} rows Specifies the visible number of lines in a text area
11898 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11899 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11900 * @cfg {string} html text
11903 * Create a new TextArea
11904 * @param {Object} config The config object
11907 Roo.bootstrap.TextArea = function(config){
11908 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11912 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
11922 getAutoCreate : function(){
11924 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11930 if(this.inputType != 'hidden'){
11931 cfg.cls = 'form-group' //input-group
11939 value : this.value || '',
11940 html: this.html || '',
11941 cls : 'form-control',
11942 placeholder : this.placeholder || ''
11946 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11947 input.maxLength = this.maxLength;
11951 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11955 input.cols = this.cols;
11958 if (this.readOnly) {
11959 input.readonly = true;
11963 input.name = this.name;
11967 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11971 ['xs','sm','md','lg'].map(function(size){
11972 if (settings[size]) {
11973 cfg.cls += ' col-' + size + '-' + settings[size];
11977 var inputblock = input;
11979 if(this.hasFeedback && !this.allowBlank){
11983 cls: 'glyphicon form-control-feedback'
11987 cls : 'has-feedback',
11996 if (this.before || this.after) {
11999 cls : 'input-group',
12003 inputblock.cn.push({
12005 cls : 'input-group-addon',
12010 inputblock.cn.push(input);
12012 if(this.hasFeedback && !this.allowBlank){
12013 inputblock.cls += ' has-feedback';
12014 inputblock.cn.push(feedback);
12018 inputblock.cn.push({
12020 cls : 'input-group-addon',
12027 if (align ==='left' && this.fieldLabel.length) {
12032 cls : 'control-label',
12033 html : this.fieldLabel
12044 if(this.labelWidth > 12){
12045 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12048 if(this.labelWidth < 13 && this.labelmd == 0){
12049 this.labelmd = this.labelWidth;
12052 if(this.labellg > 0){
12053 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12054 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12057 if(this.labelmd > 0){
12058 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12059 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12062 if(this.labelsm > 0){
12063 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12064 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12067 if(this.labelxs > 0){
12068 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12069 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12072 } else if ( this.fieldLabel.length) {
12077 //cls : 'input-group-addon',
12078 html : this.fieldLabel
12096 if (this.disabled) {
12097 input.disabled=true;
12104 * return the real textarea element.
12106 inputEl: function ()
12108 return this.el.select('textarea.form-control',true).first();
12112 * Clear any invalid styles/messages for this field
12114 clearInvalid : function()
12117 if(!this.el || this.preventMark){ // not rendered
12121 var label = this.el.select('label', true).first();
12122 var icon = this.el.select('i.fa-star', true).first();
12127 this.el.removeClass( this.validClass);
12128 this.inputEl().removeClass('is-invalid');
12130 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12132 var feedback = this.el.select('.form-control-feedback', true).first();
12135 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12140 this.fireEvent('valid', this);
12144 * Mark this field as valid
12146 markValid : function()
12148 if(!this.el || this.preventMark){ // not rendered
12152 this.el.removeClass([this.invalidClass, this.validClass]);
12153 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12155 var feedback = this.el.select('.form-control-feedback', true).first();
12158 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12161 if(this.disabled || this.allowBlank){
12165 var label = this.el.select('label', true).first();
12166 var icon = this.el.select('i.fa-star', true).first();
12171 if (Roo.bootstrap.version == 3) {
12172 this.el.addClass(this.validClass);
12174 this.inputEl().addClass('is-valid');
12178 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12180 var feedback = this.el.select('.form-control-feedback', true).first();
12183 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12184 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12189 this.fireEvent('valid', this);
12193 * Mark this field as invalid
12194 * @param {String} msg The validation message
12196 markInvalid : function(msg)
12198 if(!this.el || this.preventMark){ // not rendered
12202 this.el.removeClass([this.invalidClass, this.validClass]);
12203 this.inputEl().removeClass(['is-valid', 'is-invalid']);
12205 var feedback = this.el.select('.form-control-feedback', true).first();
12208 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12211 if(this.disabled || this.allowBlank){
12215 var label = this.el.select('label', true).first();
12216 var icon = this.el.select('i.fa-star', true).first();
12218 if(!this.getValue().length && label && !icon){
12219 this.el.createChild({
12221 cls : 'text-danger fa fa-lg fa-star',
12222 tooltip : 'This field is required',
12223 style : 'margin-right:5px;'
12227 if (Roo.bootstrap.version == 3) {
12228 this.el.addClass(this.invalidClass);
12230 this.inputEl().addClass('is-invalid');
12233 // fixme ... this may be depricated need to test..
12234 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12236 var feedback = this.el.select('.form-control-feedback', true).first();
12239 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12241 if(this.getValue().length || this.forceFeedback){
12242 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12249 this.fireEvent('invalid', this, msg);
12257 * trigger field - base class for combo..
12262 * @class Roo.bootstrap.TriggerField
12263 * @extends Roo.bootstrap.Input
12264 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12265 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12266 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12267 * for which you can provide a custom implementation. For example:
12269 var trigger = new Roo.bootstrap.TriggerField();
12270 trigger.onTriggerClick = myTriggerFn;
12271 trigger.applyTo('my-field');
12274 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12275 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12276 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
12277 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12278 * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12281 * Create a new TriggerField.
12282 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12283 * to the base TextField)
12285 Roo.bootstrap.TriggerField = function(config){
12286 this.mimicing = false;
12287 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
12292 * @cfg {String} triggerClass A CSS class to apply to the trigger
12295 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12300 * @cfg {Boolean} removable (true|false) special filter default false
12304 /** @cfg {Boolean} grow @hide */
12305 /** @cfg {Number} growMin @hide */
12306 /** @cfg {Number} growMax @hide */
12312 autoSize: Roo.emptyFn,
12316 deferHeight : true,
12319 actionMode : 'wrap',
12324 getAutoCreate : function(){
12326 var align = this.labelAlign || this.parentLabelAlign();
12331 cls: 'form-group' //input-group
12338 type : this.inputType,
12339 cls : 'form-control',
12340 autocomplete: 'new-password',
12341 placeholder : this.placeholder || ''
12345 input.name = this.name;
12348 input.cls += ' input-' + this.size;
12351 if (this.disabled) {
12352 input.disabled=true;
12355 var inputblock = input;
12357 if(this.hasFeedback && !this.allowBlank){
12361 cls: 'glyphicon form-control-feedback'
12364 if(this.removable && !this.editable ){
12366 cls : 'has-feedback',
12372 cls : 'roo-combo-removable-btn close'
12379 cls : 'has-feedback',
12388 if(this.removable && !this.editable ){
12390 cls : 'roo-removable',
12396 cls : 'roo-combo-removable-btn close'
12403 if (this.before || this.after) {
12406 cls : 'input-group',
12410 inputblock.cn.push({
12412 cls : 'input-group-addon input-group-prepend input-group-text',
12417 inputblock.cn.push(input);
12419 if(this.hasFeedback && !this.allowBlank){
12420 inputblock.cls += ' has-feedback';
12421 inputblock.cn.push(feedback);
12425 inputblock.cn.push({
12427 cls : 'input-group-addon input-group-append input-group-text',
12436 var ibwrap = inputblock;
12441 cls: 'roo-select2-choices',
12445 cls: 'roo-select2-search-field',
12457 cls: 'roo-select2-container input-group',
12462 cls: 'form-hidden-field'
12468 if(!this.multiple && this.showToggleBtn){
12474 if (this.caret != false) {
12477 cls: 'fa fa-' + this.caret
12484 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12486 Roo.bootstrap.version == 3 ? caret : '',
12489 cls: 'combobox-clear',
12503 combobox.cls += ' roo-select2-container-multi';
12507 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12508 tooltip : 'This field is required'
12510 if (Roo.bootstrap.version == 4) {
12513 style : 'display:none'
12518 if (align ==='left' && this.fieldLabel.length) {
12520 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
12527 cls : 'control-label',
12528 html : this.fieldLabel
12540 var labelCfg = cfg.cn[1];
12541 var contentCfg = cfg.cn[2];
12543 if(this.indicatorpos == 'right'){
12548 cls : 'control-label',
12552 html : this.fieldLabel
12566 labelCfg = cfg.cn[0];
12567 contentCfg = cfg.cn[1];
12570 if(this.labelWidth > 12){
12571 labelCfg.style = "width: " + this.labelWidth + 'px';
12574 if(this.labelWidth < 13 && this.labelmd == 0){
12575 this.labelmd = this.labelWidth;
12578 if(this.labellg > 0){
12579 labelCfg.cls += ' col-lg-' + this.labellg;
12580 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12583 if(this.labelmd > 0){
12584 labelCfg.cls += ' col-md-' + this.labelmd;
12585 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12588 if(this.labelsm > 0){
12589 labelCfg.cls += ' col-sm-' + this.labelsm;
12590 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12593 if(this.labelxs > 0){
12594 labelCfg.cls += ' col-xs-' + this.labelxs;
12595 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12598 } else if ( this.fieldLabel.length) {
12599 // Roo.log(" label");
12604 //cls : 'input-group-addon',
12605 html : this.fieldLabel
12613 if(this.indicatorpos == 'right'){
12621 html : this.fieldLabel
12635 // Roo.log(" no label && no align");
12642 ['xs','sm','md','lg'].map(function(size){
12643 if (settings[size]) {
12644 cfg.cls += ' col-' + size + '-' + settings[size];
12655 onResize : function(w, h){
12656 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12657 // if(typeof w == 'number'){
12658 // var x = w - this.trigger.getWidth();
12659 // this.inputEl().setWidth(this.adjustWidth('input', x));
12660 // this.trigger.setStyle('left', x+'px');
12665 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12668 getResizeEl : function(){
12669 return this.inputEl();
12673 getPositionEl : function(){
12674 return this.inputEl();
12678 alignErrorIcon : function(){
12679 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12683 initEvents : function(){
12687 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12688 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12689 if(!this.multiple && this.showToggleBtn){
12690 this.trigger = this.el.select('span.dropdown-toggle',true).first();
12691 if(this.hideTrigger){
12692 this.trigger.setDisplayed(false);
12694 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12698 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12701 if(this.removable && !this.editable && !this.tickable){
12702 var close = this.closeTriggerEl();
12705 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12706 close.on('click', this.removeBtnClick, this, close);
12710 //this.trigger.addClassOnOver('x-form-trigger-over');
12711 //this.trigger.addClassOnClick('x-form-trigger-click');
12714 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12718 closeTriggerEl : function()
12720 var close = this.el.select('.roo-combo-removable-btn', true).first();
12721 return close ? close : false;
12724 removeBtnClick : function(e, h, el)
12726 e.preventDefault();
12728 if(this.fireEvent("remove", this) !== false){
12730 this.fireEvent("afterremove", this)
12734 createList : function()
12736 this.list = Roo.get(document.body).createChild({
12737 tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12738 cls: 'typeahead typeahead-long dropdown-menu shadow',
12739 style: 'display:none'
12742 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12747 initTrigger : function(){
12752 onDestroy : function(){
12754 this.trigger.removeAllListeners();
12755 // this.trigger.remove();
12758 // this.wrap.remove();
12760 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12764 onFocus : function(){
12765 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12767 if(!this.mimicing){
12768 this.wrap.addClass('x-trigger-wrap-focus');
12769 this.mimicing = true;
12770 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12771 if(this.monitorTab){
12772 this.el.on("keydown", this.checkTab, this);
12779 checkTab : function(e){
12780 if(e.getKey() == e.TAB){
12781 this.triggerBlur();
12786 onBlur : function(){
12791 mimicBlur : function(e, t){
12793 if(!this.wrap.contains(t) && this.validateBlur()){
12794 this.triggerBlur();
12800 triggerBlur : function(){
12801 this.mimicing = false;
12802 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12803 if(this.monitorTab){
12804 this.el.un("keydown", this.checkTab, this);
12806 //this.wrap.removeClass('x-trigger-wrap-focus');
12807 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12811 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12812 validateBlur : function(e, t){
12817 onDisable : function(){
12818 this.inputEl().dom.disabled = true;
12819 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12821 // this.wrap.addClass('x-item-disabled');
12826 onEnable : function(){
12827 this.inputEl().dom.disabled = false;
12828 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12830 // this.el.removeClass('x-item-disabled');
12835 onShow : function(){
12836 var ae = this.getActionEl();
12839 ae.dom.style.display = '';
12840 ae.dom.style.visibility = 'visible';
12846 onHide : function(){
12847 var ae = this.getActionEl();
12848 ae.dom.style.display = 'none';
12852 * The function that should handle the trigger's click event. This method does nothing by default until overridden
12853 * by an implementing function.
12855 * @param {EventObject} e
12857 onTriggerClick : Roo.emptyFn
12865 * @class Roo.bootstrap.CardUploader
12866 * @extends Roo.bootstrap.Button
12867 * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12868 * @cfg {Number} errorTimeout default 3000
12869 * @cfg {Array} images an array of ?? Img objects ??? when loading existing files..
12870 * @cfg {Array} html The button text.
12874 * Create a new CardUploader
12875 * @param {Object} config The config object
12878 Roo.bootstrap.CardUploader = function(config){
12882 Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12885 this.fileCollection = new Roo.util.MixedCollection(false,function(r) {
12893 * When a image is clicked on - and needs to display a slideshow or similar..
12894 * @param {Roo.bootstrap.Card} this
12895 * @param {Object} The image information data
12901 * When a the download link is clicked
12902 * @param {Roo.bootstrap.Card} this
12903 * @param {Object} The image information data contains
12910 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input, {
12913 errorTimeout : 3000,
12917 fileCollection : false,
12920 getAutoCreate : function()
12924 cls :'form-group' ,
12929 //cls : 'input-group-addon',
12930 html : this.fieldLabel
12938 value : this.value,
12939 cls : 'd-none form-control'
12944 multiple : 'multiple',
12946 cls : 'd-none roo-card-upload-selector'
12950 cls : 'roo-card-uploader-button-container w-100 mb-2'
12953 cls : 'card-columns roo-card-uploader-container'
12963 getChildContainer : function() /// what children are added to.
12965 return this.containerEl;
12968 getButtonContainer : function() /// what children are added to.
12970 return this.el.select(".roo-card-uploader-button-container").first();
12973 initEvents : function()
12976 Roo.bootstrap.Input.prototype.initEvents.call(this);
12980 xns: Roo.bootstrap,
12983 container_method : 'getButtonContainer' ,
12984 html : this.html, // fix changable?
12987 'click' : function(btn, e) {
12996 this.urlAPI = (window.createObjectURL && window) ||
12997 (window.URL && URL.revokeObjectURL && URL) ||
12998 (window.webkitURL && webkitURL);
13003 this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13005 this.selectorEl.on('change', this.onFileSelected, this);
13008 this.images.forEach(function(img) {
13011 this.images = false;
13013 this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13019 onClick : function(e)
13021 e.preventDefault();
13023 this.selectorEl.dom.click();
13027 onFileSelected : function(e)
13029 e.preventDefault();
13031 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13035 Roo.each(this.selectorEl.dom.files, function(file){
13036 this.addFile(file);
13045 addFile : function(file)
13048 if(typeof(file) === 'string'){
13049 throw "Add file by name?"; // should not happen
13053 if(!file || !this.urlAPI){
13063 var url = _this.urlAPI.createObjectURL( file);
13066 id : Roo.bootstrap.CardUploader.ID--,
13067 is_uploaded : false,
13071 mimetype : file.type,
13079 * addCard - add an Attachment to the uploader
13080 * @param data - the data about the image to upload
13084 title : "Title of file",
13085 is_uploaded : false,
13086 src : "http://.....",
13087 srcfile : { the File upload object },
13088 mimetype : file.type,
13091 .. any other data...
13097 addCard : function (data)
13099 // hidden input element?
13100 // if the file is not an image...
13101 //then we need to use something other that and header_image
13106 xns : Roo.bootstrap,
13107 xtype : 'CardFooter',
13110 xns : Roo.bootstrap,
13116 xns : Roo.bootstrap,
13118 html : String.format("<small>{0}</small>", data.title),
13119 cls : 'col-10 text-left',
13124 click : function() {
13126 t.fireEvent( "download", t, data );
13132 xns : Roo.bootstrap,
13134 style: 'max-height: 28px; ',
13140 click : function() {
13141 t.removeCard(data.id)
13153 var cn = this.addxtype(
13156 xns : Roo.bootstrap,
13159 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13160 header_image : data.mimetype.match(/image/) ? data.src : data.preview,
13161 header_image_fit_square: true, // fixme - we probably need to use the 'Img' element to do stuff like this.
13166 initEvents : function() {
13167 Roo.bootstrap.Card.prototype.initEvents.call(this);
13169 this.imgEl = this.el.select('.card-img-top').first();
13171 this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13172 this.imgEl.set({ 'pointer' : 'cursor' });
13175 this.getCardFooter().addClass('p-1');
13182 // dont' really need ot update items.
13183 // this.items.push(cn);
13184 this.fileCollection.add(cn);
13186 if (!data.srcfile) {
13187 this.updateInput();
13192 var reader = new FileReader();
13193 reader.addEventListener("load", function() {
13194 data.srcdata = reader.result;
13197 reader.readAsDataURL(data.srcfile);
13202 removeCard : function(id)
13205 var card = this.fileCollection.get(id);
13206 card.data.is_deleted = 1;
13207 card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13208 //this.fileCollection.remove(card);
13209 //this.items = this.items.filter(function(e) { return e != card });
13210 // dont' really need ot update items.
13211 card.el.dom.parentNode.removeChild(card.el.dom);
13212 this.updateInput();
13218 this.fileCollection.each(function(card) {
13219 if (card.el.dom && card.el.dom.parentNode) {
13220 card.el.dom.parentNode.removeChild(card.el.dom);
13223 this.fileCollection.clear();
13224 this.updateInput();
13227 updateInput : function()
13230 this.fileCollection.each(function(e) {
13234 this.inputEl().dom.value = JSON.stringify(data);
13244 Roo.bootstrap.CardUploader.ID = -1;/*
13246 * Ext JS Library 1.1.1
13247 * Copyright(c) 2006-2007, Ext JS, LLC.
13249 * Originally Released Under LGPL - original licence link has changed is not relivant.
13252 * <script type="text/javascript">
13257 * @class Roo.data.SortTypes
13259 * Defines the default sorting (casting?) comparison functions used when sorting data.
13261 Roo.data.SortTypes = {
13263 * Default sort that does nothing
13264 * @param {Mixed} s The value being converted
13265 * @return {Mixed} The comparison value
13267 none : function(s){
13272 * The regular expression used to strip tags
13276 stripTagsRE : /<\/?[^>]+>/gi,
13279 * Strips all HTML tags to sort on text only
13280 * @param {Mixed} s The value being converted
13281 * @return {String} The comparison value
13283 asText : function(s){
13284 return String(s).replace(this.stripTagsRE, "");
13288 * Strips all HTML tags to sort on text only - Case insensitive
13289 * @param {Mixed} s The value being converted
13290 * @return {String} The comparison value
13292 asUCText : function(s){
13293 return String(s).toUpperCase().replace(this.stripTagsRE, "");
13297 * Case insensitive string
13298 * @param {Mixed} s The value being converted
13299 * @return {String} The comparison value
13301 asUCString : function(s) {
13302 return String(s).toUpperCase();
13307 * @param {Mixed} s The value being converted
13308 * @return {Number} The comparison value
13310 asDate : function(s) {
13314 if(s instanceof Date){
13315 return s.getTime();
13317 return Date.parse(String(s));
13322 * @param {Mixed} s The value being converted
13323 * @return {Float} The comparison value
13325 asFloat : function(s) {
13326 var val = parseFloat(String(s).replace(/,/g, ""));
13335 * @param {Mixed} s The value being converted
13336 * @return {Number} The comparison value
13338 asInt : function(s) {
13339 var val = parseInt(String(s).replace(/,/g, ""));
13347 * Ext JS Library 1.1.1
13348 * Copyright(c) 2006-2007, Ext JS, LLC.
13350 * Originally Released Under LGPL - original licence link has changed is not relivant.
13353 * <script type="text/javascript">
13357 * @class Roo.data.Record
13358 * Instances of this class encapsulate both record <em>definition</em> information, and record
13359 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13360 * to access Records cached in an {@link Roo.data.Store} object.<br>
13362 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13363 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13366 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13368 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13369 * {@link #create}. The parameters are the same.
13370 * @param {Array} data An associative Array of data values keyed by the field name.
13371 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13372 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13373 * not specified an integer id is generated.
13375 Roo.data.Record = function(data, id){
13376 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13381 * Generate a constructor for a specific record layout.
13382 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13383 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13384 * Each field definition object may contain the following properties: <ul>
13385 * <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,
13386 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13387 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13388 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13389 * is being used, then this is a string containing the javascript expression to reference the data relative to
13390 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13391 * to the data item relative to the record element. If the mapping expression is the same as the field name,
13392 * this may be omitted.</p></li>
13393 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13394 * <ul><li>auto (Default, implies no conversion)</li>
13399 * <li>date</li></ul></p></li>
13400 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13401 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13402 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13403 * by the Reader into an object that will be stored in the Record. It is passed the
13404 * following parameters:<ul>
13405 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13407 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13409 * <br>usage:<br><pre><code>
13410 var TopicRecord = Roo.data.Record.create(
13411 {name: 'title', mapping: 'topic_title'},
13412 {name: 'author', mapping: 'username'},
13413 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13414 {name: 'lastPost', mapping: 'post_time', type: 'date'},
13415 {name: 'lastPoster', mapping: 'user2'},
13416 {name: 'excerpt', mapping: 'post_text'}
13419 var myNewRecord = new TopicRecord({
13420 title: 'Do my job please',
13423 lastPost: new Date(),
13424 lastPoster: 'Animal',
13425 excerpt: 'No way dude!'
13427 myStore.add(myNewRecord);
13432 Roo.data.Record.create = function(o){
13433 var f = function(){
13434 f.superclass.constructor.apply(this, arguments);
13436 Roo.extend(f, Roo.data.Record);
13437 var p = f.prototype;
13438 p.fields = new Roo.util.MixedCollection(false, function(field){
13441 for(var i = 0, len = o.length; i < len; i++){
13442 p.fields.add(new Roo.data.Field(o[i]));
13444 f.getField = function(name){
13445 return p.fields.get(name);
13450 Roo.data.Record.AUTO_ID = 1000;
13451 Roo.data.Record.EDIT = 'edit';
13452 Roo.data.Record.REJECT = 'reject';
13453 Roo.data.Record.COMMIT = 'commit';
13455 Roo.data.Record.prototype = {
13457 * Readonly flag - true if this record has been modified.
13466 join : function(store){
13467 this.store = store;
13471 * Set the named field to the specified value.
13472 * @param {String} name The name of the field to set.
13473 * @param {Object} value The value to set the field to.
13475 set : function(name, value){
13476 if(this.data[name] == value){
13480 if(!this.modified){
13481 this.modified = {};
13483 if(typeof this.modified[name] == 'undefined'){
13484 this.modified[name] = this.data[name];
13486 this.data[name] = value;
13487 if(!this.editing && this.store){
13488 this.store.afterEdit(this);
13493 * Get the value of the named field.
13494 * @param {String} name The name of the field to get the value of.
13495 * @return {Object} The value of the field.
13497 get : function(name){
13498 return this.data[name];
13502 beginEdit : function(){
13503 this.editing = true;
13504 this.modified = {};
13508 cancelEdit : function(){
13509 this.editing = false;
13510 delete this.modified;
13514 endEdit : function(){
13515 this.editing = false;
13516 if(this.dirty && this.store){
13517 this.store.afterEdit(this);
13522 * Usually called by the {@link Roo.data.Store} which owns the Record.
13523 * Rejects all changes made to the Record since either creation, or the last commit operation.
13524 * Modified fields are reverted to their original values.
13526 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13527 * of reject operations.
13529 reject : function(){
13530 var m = this.modified;
13532 if(typeof m[n] != "function"){
13533 this.data[n] = m[n];
13536 this.dirty = false;
13537 delete this.modified;
13538 this.editing = false;
13540 this.store.afterReject(this);
13545 * Usually called by the {@link Roo.data.Store} which owns the Record.
13546 * Commits all changes made to the Record since either creation, or the last commit operation.
13548 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13549 * of commit operations.
13551 commit : function(){
13552 this.dirty = false;
13553 delete this.modified;
13554 this.editing = false;
13556 this.store.afterCommit(this);
13561 hasError : function(){
13562 return this.error != null;
13566 clearError : function(){
13571 * Creates a copy of this record.
13572 * @param {String} id (optional) A new record id if you don't want to use this record's id
13575 copy : function(newId) {
13576 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13580 * Ext JS Library 1.1.1
13581 * Copyright(c) 2006-2007, Ext JS, LLC.
13583 * Originally Released Under LGPL - original licence link has changed is not relivant.
13586 * <script type="text/javascript">
13592 * @class Roo.data.Store
13593 * @extends Roo.util.Observable
13594 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13595 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13597 * 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
13598 * has no knowledge of the format of the data returned by the Proxy.<br>
13600 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13601 * instances from the data object. These records are cached and made available through accessor functions.
13603 * Creates a new Store.
13604 * @param {Object} config A config object containing the objects needed for the Store to access data,
13605 * and read the data into Records.
13607 Roo.data.Store = function(config){
13608 this.data = new Roo.util.MixedCollection(false);
13609 this.data.getKey = function(o){
13612 this.baseParams = {};
13614 this.paramNames = {
13619 "multisort" : "_multisort"
13622 if(config && config.data){
13623 this.inlineData = config.data;
13624 delete config.data;
13627 Roo.apply(this, config);
13629 if(this.reader){ // reader passed
13630 this.reader = Roo.factory(this.reader, Roo.data);
13631 this.reader.xmodule = this.xmodule || false;
13632 if(!this.recordType){
13633 this.recordType = this.reader.recordType;
13635 if(this.reader.onMetaChange){
13636 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13640 if(this.recordType){
13641 this.fields = this.recordType.prototype.fields;
13643 this.modified = [];
13647 * @event datachanged
13648 * Fires when the data cache has changed, and a widget which is using this Store
13649 * as a Record cache should refresh its view.
13650 * @param {Store} this
13652 datachanged : true,
13654 * @event metachange
13655 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13656 * @param {Store} this
13657 * @param {Object} meta The JSON metadata
13662 * Fires when Records have been added to the Store
13663 * @param {Store} this
13664 * @param {Roo.data.Record[]} records The array of Records added
13665 * @param {Number} index The index at which the record(s) were added
13670 * Fires when a Record has been removed from the Store
13671 * @param {Store} this
13672 * @param {Roo.data.Record} record The Record that was removed
13673 * @param {Number} index The index at which the record was removed
13678 * Fires when a Record has been updated
13679 * @param {Store} this
13680 * @param {Roo.data.Record} record The Record that was updated
13681 * @param {String} operation The update operation being performed. Value may be one of:
13683 Roo.data.Record.EDIT
13684 Roo.data.Record.REJECT
13685 Roo.data.Record.COMMIT
13691 * Fires when the data cache has been cleared.
13692 * @param {Store} this
13696 * @event beforeload
13697 * Fires before a request is made for a new data object. If the beforeload handler returns false
13698 * the load action will be canceled.
13699 * @param {Store} this
13700 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13704 * @event beforeloadadd
13705 * Fires after a new set of Records has been loaded.
13706 * @param {Store} this
13707 * @param {Roo.data.Record[]} records The Records that were loaded
13708 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13710 beforeloadadd : true,
13713 * Fires after a new set of Records has been loaded, before they are added to the store.
13714 * @param {Store} this
13715 * @param {Roo.data.Record[]} records The Records that were loaded
13716 * @param {Object} options The loading options that were specified (see {@link #load} for details)
13717 * @params {Object} return from reader
13721 * @event loadexception
13722 * Fires if an exception occurs in the Proxy during loading.
13723 * Called with the signature of the Proxy's "loadexception" event.
13724 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13727 * @param {Object} return from JsonData.reader() - success, totalRecords, records
13728 * @param {Object} load options
13729 * @param {Object} jsonData from your request (normally this contains the Exception)
13731 loadexception : true
13735 this.proxy = Roo.factory(this.proxy, Roo.data);
13736 this.proxy.xmodule = this.xmodule || false;
13737 this.relayEvents(this.proxy, ["loadexception"]);
13739 this.sortToggle = {};
13740 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13742 Roo.data.Store.superclass.constructor.call(this);
13744 if(this.inlineData){
13745 this.loadData(this.inlineData);
13746 delete this.inlineData;
13750 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13752 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
13753 * without a remote query - used by combo/forms at present.
13757 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13760 * @cfg {Array} data Inline data to be loaded when the store is initialized.
13763 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13764 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13767 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13768 * on any HTTP request
13771 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13774 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13778 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13779 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13781 remoteSort : false,
13784 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13785 * loaded or when a record is removed. (defaults to false).
13787 pruneModifiedRecords : false,
13790 lastOptions : null,
13793 * Add Records to the Store and fires the add event.
13794 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13796 add : function(records){
13797 records = [].concat(records);
13798 for(var i = 0, len = records.length; i < len; i++){
13799 records[i].join(this);
13801 var index = this.data.length;
13802 this.data.addAll(records);
13803 this.fireEvent("add", this, records, index);
13807 * Remove a Record from the Store and fires the remove event.
13808 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13810 remove : function(record){
13811 var index = this.data.indexOf(record);
13812 this.data.removeAt(index);
13814 if(this.pruneModifiedRecords){
13815 this.modified.remove(record);
13817 this.fireEvent("remove", this, record, index);
13821 * Remove all Records from the Store and fires the clear event.
13823 removeAll : function(){
13825 if(this.pruneModifiedRecords){
13826 this.modified = [];
13828 this.fireEvent("clear", this);
13832 * Inserts Records to the Store at the given index and fires the add event.
13833 * @param {Number} index The start index at which to insert the passed Records.
13834 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13836 insert : function(index, records){
13837 records = [].concat(records);
13838 for(var i = 0, len = records.length; i < len; i++){
13839 this.data.insert(index, records[i]);
13840 records[i].join(this);
13842 this.fireEvent("add", this, records, index);
13846 * Get the index within the cache of the passed Record.
13847 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13848 * @return {Number} The index of the passed Record. Returns -1 if not found.
13850 indexOf : function(record){
13851 return this.data.indexOf(record);
13855 * Get the index within the cache of the Record with the passed id.
13856 * @param {String} id The id of the Record to find.
13857 * @return {Number} The index of the Record. Returns -1 if not found.
13859 indexOfId : function(id){
13860 return this.data.indexOfKey(id);
13864 * Get the Record with the specified id.
13865 * @param {String} id The id of the Record to find.
13866 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13868 getById : function(id){
13869 return this.data.key(id);
13873 * Get the Record at the specified index.
13874 * @param {Number} index The index of the Record to find.
13875 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13877 getAt : function(index){
13878 return this.data.itemAt(index);
13882 * Returns a range of Records between specified indices.
13883 * @param {Number} startIndex (optional) The starting index (defaults to 0)
13884 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13885 * @return {Roo.data.Record[]} An array of Records
13887 getRange : function(start, end){
13888 return this.data.getRange(start, end);
13892 storeOptions : function(o){
13893 o = Roo.apply({}, o);
13896 this.lastOptions = o;
13900 * Loads the Record cache from the configured Proxy using the configured Reader.
13902 * If using remote paging, then the first load call must specify the <em>start</em>
13903 * and <em>limit</em> properties in the options.params property to establish the initial
13904 * position within the dataset, and the number of Records to cache on each read from the Proxy.
13906 * <strong>It is important to note that for remote data sources, loading is asynchronous,
13907 * and this call will return before the new data has been loaded. Perform any post-processing
13908 * in a callback function, or in a "load" event handler.</strong>
13910 * @param {Object} options An object containing properties which control loading options:<ul>
13911 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13912 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13913 * passed the following arguments:<ul>
13914 * <li>r : Roo.data.Record[]</li>
13915 * <li>options: Options object from the load call</li>
13916 * <li>success: Boolean success indicator</li></ul></li>
13917 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13918 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13921 load : function(options){
13922 options = options || {};
13923 if(this.fireEvent("beforeload", this, options) !== false){
13924 this.storeOptions(options);
13925 var p = Roo.apply(options.params || {}, this.baseParams);
13926 // if meta was not loaded from remote source.. try requesting it.
13927 if (!this.reader.metaFromRemote) {
13928 p._requestMeta = 1;
13930 if(this.sortInfo && this.remoteSort){
13931 var pn = this.paramNames;
13932 p[pn["sort"]] = this.sortInfo.field;
13933 p[pn["dir"]] = this.sortInfo.direction;
13935 if (this.multiSort) {
13936 var pn = this.paramNames;
13937 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13940 this.proxy.load(p, this.reader, this.loadRecords, this, options);
13945 * Reloads the Record cache from the configured Proxy using the configured Reader and
13946 * the options from the last load operation performed.
13947 * @param {Object} options (optional) An object containing properties which may override the options
13948 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13949 * the most recently used options are reused).
13951 reload : function(options){
13952 this.load(Roo.applyIf(options||{}, this.lastOptions));
13956 // Called as a callback by the Reader during a load operation.
13957 loadRecords : function(o, options, success){
13958 if(!o || success === false){
13959 if(success !== false){
13960 this.fireEvent("load", this, [], options, o);
13962 if(options.callback){
13963 options.callback.call(options.scope || this, [], options, false);
13967 // if data returned failure - throw an exception.
13968 if (o.success === false) {
13969 // show a message if no listener is registered.
13970 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13971 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13973 // loadmask wil be hooked into this..
13974 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13977 var r = o.records, t = o.totalRecords || r.length;
13979 this.fireEvent("beforeloadadd", this, r, options, o);
13981 if(!options || options.add !== true){
13982 if(this.pruneModifiedRecords){
13983 this.modified = [];
13985 for(var i = 0, len = r.length; i < len; i++){
13989 this.data = this.snapshot;
13990 delete this.snapshot;
13993 this.data.addAll(r);
13994 this.totalLength = t;
13996 this.fireEvent("datachanged", this);
13998 this.totalLength = Math.max(t, this.data.length+r.length);
14002 if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14004 var e = new Roo.data.Record({});
14006 e.set(this.parent.displayField, this.parent.emptyTitle);
14007 e.set(this.parent.valueField, '');
14012 this.fireEvent("load", this, r, options, o);
14013 if(options.callback){
14014 options.callback.call(options.scope || this, r, options, true);
14020 * Loads data from a passed data block. A Reader which understands the format of the data
14021 * must have been configured in the constructor.
14022 * @param {Object} data The data block from which to read the Records. The format of the data expected
14023 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14024 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14026 loadData : function(o, append){
14027 var r = this.reader.readRecords(o);
14028 this.loadRecords(r, {add: append}, true);
14032 * using 'cn' the nested child reader read the child array into it's child stores.
14033 * @param {Object} rec The record with a 'children array
14035 loadDataFromChildren : function(rec)
14037 this.loadData(this.reader.toLoadData(rec));
14042 * Gets the number of cached records.
14044 * <em>If using paging, this may not be the total size of the dataset. If the data object
14045 * used by the Reader contains the dataset size, then the getTotalCount() function returns
14046 * the data set size</em>
14048 getCount : function(){
14049 return this.data.length || 0;
14053 * Gets the total number of records in the dataset as returned by the server.
14055 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14056 * the dataset size</em>
14058 getTotalCount : function(){
14059 return this.totalLength || 0;
14063 * Returns the sort state of the Store as an object with two properties:
14065 field {String} The name of the field by which the Records are sorted
14066 direction {String} The sort order, "ASC" or "DESC"
14069 getSortState : function(){
14070 return this.sortInfo;
14074 applySort : function(){
14075 if(this.sortInfo && !this.remoteSort){
14076 var s = this.sortInfo, f = s.field;
14077 var st = this.fields.get(f).sortType;
14078 var fn = function(r1, r2){
14079 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14080 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14082 this.data.sort(s.direction, fn);
14083 if(this.snapshot && this.snapshot != this.data){
14084 this.snapshot.sort(s.direction, fn);
14090 * Sets the default sort column and order to be used by the next load operation.
14091 * @param {String} fieldName The name of the field to sort by.
14092 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14094 setDefaultSort : function(field, dir){
14095 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14099 * Sort the Records.
14100 * If remote sorting is used, the sort is performed on the server, and the cache is
14101 * reloaded. If local sorting is used, the cache is sorted internally.
14102 * @param {String} fieldName The name of the field to sort by.
14103 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14105 sort : function(fieldName, dir){
14106 var f = this.fields.get(fieldName);
14108 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14110 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14111 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14116 this.sortToggle[f.name] = dir;
14117 this.sortInfo = {field: f.name, direction: dir};
14118 if(!this.remoteSort){
14120 this.fireEvent("datachanged", this);
14122 this.load(this.lastOptions);
14127 * Calls the specified function for each of the Records in the cache.
14128 * @param {Function} fn The function to call. The Record is passed as the first parameter.
14129 * Returning <em>false</em> aborts and exits the iteration.
14130 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14132 each : function(fn, scope){
14133 this.data.each(fn, scope);
14137 * Gets all records modified since the last commit. Modified records are persisted across load operations
14138 * (e.g., during paging).
14139 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14141 getModifiedRecords : function(){
14142 return this.modified;
14146 createFilterFn : function(property, value, anyMatch){
14147 if(!value.exec){ // not a regex
14148 value = String(value);
14149 if(value.length == 0){
14152 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14154 return function(r){
14155 return value.test(r.data[property]);
14160 * Sums the value of <i>property</i> for each record between start and end and returns the result.
14161 * @param {String} property A field on your records
14162 * @param {Number} start The record index to start at (defaults to 0)
14163 * @param {Number} end The last record index to include (defaults to length - 1)
14164 * @return {Number} The sum
14166 sum : function(property, start, end){
14167 var rs = this.data.items, v = 0;
14168 start = start || 0;
14169 end = (end || end === 0) ? end : rs.length-1;
14171 for(var i = start; i <= end; i++){
14172 v += (rs[i].data[property] || 0);
14178 * Filter the records by a specified property.
14179 * @param {String} field A field on your records
14180 * @param {String/RegExp} value Either a string that the field
14181 * should start with or a RegExp to test against the field
14182 * @param {Boolean} anyMatch True to match any part not just the beginning
14184 filter : function(property, value, anyMatch){
14185 var fn = this.createFilterFn(property, value, anyMatch);
14186 return fn ? this.filterBy(fn) : this.clearFilter();
14190 * Filter 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,
14192 * otherwise it is filtered.
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)
14196 filterBy : function(fn, scope){
14197 this.snapshot = this.snapshot || this.data;
14198 this.data = this.queryBy(fn, scope||this);
14199 this.fireEvent("datachanged", this);
14203 * Query the records by a specified property.
14204 * @param {String} field A field on your records
14205 * @param {String/RegExp} value Either a string that the field
14206 * should start with or a RegExp to test against the field
14207 * @param {Boolean} anyMatch True to match any part not just the beginning
14208 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14210 query : function(property, value, anyMatch){
14211 var fn = this.createFilterFn(property, value, anyMatch);
14212 return fn ? this.queryBy(fn) : this.data.clone();
14216 * Query by a function. The specified function will be called with each
14217 * record in this data source. If the function returns true the record is included
14219 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14220 * @param {Object} scope (optional) The scope of the function (defaults to this)
14221 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14223 queryBy : function(fn, scope){
14224 var data = this.snapshot || this.data;
14225 return data.filterBy(fn, scope||this);
14229 * Collects unique values for a particular dataIndex from this store.
14230 * @param {String} dataIndex The property to collect
14231 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14232 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14233 * @return {Array} An array of the unique values
14235 collect : function(dataIndex, allowNull, bypassFilter){
14236 var d = (bypassFilter === true && this.snapshot) ?
14237 this.snapshot.items : this.data.items;
14238 var v, sv, r = [], l = {};
14239 for(var i = 0, len = d.length; i < len; i++){
14240 v = d[i].data[dataIndex];
14242 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14251 * Revert to a view of the Record cache with no filtering applied.
14252 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14254 clearFilter : function(suppressEvent){
14255 if(this.snapshot && this.snapshot != this.data){
14256 this.data = this.snapshot;
14257 delete this.snapshot;
14258 if(suppressEvent !== true){
14259 this.fireEvent("datachanged", this);
14265 afterEdit : function(record){
14266 if(this.modified.indexOf(record) == -1){
14267 this.modified.push(record);
14269 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14273 afterReject : function(record){
14274 this.modified.remove(record);
14275 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14279 afterCommit : function(record){
14280 this.modified.remove(record);
14281 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14285 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14286 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14288 commitChanges : function(){
14289 var m = this.modified.slice(0);
14290 this.modified = [];
14291 for(var i = 0, len = m.length; i < len; i++){
14297 * Cancel outstanding changes on all changed records.
14299 rejectChanges : function(){
14300 var m = this.modified.slice(0);
14301 this.modified = [];
14302 for(var i = 0, len = m.length; i < len; i++){
14307 onMetaChange : function(meta, rtype, o){
14308 this.recordType = rtype;
14309 this.fields = rtype.prototype.fields;
14310 delete this.snapshot;
14311 this.sortInfo = meta.sortInfo || this.sortInfo;
14312 this.modified = [];
14313 this.fireEvent('metachange', this, this.reader.meta);
14316 moveIndex : function(data, type)
14318 var index = this.indexOf(data);
14320 var newIndex = index + type;
14324 this.insert(newIndex, data);
14329 * Ext JS Library 1.1.1
14330 * Copyright(c) 2006-2007, Ext JS, LLC.
14332 * Originally Released Under LGPL - original licence link has changed is not relivant.
14335 * <script type="text/javascript">
14339 * @class Roo.data.SimpleStore
14340 * @extends Roo.data.Store
14341 * Small helper class to make creating Stores from Array data easier.
14342 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14343 * @cfg {Array} fields An array of field definition objects, or field name strings.
14344 * @cfg {Object} an existing reader (eg. copied from another store)
14345 * @cfg {Array} data The multi-dimensional array of data
14347 * @param {Object} config
14349 Roo.data.SimpleStore = function(config)
14351 Roo.data.SimpleStore.superclass.constructor.call(this, {
14353 reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14356 Roo.data.Record.create(config.fields)
14358 proxy : new Roo.data.MemoryProxy(config.data)
14362 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14364 * Ext JS Library 1.1.1
14365 * Copyright(c) 2006-2007, Ext JS, LLC.
14367 * Originally Released Under LGPL - original licence link has changed is not relivant.
14370 * <script type="text/javascript">
14375 * @extends Roo.data.Store
14376 * @class Roo.data.JsonStore
14377 * Small helper class to make creating Stores for JSON data easier. <br/>
14379 var store = new Roo.data.JsonStore({
14380 url: 'get-images.php',
14382 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14385 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14386 * JsonReader and HttpProxy (unless inline data is provided).</b>
14387 * @cfg {Array} fields An array of field definition objects, or field name strings.
14389 * @param {Object} config
14391 Roo.data.JsonStore = function(c){
14392 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14393 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14394 reader: new Roo.data.JsonReader(c, c.fields)
14397 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14399 * Ext JS Library 1.1.1
14400 * Copyright(c) 2006-2007, Ext JS, LLC.
14402 * Originally Released Under LGPL - original licence link has changed is not relivant.
14405 * <script type="text/javascript">
14409 Roo.data.Field = function(config){
14410 if(typeof config == "string"){
14411 config = {name: config};
14413 Roo.apply(this, config);
14416 this.type = "auto";
14419 var st = Roo.data.SortTypes;
14420 // named sortTypes are supported, here we look them up
14421 if(typeof this.sortType == "string"){
14422 this.sortType = st[this.sortType];
14425 // set default sortType for strings and dates
14426 if(!this.sortType){
14429 this.sortType = st.asUCString;
14432 this.sortType = st.asDate;
14435 this.sortType = st.none;
14440 var stripRe = /[\$,%]/g;
14442 // prebuilt conversion function for this field, instead of
14443 // switching every time we're reading a value
14445 var cv, dateFormat = this.dateFormat;
14450 cv = function(v){ return v; };
14453 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14457 return v !== undefined && v !== null && v !== '' ?
14458 parseInt(String(v).replace(stripRe, ""), 10) : '';
14463 return v !== undefined && v !== null && v !== '' ?
14464 parseFloat(String(v).replace(stripRe, ""), 10) : '';
14469 cv = function(v){ return v === true || v === "true" || v == 1; };
14476 if(v instanceof Date){
14480 if(dateFormat == "timestamp"){
14481 return new Date(v*1000);
14483 return Date.parseDate(v, dateFormat);
14485 var parsed = Date.parse(v);
14486 return parsed ? new Date(parsed) : null;
14495 Roo.data.Field.prototype = {
14503 * Ext JS Library 1.1.1
14504 * Copyright(c) 2006-2007, Ext JS, LLC.
14506 * Originally Released Under LGPL - original licence link has changed is not relivant.
14509 * <script type="text/javascript">
14512 // Base class for reading structured data from a data source. This class is intended to be
14513 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14516 * @class Roo.data.DataReader
14517 * Base class for reading structured data from a data source. This class is intended to be
14518 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14521 Roo.data.DataReader = function(meta, recordType){
14525 this.recordType = recordType instanceof Array ?
14526 Roo.data.Record.create(recordType) : recordType;
14529 Roo.data.DataReader.prototype = {
14532 readerType : 'Data',
14534 * Create an empty record
14535 * @param {Object} data (optional) - overlay some values
14536 * @return {Roo.data.Record} record created.
14538 newRow : function(d) {
14540 this.recordType.prototype.fields.each(function(c) {
14542 case 'int' : da[c.name] = 0; break;
14543 case 'date' : da[c.name] = new Date(); break;
14544 case 'float' : da[c.name] = 0.0; break;
14545 case 'boolean' : da[c.name] = false; break;
14546 default : da[c.name] = ""; break;
14550 return new this.recordType(Roo.apply(da, d));
14556 * Ext JS Library 1.1.1
14557 * Copyright(c) 2006-2007, Ext JS, LLC.
14559 * Originally Released Under LGPL - original licence link has changed is not relivant.
14562 * <script type="text/javascript">
14566 * @class Roo.data.DataProxy
14567 * @extends Roo.data.Observable
14568 * This class is an abstract base class for implementations which provide retrieval of
14569 * unformatted data objects.<br>
14571 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14572 * (of the appropriate type which knows how to parse the data object) to provide a block of
14573 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14575 * Custom implementations must implement the load method as described in
14576 * {@link Roo.data.HttpProxy#load}.
14578 Roo.data.DataProxy = function(){
14581 * @event beforeload
14582 * Fires before a network request is made to retrieve a data object.
14583 * @param {Object} This DataProxy object.
14584 * @param {Object} params The params parameter to the load function.
14589 * Fires before the load method's callback is called.
14590 * @param {Object} This DataProxy object.
14591 * @param {Object} o The data object.
14592 * @param {Object} arg The callback argument object passed to the load function.
14596 * @event loadexception
14597 * Fires if an Exception occurs during data retrieval.
14598 * @param {Object} This DataProxy object.
14599 * @param {Object} o The data object.
14600 * @param {Object} arg The callback argument object passed to the load function.
14601 * @param {Object} e The Exception.
14603 loadexception : true
14605 Roo.data.DataProxy.superclass.constructor.call(this);
14608 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14611 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14615 * Ext JS Library 1.1.1
14616 * Copyright(c) 2006-2007, Ext JS, LLC.
14618 * Originally Released Under LGPL - original licence link has changed is not relivant.
14621 * <script type="text/javascript">
14624 * @class Roo.data.MemoryProxy
14625 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14626 * to the Reader when its load method is called.
14628 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14630 Roo.data.MemoryProxy = function(data){
14634 Roo.data.MemoryProxy.superclass.constructor.call(this);
14638 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14641 * Load data from the requested source (in this case an in-memory
14642 * data object passed to the constructor), read the data object into
14643 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14644 * process that block using the passed callback.
14645 * @param {Object} params This parameter is not used by the MemoryProxy class.
14646 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14647 * object into a block of Roo.data.Records.
14648 * @param {Function} callback The function into which to pass the block of Roo.data.records.
14649 * The function must be passed <ul>
14650 * <li>The Record block object</li>
14651 * <li>The "arg" argument from the load function</li>
14652 * <li>A boolean success indicator</li>
14654 * @param {Object} scope The scope in which to call the callback
14655 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14657 load : function(params, reader, callback, scope, arg){
14658 params = params || {};
14661 result = reader.readRecords(params.data ? params.data :this.data);
14663 this.fireEvent("loadexception", this, arg, null, e);
14664 callback.call(scope, null, arg, false);
14667 callback.call(scope, result, arg, true);
14671 update : function(params, records){
14676 * Ext JS Library 1.1.1
14677 * Copyright(c) 2006-2007, Ext JS, LLC.
14679 * Originally Released Under LGPL - original licence link has changed is not relivant.
14682 * <script type="text/javascript">
14685 * @class Roo.data.HttpProxy
14686 * @extends Roo.data.DataProxy
14687 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14688 * configured to reference a certain URL.<br><br>
14690 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14691 * from which the running page was served.<br><br>
14693 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14695 * Be aware that to enable the browser to parse an XML document, the server must set
14696 * the Content-Type header in the HTTP response to "text/xml".
14698 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14699 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
14700 * will be used to make the request.
14702 Roo.data.HttpProxy = function(conn){
14703 Roo.data.HttpProxy.superclass.constructor.call(this);
14704 // is conn a conn config or a real conn?
14706 this.useAjax = !conn || !conn.events;
14710 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14711 // thse are take from connection...
14714 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14717 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14718 * extra parameters to each request made by this object. (defaults to undefined)
14721 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14722 * to each request made by this object. (defaults to undefined)
14725 * @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)
14728 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14731 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14737 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14741 * Return the {@link Roo.data.Connection} object being used by this Proxy.
14742 * @return {Connection} The Connection object. This object may be used to subscribe to events on
14743 * a finer-grained basis than the DataProxy events.
14745 getConnection : function(){
14746 return this.useAjax ? Roo.Ajax : this.conn;
14750 * Load data from the configured {@link Roo.data.Connection}, read the data object into
14751 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14752 * process that block using the passed callback.
14753 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14754 * for the request to the remote server.
14755 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14756 * object into a block of Roo.data.Records.
14757 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14758 * The function must be passed <ul>
14759 * <li>The Record block object</li>
14760 * <li>The "arg" argument from the load function</li>
14761 * <li>A boolean success indicator</li>
14763 * @param {Object} scope The scope in which to call the callback
14764 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14766 load : function(params, reader, callback, scope, arg){
14767 if(this.fireEvent("beforeload", this, params) !== false){
14769 params : params || {},
14771 callback : callback,
14776 callback : this.loadResponse,
14780 Roo.applyIf(o, this.conn);
14781 if(this.activeRequest){
14782 Roo.Ajax.abort(this.activeRequest);
14784 this.activeRequest = Roo.Ajax.request(o);
14786 this.conn.request(o);
14789 callback.call(scope||this, null, arg, false);
14794 loadResponse : function(o, success, response){
14795 delete this.activeRequest;
14797 this.fireEvent("loadexception", this, o, response);
14798 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14803 result = o.reader.read(response);
14805 this.fireEvent("loadexception", this, o, response, e);
14806 o.request.callback.call(o.request.scope, null, o.request.arg, false);
14810 this.fireEvent("load", this, o, o.request.arg);
14811 o.request.callback.call(o.request.scope, result, o.request.arg, true);
14815 update : function(dataSet){
14820 updateResponse : function(dataSet){
14825 * Ext JS Library 1.1.1
14826 * Copyright(c) 2006-2007, Ext JS, LLC.
14828 * Originally Released Under LGPL - original licence link has changed is not relivant.
14831 * <script type="text/javascript">
14835 * @class Roo.data.ScriptTagProxy
14836 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14837 * other than the originating domain of the running page.<br><br>
14839 * <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
14840 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14842 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14843 * source code that is used as the source inside a <script> tag.<br><br>
14845 * In order for the browser to process the returned data, the server must wrap the data object
14846 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14847 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14848 * depending on whether the callback name was passed:
14851 boolean scriptTag = false;
14852 String cb = request.getParameter("callback");
14855 response.setContentType("text/javascript");
14857 response.setContentType("application/x-json");
14859 Writer out = response.getWriter();
14861 out.write(cb + "(");
14863 out.print(dataBlock.toJsonString());
14870 * @param {Object} config A configuration object.
14872 Roo.data.ScriptTagProxy = function(config){
14873 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14874 Roo.apply(this, config);
14875 this.head = document.getElementsByTagName("head")[0];
14878 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14880 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14882 * @cfg {String} url The URL from which to request the data object.
14885 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14889 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14890 * the server the name of the callback function set up by the load call to process the returned data object.
14891 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14892 * javascript output which calls this named function passing the data object as its only parameter.
14894 callbackParam : "callback",
14896 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14897 * name to the request.
14902 * Load data from the configured URL, read the data object into
14903 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14904 * process that block using the passed callback.
14905 * @param {Object} params An object containing properties which are to be used as HTTP parameters
14906 * for the request to the remote server.
14907 * @param {Roo.data.DataReader} reader The Reader object which converts the data
14908 * object into a block of Roo.data.Records.
14909 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14910 * The function must be passed <ul>
14911 * <li>The Record block object</li>
14912 * <li>The "arg" argument from the load function</li>
14913 * <li>A boolean success indicator</li>
14915 * @param {Object} scope The scope in which to call the callback
14916 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14918 load : function(params, reader, callback, scope, arg){
14919 if(this.fireEvent("beforeload", this, params) !== false){
14921 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14923 var url = this.url;
14924 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14926 url += "&_dc=" + (new Date().getTime());
14928 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14931 cb : "stcCallback"+transId,
14932 scriptId : "stcScript"+transId,
14936 callback : callback,
14942 window[trans.cb] = function(o){
14943 conn.handleResponse(o, trans);
14946 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14948 if(this.autoAbort !== false){
14952 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14954 var script = document.createElement("script");
14955 script.setAttribute("src", url);
14956 script.setAttribute("type", "text/javascript");
14957 script.setAttribute("id", trans.scriptId);
14958 this.head.appendChild(script);
14960 this.trans = trans;
14962 callback.call(scope||this, null, arg, false);
14967 isLoading : function(){
14968 return this.trans ? true : false;
14972 * Abort the current server request.
14974 abort : function(){
14975 if(this.isLoading()){
14976 this.destroyTrans(this.trans);
14981 destroyTrans : function(trans, isLoaded){
14982 this.head.removeChild(document.getElementById(trans.scriptId));
14983 clearTimeout(trans.timeoutId);
14985 window[trans.cb] = undefined;
14987 delete window[trans.cb];
14990 // if hasn't been loaded, wait for load to remove it to prevent script error
14991 window[trans.cb] = function(){
14992 window[trans.cb] = undefined;
14994 delete window[trans.cb];
15001 handleResponse : function(o, trans){
15002 this.trans = false;
15003 this.destroyTrans(trans, true);
15006 result = trans.reader.readRecords(o);
15008 this.fireEvent("loadexception", this, o, trans.arg, e);
15009 trans.callback.call(trans.scope||window, null, trans.arg, false);
15012 this.fireEvent("load", this, o, trans.arg);
15013 trans.callback.call(trans.scope||window, result, trans.arg, true);
15017 handleFailure : function(trans){
15018 this.trans = false;
15019 this.destroyTrans(trans, false);
15020 this.fireEvent("loadexception", this, null, trans.arg);
15021 trans.callback.call(trans.scope||window, null, trans.arg, false);
15025 * Ext JS Library 1.1.1
15026 * Copyright(c) 2006-2007, Ext JS, LLC.
15028 * Originally Released Under LGPL - original licence link has changed is not relivant.
15031 * <script type="text/javascript">
15035 * @class Roo.data.JsonReader
15036 * @extends Roo.data.DataReader
15037 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15038 * based on mappings in a provided Roo.data.Record constructor.
15040 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15041 * in the reply previously.
15046 var RecordDef = Roo.data.Record.create([
15047 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
15048 {name: 'occupation'} // This field will use "occupation" as the mapping.
15050 var myReader = new Roo.data.JsonReader({
15051 totalProperty: "results", // The property which contains the total dataset size (optional)
15052 root: "rows", // The property which contains an Array of row objects
15053 id: "id" // The property within each row object that provides an ID for the record (optional)
15057 * This would consume a JSON file like this:
15059 { 'results': 2, 'rows': [
15060 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15061 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15064 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15065 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15066 * paged from the remote server.
15067 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15068 * @cfg {String} root name of the property which contains the Array of row objects.
15069 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15070 * @cfg {Array} fields Array of field definition objects
15072 * Create a new JsonReader
15073 * @param {Object} meta Metadata configuration options
15074 * @param {Object} recordType Either an Array of field definition objects,
15075 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15077 Roo.data.JsonReader = function(meta, recordType){
15080 // set some defaults:
15081 Roo.applyIf(meta, {
15082 totalProperty: 'total',
15083 successProperty : 'success',
15088 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15090 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15092 readerType : 'Json',
15095 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
15096 * Used by Store query builder to append _requestMeta to params.
15099 metaFromRemote : false,
15101 * This method is only used by a DataProxy which has retrieved data from a remote server.
15102 * @param {Object} response The XHR object which contains the JSON data in its responseText.
15103 * @return {Object} data A data block which is used by an Roo.data.Store object as
15104 * a cache of Roo.data.Records.
15106 read : function(response){
15107 var json = response.responseText;
15109 var o = /* eval:var:o */ eval("("+json+")");
15111 throw {message: "JsonReader.read: Json object not found"};
15117 this.metaFromRemote = true;
15118 this.meta = o.metaData;
15119 this.recordType = Roo.data.Record.create(o.metaData.fields);
15120 this.onMetaChange(this.meta, this.recordType, o);
15122 return this.readRecords(o);
15125 // private function a store will implement
15126 onMetaChange : function(meta, recordType, o){
15133 simpleAccess: function(obj, subsc) {
15140 getJsonAccessor: function(){
15142 return function(expr) {
15144 return(re.test(expr))
15145 ? new Function("obj", "return obj." + expr)
15150 return Roo.emptyFn;
15155 * Create a data block containing Roo.data.Records from an XML document.
15156 * @param {Object} o An object which contains an Array of row objects in the property specified
15157 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15158 * which contains the total size of the dataset.
15159 * @return {Object} data A data block which is used by an Roo.data.Store object as
15160 * a cache of Roo.data.Records.
15162 readRecords : function(o){
15164 * After any data loads, the raw JSON data is available for further custom processing.
15168 var s = this.meta, Record = this.recordType,
15169 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15171 // Generate extraction functions for the totalProperty, the root, the id, and for each field
15173 if(s.totalProperty) {
15174 this.getTotal = this.getJsonAccessor(s.totalProperty);
15176 if(s.successProperty) {
15177 this.getSuccess = this.getJsonAccessor(s.successProperty);
15179 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15181 var g = this.getJsonAccessor(s.id);
15182 this.getId = function(rec) {
15184 return (r === undefined || r === "") ? null : r;
15187 this.getId = function(){return null;};
15190 for(var jj = 0; jj < fl; jj++){
15192 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15193 this.ef[jj] = this.getJsonAccessor(map);
15197 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15198 if(s.totalProperty){
15199 var vt = parseInt(this.getTotal(o), 10);
15204 if(s.successProperty){
15205 var vs = this.getSuccess(o);
15206 if(vs === false || vs === 'false'){
15211 for(var i = 0; i < c; i++){
15214 var id = this.getId(n);
15215 for(var j = 0; j < fl; j++){
15217 var v = this.ef[j](n);
15219 Roo.log('missing convert for ' + f.name);
15223 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15225 var record = new Record(values, id);
15227 records[i] = record;
15233 totalRecords : totalRecords
15236 // used when loading children.. @see loadDataFromChildren
15237 toLoadData: function(rec)
15239 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15240 var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15241 return { data : data, total : data.length };
15246 * Ext JS Library 1.1.1
15247 * Copyright(c) 2006-2007, Ext JS, LLC.
15249 * Originally Released Under LGPL - original licence link has changed is not relivant.
15252 * <script type="text/javascript">
15256 * @class Roo.data.ArrayReader
15257 * @extends Roo.data.DataReader
15258 * Data reader class to create an Array of Roo.data.Record objects from an Array.
15259 * Each element of that Array represents a row of data fields. The
15260 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15261 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15265 var RecordDef = Roo.data.Record.create([
15266 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
15267 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
15269 var myReader = new Roo.data.ArrayReader({
15270 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
15274 * This would consume an Array like this:
15276 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15280 * Create a new JsonReader
15281 * @param {Object} meta Metadata configuration options.
15282 * @param {Object|Array} recordType Either an Array of field definition objects
15284 * @cfg {Array} fields Array of field definition objects
15285 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15286 * as specified to {@link Roo.data.Record#create},
15287 * or an {@link Roo.data.Record} object
15290 * created using {@link Roo.data.Record#create}.
15292 Roo.data.ArrayReader = function(meta, recordType)
15294 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15297 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15300 * Create a data block containing Roo.data.Records from an XML document.
15301 * @param {Object} o An Array of row objects which represents the dataset.
15302 * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15303 * a cache of Roo.data.Records.
15305 readRecords : function(o)
15307 var sid = this.meta ? this.meta.id : null;
15308 var recordType = this.recordType, fields = recordType.prototype.fields;
15311 for(var i = 0; i < root.length; i++){
15314 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15315 for(var j = 0, jlen = fields.length; j < jlen; j++){
15316 var f = fields.items[j];
15317 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15318 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15320 values[f.name] = v;
15322 var record = new recordType(values, id);
15324 records[records.length] = record;
15328 totalRecords : records.length
15331 // used when loading children.. @see loadDataFromChildren
15332 toLoadData: function(rec)
15334 // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15335 return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15346 * @class Roo.bootstrap.ComboBox
15347 * @extends Roo.bootstrap.TriggerField
15348 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15349 * @cfg {Boolean} append (true|false) default false
15350 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15351 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15352 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15353 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15354 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15355 * @cfg {Boolean} animate default true
15356 * @cfg {Boolean} emptyResultText only for touch device
15357 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15358 * @cfg {String} emptyTitle default ''
15359 * @cfg {Number} width fixed with? experimental
15361 * Create a new ComboBox.
15362 * @param {Object} config Configuration options
15364 Roo.bootstrap.ComboBox = function(config){
15365 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15369 * Fires when the dropdown list is expanded
15370 * @param {Roo.bootstrap.ComboBox} combo This combo box
15375 * Fires when the dropdown list is collapsed
15376 * @param {Roo.bootstrap.ComboBox} combo This combo box
15380 * @event beforeselect
15381 * Fires before a list item is selected. Return false to cancel the selection.
15382 * @param {Roo.bootstrap.ComboBox} combo This combo box
15383 * @param {Roo.data.Record} record The data record returned from the underlying store
15384 * @param {Number} index The index of the selected item in the dropdown list
15386 'beforeselect' : true,
15389 * Fires when a list item is selected
15390 * @param {Roo.bootstrap.ComboBox} combo This combo box
15391 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15392 * @param {Number} index The index of the selected item in the dropdown list
15396 * @event beforequery
15397 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15398 * The event object passed has these properties:
15399 * @param {Roo.bootstrap.ComboBox} combo This combo box
15400 * @param {String} query The query
15401 * @param {Boolean} forceAll true to force "all" query
15402 * @param {Boolean} cancel true to cancel the query
15403 * @param {Object} e The query event object
15405 'beforequery': true,
15408 * Fires when the 'add' icon is pressed (add a listener to enable add button)
15409 * @param {Roo.bootstrap.ComboBox} combo This combo box
15414 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15415 * @param {Roo.bootstrap.ComboBox} combo This combo box
15416 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15421 * Fires when the remove value from the combobox array
15422 * @param {Roo.bootstrap.ComboBox} combo This combo box
15426 * @event afterremove
15427 * Fires when the remove value from the combobox array
15428 * @param {Roo.bootstrap.ComboBox} combo This combo box
15430 'afterremove' : true,
15432 * @event specialfilter
15433 * Fires when specialfilter
15434 * @param {Roo.bootstrap.ComboBox} combo This combo box
15436 'specialfilter' : true,
15439 * Fires when tick the element
15440 * @param {Roo.bootstrap.ComboBox} combo This combo box
15444 * @event touchviewdisplay
15445 * Fires when touch view require special display (default is using displayField)
15446 * @param {Roo.bootstrap.ComboBox} combo This combo box
15447 * @param {Object} cfg set html .
15449 'touchviewdisplay' : true
15454 this.tickItems = [];
15456 this.selectedIndex = -1;
15457 if(this.mode == 'local'){
15458 if(config.queryDelay === undefined){
15459 this.queryDelay = 10;
15461 if(config.minChars === undefined){
15467 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15470 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15471 * rendering into an Roo.Editor, defaults to false)
15474 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15475 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15478 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15481 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15482 * the dropdown list (defaults to undefined, with no header element)
15486 * @cfg {String/Roo.Template} tpl The template to use to render the output default is '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>'
15490 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15492 listWidth: undefined,
15494 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15495 * mode = 'remote' or 'text' if mode = 'local')
15497 displayField: undefined,
15500 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15501 * mode = 'remote' or 'value' if mode = 'local').
15502 * Note: use of a valueField requires the user make a selection
15503 * in order for a value to be mapped.
15505 valueField: undefined,
15507 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15512 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15513 * field's data value (defaults to the underlying DOM element's name)
15515 hiddenName: undefined,
15517 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15521 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15523 selectedClass: 'active',
15526 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15530 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15531 * anchor positions (defaults to 'tl-bl')
15533 listAlign: 'tl-bl?',
15535 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15539 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
15540 * query specified by the allQuery config option (defaults to 'query')
15542 triggerAction: 'query',
15544 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15545 * (defaults to 4, does not apply if editable = false)
15549 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15550 * delay (typeAheadDelay) if it matches a known value (defaults to false)
15554 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15555 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15559 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15560 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
15564 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
15565 * when editable = true (defaults to false)
15567 selectOnFocus:false,
15569 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15571 queryParam: 'query',
15573 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
15574 * when mode = 'remote' (defaults to 'Loading...')
15576 loadingText: 'Loading...',
15578 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15582 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15586 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15587 * traditional select (defaults to true)
15591 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15595 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15599 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15600 * listWidth has a higher value)
15604 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15605 * allow the user to set arbitrary text into the field (defaults to false)
15607 forceSelection:false,
15609 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15610 * if typeAhead = true (defaults to 250)
15612 typeAheadDelay : 250,
15614 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15615 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15617 valueNotFoundText : undefined,
15619 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15621 blockFocus : false,
15624 * @cfg {Boolean} disableClear Disable showing of clear button.
15626 disableClear : false,
15628 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
15630 alwaysQuery : false,
15633 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
15638 * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15640 invalidClass : "has-warning",
15643 * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15645 validClass : "has-success",
15648 * @cfg {Boolean} specialFilter (true|false) special filter default false
15650 specialFilter : false,
15653 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15655 mobileTouchView : true,
15658 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15660 useNativeIOS : false,
15663 * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15665 mobile_restrict_height : false,
15667 ios_options : false,
15679 btnPosition : 'right',
15680 triggerList : true,
15681 showToggleBtn : true,
15683 emptyResultText: 'Empty',
15684 triggerText : 'Select',
15688 // element that contains real text value.. (when hidden is used..)
15690 getAutoCreate : function()
15695 * Render classic select for iso
15698 if(Roo.isIOS && this.useNativeIOS){
15699 cfg = this.getAutoCreateNativeIOS();
15707 if(Roo.isTouch && this.mobileTouchView){
15708 cfg = this.getAutoCreateTouchView();
15715 if(!this.tickable){
15716 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15721 * ComboBox with tickable selections
15724 var align = this.labelAlign || this.parentLabelAlign();
15727 cls : 'form-group roo-combobox-tickable' //input-group
15730 var btn_text_select = '';
15731 var btn_text_done = '';
15732 var btn_text_cancel = '';
15734 if (this.btn_text_show) {
15735 btn_text_select = 'Select';
15736 btn_text_done = 'Done';
15737 btn_text_cancel = 'Cancel';
15742 cls : 'tickable-buttons',
15747 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15748 //html : this.triggerText
15749 html: btn_text_select
15755 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15757 html: btn_text_done
15763 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15765 html: btn_text_cancel
15771 buttons.cn.unshift({
15773 cls: 'roo-select2-search-field-input'
15779 Roo.each(buttons.cn, function(c){
15781 c.cls += ' btn-' + _this.size;
15784 if (_this.disabled) {
15791 style : 'display: contents',
15796 cls: 'form-hidden-field'
15800 cls: 'roo-select2-choices',
15804 cls: 'roo-select2-search-field',
15815 cls: 'roo-select2-container input-group roo-select2-container-multi',
15821 // cls: 'typeahead typeahead-long dropdown-menu',
15822 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
15827 if(this.hasFeedback && !this.allowBlank){
15831 cls: 'glyphicon form-control-feedback'
15834 combobox.cn.push(feedback);
15841 cls : 'roo-required-indicator ' + (this.indicatorpos == 'right' ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15842 tooltip : 'This field is required'
15844 if (Roo.bootstrap.version == 4) {
15847 style : 'display:none'
15850 if (align ==='left' && this.fieldLabel.length) {
15852 cfg.cls += ' roo-form-group-label-left' + (Roo.bootstrap.version == 4 ? ' row' : '');
15859 cls : 'control-label col-form-label',
15860 html : this.fieldLabel
15872 var labelCfg = cfg.cn[1];
15873 var contentCfg = cfg.cn[2];
15876 if(this.indicatorpos == 'right'){
15882 cls : 'control-label col-form-label',
15886 html : this.fieldLabel
15902 labelCfg = cfg.cn[0];
15903 contentCfg = cfg.cn[1];
15907 if(this.labelWidth > 12){
15908 labelCfg.style = "width: " + this.labelWidth + 'px';
15910 if(this.width * 1 > 0){
15911 contentCfg.style = "width: " + this.width + 'px';
15913 if(this.labelWidth < 13 && this.labelmd == 0){
15914 this.labelmd = this.labelWidth;
15917 if(this.labellg > 0){
15918 labelCfg.cls += ' col-lg-' + this.labellg;
15919 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15922 if(this.labelmd > 0){
15923 labelCfg.cls += ' col-md-' + this.labelmd;
15924 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15927 if(this.labelsm > 0){
15928 labelCfg.cls += ' col-sm-' + this.labelsm;
15929 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15932 if(this.labelxs > 0){
15933 labelCfg.cls += ' col-xs-' + this.labelxs;
15934 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15938 } else if ( this.fieldLabel.length) {
15939 // Roo.log(" label");
15944 //cls : 'input-group-addon',
15945 html : this.fieldLabel
15950 if(this.indicatorpos == 'right'){
15954 //cls : 'input-group-addon',
15955 html : this.fieldLabel
15965 // Roo.log(" no label && no align");
15972 ['xs','sm','md','lg'].map(function(size){
15973 if (settings[size]) {
15974 cfg.cls += ' col-' + size + '-' + settings[size];
15982 _initEventsCalled : false,
15985 initEvents: function()
15987 if (this._initEventsCalled) { // as we call render... prevent looping...
15990 this._initEventsCalled = true;
15993 throw "can not find store for combo";
15996 this.indicator = this.indicatorEl();
15998 this.store = Roo.factory(this.store, Roo.data);
15999 this.store.parent = this;
16001 // if we are building from html. then this element is so complex, that we can not really
16002 // use the rendered HTML.
16003 // so we have to trash and replace the previous code.
16004 if (Roo.XComponent.build_from_html) {
16005 // remove this element....
16006 var e = this.el.dom, k=0;
16007 while (e ) { e = e.previousSibling; ++k;}
16012 this.rendered = false;
16014 this.render(this.parent().getChildContainer(true), k);
16017 if(Roo.isIOS && this.useNativeIOS){
16018 this.initIOSView();
16026 if(Roo.isTouch && this.mobileTouchView){
16027 this.initTouchView();
16032 this.initTickableEvents();
16036 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16038 if(this.hiddenName){
16040 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16042 this.hiddenField.dom.value =
16043 this.hiddenValue !== undefined ? this.hiddenValue :
16044 this.value !== undefined ? this.value : '';
16046 // prevent input submission
16047 this.el.dom.removeAttribute('name');
16048 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16053 // this.el.dom.setAttribute('autocomplete', 'off');
16056 var cls = 'x-combo-list';
16058 //this.list = new Roo.Layer({
16059 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16065 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16066 _this.list.setWidth(lw);
16069 this.list.on('mouseover', this.onViewOver, this);
16070 this.list.on('mousemove', this.onViewMove, this);
16071 this.list.on('scroll', this.onViewScroll, this);
16074 this.list.swallowEvent('mousewheel');
16075 this.assetHeight = 0;
16078 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16079 this.assetHeight += this.header.getHeight();
16082 this.innerList = this.list.createChild({cls:cls+'-inner'});
16083 this.innerList.on('mouseover', this.onViewOver, this);
16084 this.innerList.on('mousemove', this.onViewMove, this);
16085 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16087 if(this.allowBlank && !this.pageSize && !this.disableClear){
16088 this.footer = this.list.createChild({cls:cls+'-ft'});
16089 this.pageTb = new Roo.Toolbar(this.footer);
16093 this.footer = this.list.createChild({cls:cls+'-ft'});
16094 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16095 {pageSize: this.pageSize});
16099 if (this.pageTb && this.allowBlank && !this.disableClear) {
16101 this.pageTb.add(new Roo.Toolbar.Fill(), {
16102 cls: 'x-btn-icon x-btn-clear',
16104 handler: function()
16107 _this.clearValue();
16108 _this.onSelect(false, -1);
16113 this.assetHeight += this.footer.getHeight();
16118 this.tpl = Roo.bootstrap.version == 4 ?
16119 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' : // 4 does not need <li> and it get's really confisued.
16120 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16123 this.view = new Roo.View(this.list, this.tpl, {
16124 singleSelect:true, store: this.store, selectedClass: this.selectedClass
16126 //this.view.wrapEl.setDisplayed(false);
16127 this.view.on('click', this.onViewClick, this);
16130 this.store.on('beforeload', this.onBeforeLoad, this);
16131 this.store.on('load', this.onLoad, this);
16132 this.store.on('loadexception', this.onLoadException, this);
16134 if(this.resizable){
16135 this.resizer = new Roo.Resizable(this.list, {
16136 pinned:true, handles:'se'
16138 this.resizer.on('resize', function(r, w, h){
16139 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16140 this.listWidth = w;
16141 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16142 this.restrictHeight();
16144 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16147 if(!this.editable){
16148 this.editable = true;
16149 this.setEditable(false);
16154 if (typeof(this.events.add.listeners) != 'undefined') {
16156 this.addicon = this.wrap.createChild(
16157 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
16159 this.addicon.on('click', function(e) {
16160 this.fireEvent('add', this);
16163 if (typeof(this.events.edit.listeners) != 'undefined') {
16165 this.editicon = this.wrap.createChild(
16166 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
16167 if (this.addicon) {
16168 this.editicon.setStyle('margin-left', '40px');
16170 this.editicon.on('click', function(e) {
16172 // we fire even if inothing is selected..
16173 this.fireEvent('edit', this, this.lastData );
16179 this.keyNav = new Roo.KeyNav(this.inputEl(), {
16180 "up" : function(e){
16181 this.inKeyMode = true;
16185 "down" : function(e){
16186 if(!this.isExpanded()){
16187 this.onTriggerClick();
16189 this.inKeyMode = true;
16194 "enter" : function(e){
16195 // this.onViewClick();
16199 if(this.fireEvent("specialkey", this, e)){
16200 this.onViewClick(false);
16206 "esc" : function(e){
16210 "tab" : function(e){
16213 if(this.fireEvent("specialkey", this, e)){
16214 this.onViewClick(false);
16222 doRelay : function(foo, bar, hname){
16223 if(hname == 'down' || this.scope.isExpanded()){
16224 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16233 this.queryDelay = Math.max(this.queryDelay || 10,
16234 this.mode == 'local' ? 10 : 250);
16237 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16239 if(this.typeAhead){
16240 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16242 if(this.editable !== false){
16243 this.inputEl().on("keyup", this.onKeyUp, this);
16245 if(this.forceSelection){
16246 this.inputEl().on('blur', this.doForce, this);
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();
16255 initTickableEvents: function()
16259 if(this.hiddenName){
16261 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16263 this.hiddenField.dom.value =
16264 this.hiddenValue !== undefined ? this.hiddenValue :
16265 this.value !== undefined ? this.value : '';
16267 // prevent input submission
16268 this.el.dom.removeAttribute('name');
16269 this.hiddenField.dom.setAttribute('name', this.hiddenName);
16274 // this.list = this.el.select('ul.dropdown-menu',true).first();
16276 this.choices = this.el.select('ul.roo-select2-choices', true).first();
16277 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16278 if(this.triggerList){
16279 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16282 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16283 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16285 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16286 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16288 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16289 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16291 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16292 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16293 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16296 this.cancelBtn.hide();
16301 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16302 _this.list.setWidth(lw);
16305 this.list.on('mouseover', this.onViewOver, this);
16306 this.list.on('mousemove', this.onViewMove, this);
16308 this.list.on('scroll', this.onViewScroll, this);
16311 this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' +
16312 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16315 this.view = new Roo.View(this.list, this.tpl, {
16320 selectedClass: this.selectedClass
16323 //this.view.wrapEl.setDisplayed(false);
16324 this.view.on('click', this.onViewClick, this);
16328 this.store.on('beforeload', this.onBeforeLoad, this);
16329 this.store.on('load', this.onLoad, this);
16330 this.store.on('loadexception', this.onLoadException, this);
16333 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16334 "up" : function(e){
16335 this.inKeyMode = true;
16339 "down" : function(e){
16340 this.inKeyMode = true;
16344 "enter" : function(e){
16345 if(this.fireEvent("specialkey", this, e)){
16346 this.onViewClick(false);
16352 "esc" : function(e){
16353 this.onTickableFooterButtonClick(e, false, false);
16356 "tab" : function(e){
16357 this.fireEvent("specialkey", this, e);
16359 this.onTickableFooterButtonClick(e, false, false);
16366 doRelay : function(e, fn, key){
16367 if(this.scope.isExpanded()){
16368 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16377 this.queryDelay = Math.max(this.queryDelay || 10,
16378 this.mode == 'local' ? 10 : 250);
16381 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16383 if(this.typeAhead){
16384 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16387 if(this.editable !== false){
16388 this.tickableInputEl().on("keyup", this.onKeyUp, this);
16391 this.indicator = this.indicatorEl();
16393 if(this.indicator){
16394 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16395 this.indicator.hide();
16400 onDestroy : function(){
16402 this.view.setStore(null);
16403 this.view.el.removeAllListeners();
16404 this.view.el.remove();
16405 this.view.purgeListeners();
16408 this.list.dom.innerHTML = '';
16412 this.store.un('beforeload', this.onBeforeLoad, this);
16413 this.store.un('load', this.onLoad, this);
16414 this.store.un('loadexception', this.onLoadException, this);
16416 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16420 fireKey : function(e){
16421 if(e.isNavKeyPress() && !this.list.isVisible()){
16422 this.fireEvent("specialkey", this, e);
16427 onResize: function(w, h)
16431 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16433 // if(typeof w != 'number'){
16434 // // we do not handle it!?!?
16437 // var tw = this.trigger.getWidth();
16438 // // tw += this.addicon ? this.addicon.getWidth() : 0;
16439 // // tw += this.editicon ? this.editicon.getWidth() : 0;
16441 // this.inputEl().setWidth( this.adjustWidth('input', x));
16443 // //this.trigger.setStyle('left', x+'px');
16445 // if(this.list && this.listWidth === undefined){
16446 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16447 // this.list.setWidth(lw);
16448 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16456 * Allow or prevent the user from directly editing the field text. If false is passed,
16457 * the user will only be able to select from the items defined in the dropdown list. This method
16458 * is the runtime equivalent of setting the 'editable' config option at config time.
16459 * @param {Boolean} value True to allow the user to directly edit the field text
16461 setEditable : function(value){
16462 if(value == this.editable){
16465 this.editable = value;
16467 this.inputEl().dom.setAttribute('readOnly', true);
16468 this.inputEl().on('mousedown', this.onTriggerClick, this);
16469 this.inputEl().addClass('x-combo-noedit');
16471 this.inputEl().dom.removeAttribute('readOnly');
16472 this.inputEl().un('mousedown', this.onTriggerClick, this);
16473 this.inputEl().removeClass('x-combo-noedit');
16479 onBeforeLoad : function(combo,opts){
16480 if(!this.hasFocus){
16484 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16486 this.restrictHeight();
16487 this.selectedIndex = -1;
16491 onLoad : function(){
16493 this.hasQuery = false;
16495 if(!this.hasFocus){
16499 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16500 this.loading.hide();
16503 if(this.store.getCount() > 0){
16506 this.restrictHeight();
16507 if(this.lastQuery == this.allQuery){
16508 if(this.editable && !this.tickable){
16509 this.inputEl().dom.select();
16513 !this.selectByValue(this.value, true) &&
16516 !this.store.lastOptions ||
16517 typeof(this.store.lastOptions.add) == 'undefined' ||
16518 this.store.lastOptions.add != true
16521 this.select(0, true);
16524 if(this.autoFocus){
16527 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16528 this.taTask.delay(this.typeAheadDelay);
16532 this.onEmptyResults();
16538 onLoadException : function()
16540 this.hasQuery = false;
16542 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16543 this.loading.hide();
16546 if(this.tickable && this.editable){
16551 // only causes errors at present
16552 //Roo.log(this.store.reader.jsonData);
16553 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16555 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16561 onTypeAhead : function(){
16562 if(this.store.getCount() > 0){
16563 var r = this.store.getAt(0);
16564 var newValue = r.data[this.displayField];
16565 var len = newValue.length;
16566 var selStart = this.getRawValue().length;
16568 if(selStart != len){
16569 this.setRawValue(newValue);
16570 this.selectText(selStart, newValue.length);
16576 onSelect : function(record, index){
16578 if(this.fireEvent('beforeselect', this, record, index) !== false){
16580 this.setFromData(index > -1 ? record.data : false);
16583 this.fireEvent('select', this, record, index);
16588 * Returns the currently selected field value or empty string if no value is set.
16589 * @return {String} value The selected value
16591 getValue : function()
16593 if(Roo.isIOS && this.useNativeIOS){
16594 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16598 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16601 if(this.valueField){
16602 return typeof this.value != 'undefined' ? this.value : '';
16604 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16608 getRawValue : function()
16610 if(Roo.isIOS && this.useNativeIOS){
16611 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16614 var v = this.inputEl().getValue();
16620 * Clears any text/value currently set in the field
16622 clearValue : function(){
16624 if(this.hiddenField){
16625 this.hiddenField.dom.value = '';
16628 this.setRawValue('');
16629 this.lastSelectionText = '';
16630 this.lastData = false;
16632 var close = this.closeTriggerEl();
16643 * Sets the specified value into the field. If the value finds a match, the corresponding record text
16644 * will be displayed in the field. If the value does not match the data value of an existing item,
16645 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16646 * Otherwise the field will be blank (although the value will still be set).
16647 * @param {String} value The value to match
16649 setValue : function(v)
16651 if(Roo.isIOS && this.useNativeIOS){
16652 this.setIOSValue(v);
16662 if(this.valueField){
16663 var r = this.findRecord(this.valueField, v);
16665 text = r.data[this.displayField];
16666 }else if(this.valueNotFoundText !== undefined){
16667 text = this.valueNotFoundText;
16670 this.lastSelectionText = text;
16671 if(this.hiddenField){
16672 this.hiddenField.dom.value = v;
16674 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16677 var close = this.closeTriggerEl();
16680 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16686 * @property {Object} the last set data for the element
16691 * Sets the value of the field based on a object which is related to the record format for the store.
16692 * @param {Object} value the value to set as. or false on reset?
16694 setFromData : function(o){
16701 var dv = ''; // display value
16702 var vv = ''; // value value..
16704 if (this.displayField) {
16705 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16707 // this is an error condition!!!
16708 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
16711 if(this.valueField){
16712 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16715 var close = this.closeTriggerEl();
16718 if(dv.length || vv * 1 > 0){
16720 this.blockFocus=true;
16726 if(this.hiddenField){
16727 this.hiddenField.dom.value = vv;
16729 this.lastSelectionText = dv;
16730 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16734 // no hidden field.. - we store the value in 'value', but still display
16735 // display field!!!!
16736 this.lastSelectionText = dv;
16737 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16744 reset : function(){
16745 // overridden so that last data is reset..
16752 this.setValue(this.originalValue);
16753 //this.clearInvalid();
16754 this.lastData = false;
16756 this.view.clearSelections();
16762 findRecord : function(prop, value){
16764 if(this.store.getCount() > 0){
16765 this.store.each(function(r){
16766 if(r.data[prop] == value){
16776 getName: function()
16778 // returns hidden if it's set..
16779 if (!this.rendered) {return ''};
16780 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
16784 onViewMove : function(e, t){
16785 this.inKeyMode = false;
16789 onViewOver : function(e, t){
16790 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16793 var item = this.view.findItemFromChild(t);
16796 var index = this.view.indexOf(item);
16797 this.select(index, false);
16802 onViewClick : function(view, doFocus, el, e)
16804 var index = this.view.getSelectedIndexes()[0];
16806 var r = this.store.getAt(index);
16810 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16817 Roo.each(this.tickItems, function(v,k){
16819 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16821 _this.tickItems.splice(k, 1);
16823 if(typeof(e) == 'undefined' && view == false){
16824 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16836 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16837 this.tickItems.push(r.data);
16840 if(typeof(e) == 'undefined' && view == false){
16841 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16848 this.onSelect(r, index);
16850 if(doFocus !== false && !this.blockFocus){
16851 this.inputEl().focus();
16856 restrictHeight : function(){
16857 //this.innerList.dom.style.height = '';
16858 //var inner = this.innerList.dom;
16859 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16860 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16861 //this.list.beginUpdate();
16862 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16863 this.list.alignTo(this.inputEl(), this.listAlign);
16864 this.list.alignTo(this.inputEl(), this.listAlign);
16865 //this.list.endUpdate();
16869 onEmptyResults : function(){
16871 if(this.tickable && this.editable){
16872 this.hasFocus = false;
16873 this.restrictHeight();
16881 * Returns true if the dropdown list is expanded, else false.
16883 isExpanded : function(){
16884 return this.list.isVisible();
16888 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16889 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16890 * @param {String} value The data value of the item to select
16891 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16892 * selected item if it is not currently in view (defaults to true)
16893 * @return {Boolean} True if the value matched an item in the list, else false
16895 selectByValue : function(v, scrollIntoView){
16896 if(v !== undefined && v !== null){
16897 var r = this.findRecord(this.valueField || this.displayField, v);
16899 this.select(this.store.indexOf(r), scrollIntoView);
16907 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16908 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16909 * @param {Number} index The zero-based index of the list item to select
16910 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16911 * selected item if it is not currently in view (defaults to true)
16913 select : function(index, scrollIntoView){
16914 this.selectedIndex = index;
16915 this.view.select(index);
16916 if(scrollIntoView !== false){
16917 var el = this.view.getNode(index);
16919 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16922 this.list.scrollChildIntoView(el, false);
16928 selectNext : function(){
16929 var ct = this.store.getCount();
16931 if(this.selectedIndex == -1){
16933 }else if(this.selectedIndex < ct-1){
16934 this.select(this.selectedIndex+1);
16940 selectPrev : function(){
16941 var ct = this.store.getCount();
16943 if(this.selectedIndex == -1){
16945 }else if(this.selectedIndex != 0){
16946 this.select(this.selectedIndex-1);
16952 onKeyUp : function(e){
16953 if(this.editable !== false && !e.isSpecialKey()){
16954 this.lastKey = e.getKey();
16955 this.dqTask.delay(this.queryDelay);
16960 validateBlur : function(){
16961 return !this.list || !this.list.isVisible();
16965 initQuery : function(){
16967 var v = this.getRawValue();
16969 if(this.tickable && this.editable){
16970 v = this.tickableInputEl().getValue();
16977 doForce : function(){
16978 if(this.inputEl().dom.value.length > 0){
16979 this.inputEl().dom.value =
16980 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16986 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
16987 * query allowing the query action to be canceled if needed.
16988 * @param {String} query The SQL query to execute
16989 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16990 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
16991 * saved in the current store (defaults to false)
16993 doQuery : function(q, forceAll){
16995 if(q === undefined || q === null){
17000 forceAll: forceAll,
17004 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17009 forceAll = qe.forceAll;
17010 if(forceAll === true || (q.length >= this.minChars)){
17012 this.hasQuery = true;
17014 if(this.lastQuery != q || this.alwaysQuery){
17015 this.lastQuery = q;
17016 if(this.mode == 'local'){
17017 this.selectedIndex = -1;
17019 this.store.clearFilter();
17022 if(this.specialFilter){
17023 this.fireEvent('specialfilter', this);
17028 this.store.filter(this.displayField, q);
17031 this.store.fireEvent("datachanged", this.store);
17038 this.store.baseParams[this.queryParam] = q;
17040 var options = {params : this.getParams(q)};
17043 options.add = true;
17044 options.params.start = this.page * this.pageSize;
17047 this.store.load(options);
17050 * this code will make the page width larger, at the beginning, the list not align correctly,
17051 * we should expand the list on onLoad
17052 * so command out it
17057 this.selectedIndex = -1;
17062 this.loadNext = false;
17066 getParams : function(q){
17068 //p[this.queryParam] = q;
17072 p.limit = this.pageSize;
17078 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17080 collapse : function(){
17081 if(!this.isExpanded()){
17087 this.hasFocus = false;
17091 this.cancelBtn.hide();
17092 this.trigger.show();
17095 this.tickableInputEl().dom.value = '';
17096 this.tickableInputEl().blur();
17101 Roo.get(document).un('mousedown', this.collapseIf, this);
17102 Roo.get(document).un('mousewheel', this.collapseIf, this);
17103 if (!this.editable) {
17104 Roo.get(document).un('keydown', this.listKeyPress, this);
17106 this.fireEvent('collapse', this);
17112 collapseIf : function(e){
17113 var in_combo = e.within(this.el);
17114 var in_list = e.within(this.list);
17115 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17117 if (in_combo || in_list || is_list) {
17118 //e.stopPropagation();
17123 this.onTickableFooterButtonClick(e, false, false);
17131 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17133 expand : function(){
17135 if(this.isExpanded() || !this.hasFocus){
17139 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17140 this.list.setWidth(lw);
17146 this.restrictHeight();
17150 this.tickItems = Roo.apply([], this.item);
17153 this.cancelBtn.show();
17154 this.trigger.hide();
17157 this.tickableInputEl().focus();
17162 Roo.get(document).on('mousedown', this.collapseIf, this);
17163 Roo.get(document).on('mousewheel', this.collapseIf, this);
17164 if (!this.editable) {
17165 Roo.get(document).on('keydown', this.listKeyPress, this);
17168 this.fireEvent('expand', this);
17172 // Implements the default empty TriggerField.onTriggerClick function
17173 onTriggerClick : function(e)
17175 Roo.log('trigger click');
17177 if(this.disabled || !this.triggerList){
17182 this.loadNext = false;
17184 if(this.isExpanded()){
17186 if (!this.blockFocus) {
17187 this.inputEl().focus();
17191 this.hasFocus = true;
17192 if(this.triggerAction == 'all') {
17193 this.doQuery(this.allQuery, true);
17195 this.doQuery(this.getRawValue());
17197 if (!this.blockFocus) {
17198 this.inputEl().focus();
17203 onTickableTriggerClick : function(e)
17210 this.loadNext = false;
17211 this.hasFocus = true;
17213 if(this.triggerAction == 'all') {
17214 this.doQuery(this.allQuery, true);
17216 this.doQuery(this.getRawValue());
17220 onSearchFieldClick : function(e)
17222 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17223 this.onTickableFooterButtonClick(e, false, false);
17227 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17232 this.loadNext = false;
17233 this.hasFocus = true;
17235 if(this.triggerAction == 'all') {
17236 this.doQuery(this.allQuery, true);
17238 this.doQuery(this.getRawValue());
17242 listKeyPress : function(e)
17244 //Roo.log('listkeypress');
17245 // scroll to first matching element based on key pres..
17246 if (e.isSpecialKey()) {
17249 var k = String.fromCharCode(e.getKey()).toUpperCase();
17252 var csel = this.view.getSelectedNodes();
17253 var cselitem = false;
17255 var ix = this.view.indexOf(csel[0]);
17256 cselitem = this.store.getAt(ix);
17257 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17263 this.store.each(function(v) {
17265 // start at existing selection.
17266 if (cselitem.id == v.id) {
17272 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17273 match = this.store.indexOf(v);
17279 if (match === false) {
17280 return true; // no more action?
17283 this.view.select(match);
17284 var sn = Roo.get(this.view.getSelectedNodes()[0]);
17285 sn.scrollIntoView(sn.dom.parentNode, false);
17288 onViewScroll : function(e, t){
17290 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){
17294 this.hasQuery = true;
17296 this.loading = this.list.select('.loading', true).first();
17298 if(this.loading === null){
17299 this.list.createChild({
17301 cls: 'loading roo-select2-more-results roo-select2-active',
17302 html: 'Loading more results...'
17305 this.loading = this.list.select('.loading', true).first();
17307 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17309 this.loading.hide();
17312 this.loading.show();
17317 this.loadNext = true;
17319 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17324 addItem : function(o)
17326 var dv = ''; // display value
17328 if (this.displayField) {
17329 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17331 // this is an error condition!!!
17332 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
17339 var choice = this.choices.createChild({
17341 cls: 'roo-select2-search-choice',
17350 cls: 'roo-select2-search-choice-close fa fa-times',
17355 }, this.searchField);
17357 var close = choice.select('a.roo-select2-search-choice-close', true).first();
17359 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17367 this.inputEl().dom.value = '';
17372 onRemoveItem : function(e, _self, o)
17374 e.preventDefault();
17376 this.lastItem = Roo.apply([], this.item);
17378 var index = this.item.indexOf(o.data) * 1;
17381 Roo.log('not this item?!');
17385 this.item.splice(index, 1);
17390 this.fireEvent('remove', this, e);
17396 syncValue : function()
17398 if(!this.item.length){
17405 Roo.each(this.item, function(i){
17406 if(_this.valueField){
17407 value.push(i[_this.valueField]);
17414 this.value = value.join(',');
17416 if(this.hiddenField){
17417 this.hiddenField.dom.value = this.value;
17420 this.store.fireEvent("datachanged", this.store);
17425 clearItem : function()
17427 if(!this.multiple){
17433 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17441 if(this.tickable && !Roo.isTouch){
17442 this.view.refresh();
17446 inputEl: function ()
17448 if(Roo.isIOS && this.useNativeIOS){
17449 return this.el.select('select.roo-ios-select', true).first();
17452 if(Roo.isTouch && this.mobileTouchView){
17453 return this.el.select('input.form-control',true).first();
17457 return this.searchField;
17460 return this.el.select('input.form-control',true).first();
17463 onTickableFooterButtonClick : function(e, btn, el)
17465 e.preventDefault();
17467 this.lastItem = Roo.apply([], this.item);
17469 if(btn && btn.name == 'cancel'){
17470 this.tickItems = Roo.apply([], this.item);
17479 Roo.each(this.tickItems, function(o){
17487 validate : function()
17489 if(this.getVisibilityEl().hasClass('hidden')){
17493 var v = this.getRawValue();
17496 v = this.getValue();
17499 if(this.disabled || this.allowBlank || v.length){
17504 this.markInvalid();
17508 tickableInputEl : function()
17510 if(!this.tickable || !this.editable){
17511 return this.inputEl();
17514 return this.inputEl().select('.roo-select2-search-field-input', true).first();
17518 getAutoCreateTouchView : function()
17523 cls: 'form-group' //input-group
17529 type : this.inputType,
17530 cls : 'form-control x-combo-noedit',
17531 autocomplete: 'new-password',
17532 placeholder : this.placeholder || '',
17537 input.name = this.name;
17541 input.cls += ' input-' + this.size;
17544 if (this.disabled) {
17545 input.disabled = true;
17549 cls : 'roo-combobox-wrap',
17556 inputblock.cls += ' input-group';
17558 inputblock.cn.unshift({
17560 cls : 'input-group-addon input-group-prepend input-group-text',
17565 if(this.removable && !this.multiple){
17566 inputblock.cls += ' roo-removable';
17568 inputblock.cn.push({
17571 cls : 'roo-combo-removable-btn close'
17575 if(this.hasFeedback && !this.allowBlank){
17577 inputblock.cls += ' has-feedback';
17579 inputblock.cn.push({
17581 cls: 'glyphicon form-control-feedback'
17588 inputblock.cls += (this.before) ? '' : ' input-group';
17590 inputblock.cn.push({
17592 cls : 'input-group-addon input-group-append input-group-text',
17598 var ibwrap = inputblock;
17603 cls: 'roo-select2-choices',
17607 cls: 'roo-select2-search-field',
17620 cls: 'roo-select2-container input-group roo-touchview-combobox ',
17625 cls: 'form-hidden-field'
17631 if(!this.multiple && this.showToggleBtn){
17637 if (this.caret != false) {
17640 cls: 'fa fa-' + this.caret
17647 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17649 Roo.bootstrap.version == 3 ? caret : '',
17652 cls: 'combobox-clear',
17666 combobox.cls += ' roo-select2-container-multi';
17669 var required = this.allowBlank ? {
17671 style: 'display: none'
17674 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17675 tooltip : 'This field is required'
17678 var align = this.labelAlign || this.parentLabelAlign();
17680 if (align ==='left' && this.fieldLabel.length) {
17686 cls : 'control-label col-form-label',
17687 html : this.fieldLabel
17691 cls : 'roo-combobox-wrap ',
17698 var labelCfg = cfg.cn[1];
17699 var contentCfg = cfg.cn[2];
17702 if(this.indicatorpos == 'right'){
17707 cls : 'control-label col-form-label',
17711 html : this.fieldLabel
17717 cls : "roo-combobox-wrap ",
17725 labelCfg = cfg.cn[0];
17726 contentCfg = cfg.cn[1];
17731 if(this.labelWidth > 12){
17732 labelCfg.style = "width: " + this.labelWidth + 'px';
17735 if(this.labelWidth < 13 && this.labelmd == 0){
17736 this.labelmd = this.labelWidth;
17739 if(this.labellg > 0){
17740 labelCfg.cls += ' col-lg-' + this.labellg;
17741 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17744 if(this.labelmd > 0){
17745 labelCfg.cls += ' col-md-' + this.labelmd;
17746 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17749 if(this.labelsm > 0){
17750 labelCfg.cls += ' col-sm-' + this.labelsm;
17751 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17754 if(this.labelxs > 0){
17755 labelCfg.cls += ' col-xs-' + this.labelxs;
17756 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17760 } else if ( this.fieldLabel.length) {
17765 cls : 'control-label',
17766 html : this.fieldLabel
17777 if(this.indicatorpos == 'right'){
17781 cls : 'control-label',
17782 html : this.fieldLabel,
17800 var settings = this;
17802 ['xs','sm','md','lg'].map(function(size){
17803 if (settings[size]) {
17804 cfg.cls += ' col-' + size + '-' + settings[size];
17811 initTouchView : function()
17813 this.renderTouchView();
17815 this.touchViewEl.on('scroll', function(){
17816 this.el.dom.scrollTop = 0;
17819 this.originalValue = this.getValue();
17821 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17823 this.inputEl().on("click", this.showTouchView, this);
17824 if (this.triggerEl) {
17825 this.triggerEl.on("click", this.showTouchView, this);
17829 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17830 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17832 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17834 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17835 this.store.on('load', this.onTouchViewLoad, this);
17836 this.store.on('loadexception', this.onTouchViewLoadException, this);
17838 if(this.hiddenName){
17840 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17842 this.hiddenField.dom.value =
17843 this.hiddenValue !== undefined ? this.hiddenValue :
17844 this.value !== undefined ? this.value : '';
17846 this.el.dom.removeAttribute('name');
17847 this.hiddenField.dom.setAttribute('name', this.hiddenName);
17851 this.choices = this.el.select('ul.roo-select2-choices', true).first();
17852 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17855 if(this.removable && !this.multiple){
17856 var close = this.closeTriggerEl();
17858 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17859 close.on('click', this.removeBtnClick, this, close);
17863 * fix the bug in Safari iOS8
17865 this.inputEl().on("focus", function(e){
17866 document.activeElement.blur();
17869 this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17876 renderTouchView : function()
17878 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17879 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17881 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17882 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17884 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17885 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17886 this.touchViewBodyEl.setStyle('overflow', 'auto');
17888 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17889 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17891 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17892 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17896 showTouchView : function()
17902 this.touchViewHeaderEl.hide();
17904 if(this.modalTitle.length){
17905 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17906 this.touchViewHeaderEl.show();
17909 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17910 this.touchViewEl.show();
17912 this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17914 //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17915 // Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17917 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17919 if(this.modalTitle.length){
17920 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17923 this.touchViewBodyEl.setHeight(bodyHeight);
17927 (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17929 this.touchViewEl.addClass(['in','show']);
17932 if(this._touchViewMask){
17933 Roo.get(document.body).addClass("x-body-masked");
17934 this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17935 this._touchViewMask.setStyle('z-index', 10000);
17936 this._touchViewMask.addClass('show');
17939 this.doTouchViewQuery();
17943 hideTouchView : function()
17945 this.touchViewEl.removeClass(['in','show']);
17949 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17951 this.touchViewEl.setStyle('display', 'none');
17954 if(this._touchViewMask){
17955 this._touchViewMask.removeClass('show');
17956 Roo.get(document.body).removeClass("x-body-masked");
17960 setTouchViewValue : function()
17967 Roo.each(this.tickItems, function(o){
17972 this.hideTouchView();
17975 doTouchViewQuery : function()
17984 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17988 if(!this.alwaysQuery || this.mode == 'local'){
17989 this.onTouchViewLoad();
17996 onTouchViewBeforeLoad : function(combo,opts)
18002 onTouchViewLoad : function()
18004 if(this.store.getCount() < 1){
18005 this.onTouchViewEmptyResults();
18009 this.clearTouchView();
18011 var rawValue = this.getRawValue();
18013 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18015 this.tickItems = [];
18017 this.store.data.each(function(d, rowIndex){
18018 var row = this.touchViewListGroup.createChild(template);
18020 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18021 row.addClass(d.data.cls);
18024 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18027 html : d.data[this.displayField]
18030 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18031 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18034 row.removeClass('selected');
18035 if(!this.multiple && this.valueField &&
18036 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18039 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18040 row.addClass('selected');
18043 if(this.multiple && this.valueField &&
18044 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18048 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18049 this.tickItems.push(d.data);
18052 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18056 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18058 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18060 if(this.modalTitle.length){
18061 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18064 var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18066 if(this.mobile_restrict_height && listHeight < bodyHeight){
18067 this.touchViewBodyEl.setHeight(listHeight);
18072 if(firstChecked && listHeight > bodyHeight){
18073 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18078 onTouchViewLoadException : function()
18080 this.hideTouchView();
18083 onTouchViewEmptyResults : function()
18085 this.clearTouchView();
18087 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18089 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18093 clearTouchView : function()
18095 this.touchViewListGroup.dom.innerHTML = '';
18098 onTouchViewClick : function(e, el, o)
18100 e.preventDefault();
18103 var rowIndex = o.rowIndex;
18105 var r = this.store.getAt(rowIndex);
18107 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18109 if(!this.multiple){
18110 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18111 c.dom.removeAttribute('checked');
18114 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18116 this.setFromData(r.data);
18118 var close = this.closeTriggerEl();
18124 this.hideTouchView();
18126 this.fireEvent('select', this, r, rowIndex);
18131 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18132 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18133 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18137 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18138 this.addItem(r.data);
18139 this.tickItems.push(r.data);
18143 getAutoCreateNativeIOS : function()
18146 cls: 'form-group' //input-group,
18151 cls : 'roo-ios-select'
18155 combobox.name = this.name;
18158 if (this.disabled) {
18159 combobox.disabled = true;
18162 var settings = this;
18164 ['xs','sm','md','lg'].map(function(size){
18165 if (settings[size]) {
18166 cfg.cls += ' col-' + size + '-' + settings[size];
18176 initIOSView : function()
18178 this.store.on('load', this.onIOSViewLoad, this);
18183 onIOSViewLoad : function()
18185 if(this.store.getCount() < 1){
18189 this.clearIOSView();
18191 if(this.allowBlank) {
18193 var default_text = '-- SELECT --';
18195 if(this.placeholder.length){
18196 default_text = this.placeholder;
18199 if(this.emptyTitle.length){
18200 default_text += ' - ' + this.emptyTitle + ' -';
18203 var opt = this.inputEl().createChild({
18206 html : default_text
18210 o[this.valueField] = 0;
18211 o[this.displayField] = default_text;
18213 this.ios_options.push({
18220 this.store.data.each(function(d, rowIndex){
18224 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18225 html = d.data[this.displayField];
18230 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18231 value = d.data[this.valueField];
18240 if(this.value == d.data[this.valueField]){
18241 option['selected'] = true;
18244 var opt = this.inputEl().createChild(option);
18246 this.ios_options.push({
18253 this.inputEl().on('change', function(){
18254 this.fireEvent('select', this);
18259 clearIOSView: function()
18261 this.inputEl().dom.innerHTML = '';
18263 this.ios_options = [];
18266 setIOSValue: function(v)
18270 if(!this.ios_options){
18274 Roo.each(this.ios_options, function(opts){
18276 opts.el.dom.removeAttribute('selected');
18278 if(opts.data[this.valueField] != v){
18282 opts.el.dom.setAttribute('selected', true);
18288 * @cfg {Boolean} grow
18292 * @cfg {Number} growMin
18296 * @cfg {Number} growMax
18305 Roo.apply(Roo.bootstrap.ComboBox, {
18309 cls: 'modal-header',
18331 cls: 'list-group-item',
18335 cls: 'roo-combobox-list-group-item-value'
18339 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18353 listItemCheckbox : {
18355 cls: 'list-group-item',
18359 cls: 'roo-combobox-list-group-item-value'
18363 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18379 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18384 cls: 'modal-footer',
18392 cls: 'col-xs-6 text-left',
18395 cls: 'btn btn-danger roo-touch-view-cancel',
18401 cls: 'col-xs-6 text-right',
18404 cls: 'btn btn-success roo-touch-view-ok',
18415 Roo.apply(Roo.bootstrap.ComboBox, {
18417 touchViewTemplate : {
18419 cls: 'modal fade roo-combobox-touch-view',
18423 cls: 'modal-dialog',
18424 style : 'position:fixed', // we have to fix position....
18428 cls: 'modal-content',
18430 Roo.bootstrap.ComboBox.header,
18431 Roo.bootstrap.ComboBox.body,
18432 Roo.bootstrap.ComboBox.footer
18441 * Ext JS Library 1.1.1
18442 * Copyright(c) 2006-2007, Ext JS, LLC.
18444 * Originally Released Under LGPL - original licence link has changed is not relivant.
18447 * <script type="text/javascript">
18452 * @extends Roo.util.Observable
18453 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
18454 * This class also supports single and multi selection modes. <br>
18455 * Create a data model bound view:
18457 var store = new Roo.data.Store(...);
18459 var view = new Roo.View({
18461 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
18463 singleSelect: true,
18464 selectedClass: "ydataview-selected",
18468 // listen for node click?
18469 view.on("click", function(vw, index, node, e){
18470 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18474 dataModel.load("foobar.xml");
18476 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18478 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18479 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18481 * Note: old style constructor is still suported (container, template, config)
18484 * Create a new View
18485 * @param {Object} config The config object
18488 Roo.View = function(config, depreciated_tpl, depreciated_config){
18490 this.parent = false;
18492 if (typeof(depreciated_tpl) == 'undefined') {
18493 // new way.. - universal constructor.
18494 Roo.apply(this, config);
18495 this.el = Roo.get(this.el);
18498 this.el = Roo.get(config);
18499 this.tpl = depreciated_tpl;
18500 Roo.apply(this, depreciated_config);
18502 this.wrapEl = this.el.wrap().wrap();
18503 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18506 if(typeof(this.tpl) == "string"){
18507 this.tpl = new Roo.Template(this.tpl);
18509 // support xtype ctors..
18510 this.tpl = new Roo.factory(this.tpl, Roo);
18514 this.tpl.compile();
18519 * @event beforeclick
18520 * Fires before a click is processed. Returns false to cancel the default action.
18521 * @param {Roo.View} this
18522 * @param {Number} index The index of the target node
18523 * @param {HTMLElement} node The target node
18524 * @param {Roo.EventObject} e The raw event object
18526 "beforeclick" : true,
18529 * Fires when a template node is clicked.
18530 * @param {Roo.View} this
18531 * @param {Number} index The index of the target node
18532 * @param {HTMLElement} node The target node
18533 * @param {Roo.EventObject} e The raw event object
18538 * Fires when a template node is double clicked.
18539 * @param {Roo.View} this
18540 * @param {Number} index The index of the target node
18541 * @param {HTMLElement} node The target node
18542 * @param {Roo.EventObject} e The raw event object
18546 * @event contextmenu
18547 * Fires when a template node is right clicked.
18548 * @param {Roo.View} this
18549 * @param {Number} index The index of the target node
18550 * @param {HTMLElement} node The target node
18551 * @param {Roo.EventObject} e The raw event object
18553 "contextmenu" : true,
18555 * @event selectionchange
18556 * Fires when the selected nodes change.
18557 * @param {Roo.View} this
18558 * @param {Array} selections Array of the selected nodes
18560 "selectionchange" : true,
18563 * @event beforeselect
18564 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18565 * @param {Roo.View} this
18566 * @param {HTMLElement} node The node to be selected
18567 * @param {Array} selections Array of currently selected nodes
18569 "beforeselect" : true,
18571 * @event preparedata
18572 * Fires on every row to render, to allow you to change the data.
18573 * @param {Roo.View} this
18574 * @param {Object} data to be rendered (change this)
18576 "preparedata" : true
18584 "click": this.onClick,
18585 "dblclick": this.onDblClick,
18586 "contextmenu": this.onContextMenu,
18590 this.selections = [];
18592 this.cmp = new Roo.CompositeElementLite([]);
18594 this.store = Roo.factory(this.store, Roo.data);
18595 this.setStore(this.store, true);
18598 if ( this.footer && this.footer.xtype) {
18600 var fctr = this.wrapEl.appendChild(document.createElement("div"));
18602 this.footer.dataSource = this.store;
18603 this.footer.container = fctr;
18604 this.footer = Roo.factory(this.footer, Roo);
18605 fctr.insertFirst(this.el);
18607 // this is a bit insane - as the paging toolbar seems to detach the el..
18608 // dom.parentNode.parentNode.parentNode
18609 // they get detached?
18613 Roo.View.superclass.constructor.call(this);
18618 Roo.extend(Roo.View, Roo.util.Observable, {
18621 * @cfg {Roo.data.Store} store Data store to load data from.
18626 * @cfg {String|Roo.Element} el The container element.
18631 * @cfg {String|Roo.Template} tpl The template used by this View
18635 * @cfg {String} dataName the named area of the template to use as the data area
18636 * Works with domtemplates roo-name="name"
18640 * @cfg {String} selectedClass The css class to add to selected nodes
18642 selectedClass : "x-view-selected",
18644 * @cfg {String} emptyText The empty text to show when nothing is loaded.
18649 * @cfg {String} text to display on mask (default Loading)
18653 * @cfg {Boolean} multiSelect Allow multiple selection
18655 multiSelect : false,
18657 * @cfg {Boolean} singleSelect Allow single selection
18659 singleSelect: false,
18662 * @cfg {Boolean} toggleSelect - selecting
18664 toggleSelect : false,
18667 * @cfg {Boolean} tickable - selecting
18672 * Returns the element this view is bound to.
18673 * @return {Roo.Element}
18675 getEl : function(){
18676 return this.wrapEl;
18682 * Refreshes the view. - called by datachanged on the store. - do not call directly.
18684 refresh : function(){
18685 //Roo.log('refresh');
18688 // if we are using something like 'domtemplate', then
18689 // the what gets used is:
18690 // t.applySubtemplate(NAME, data, wrapping data..)
18691 // the outer template then get' applied with
18692 // the store 'extra data'
18693 // and the body get's added to the
18694 // roo-name="data" node?
18695 // <span class='roo-tpl-{name}'></span> ?????
18699 this.clearSelections();
18700 this.el.update("");
18702 var records = this.store.getRange();
18703 if(records.length < 1) {
18705 // is this valid?? = should it render a template??
18707 this.el.update(this.emptyText);
18711 if (this.dataName) {
18712 this.el.update(t.apply(this.store.meta)); //????
18713 el = this.el.child('.roo-tpl-' + this.dataName);
18716 for(var i = 0, len = records.length; i < len; i++){
18717 var data = this.prepareData(records[i].data, i, records[i]);
18718 this.fireEvent("preparedata", this, data, i, records[i]);
18720 var d = Roo.apply({}, data);
18723 Roo.apply(d, {'roo-id' : Roo.id()});
18727 Roo.each(this.parent.item, function(item){
18728 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18731 Roo.apply(d, {'roo-data-checked' : 'checked'});
18735 html[html.length] = Roo.util.Format.trim(
18737 t.applySubtemplate(this.dataName, d, this.store.meta) :
18744 el.update(html.join(""));
18745 this.nodes = el.dom.childNodes;
18746 this.updateIndexes(0);
18751 * Function to override to reformat the data that is sent to
18752 * the template for each node.
18753 * DEPRICATED - use the preparedata event handler.
18754 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18755 * a JSON object for an UpdateManager bound view).
18757 prepareData : function(data, index, record)
18759 this.fireEvent("preparedata", this, data, index, record);
18763 onUpdate : function(ds, record){
18764 // Roo.log('on update');
18765 this.clearSelections();
18766 var index = this.store.indexOf(record);
18767 var n = this.nodes[index];
18768 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18769 n.parentNode.removeChild(n);
18770 this.updateIndexes(index, index);
18776 onAdd : function(ds, records, index)
18778 //Roo.log(['on Add', ds, records, index] );
18779 this.clearSelections();
18780 if(this.nodes.length == 0){
18784 var n = this.nodes[index];
18785 for(var i = 0, len = records.length; i < len; i++){
18786 var d = this.prepareData(records[i].data, i, records[i]);
18788 this.tpl.insertBefore(n, d);
18791 this.tpl.append(this.el, d);
18794 this.updateIndexes(index);
18797 onRemove : function(ds, record, index){
18798 // Roo.log('onRemove');
18799 this.clearSelections();
18800 var el = this.dataName ?
18801 this.el.child('.roo-tpl-' + this.dataName) :
18804 el.dom.removeChild(this.nodes[index]);
18805 this.updateIndexes(index);
18809 * Refresh an individual node.
18810 * @param {Number} index
18812 refreshNode : function(index){
18813 this.onUpdate(this.store, this.store.getAt(index));
18816 updateIndexes : function(startIndex, endIndex){
18817 var ns = this.nodes;
18818 startIndex = startIndex || 0;
18819 endIndex = endIndex || ns.length - 1;
18820 for(var i = startIndex; i <= endIndex; i++){
18821 ns[i].nodeIndex = i;
18826 * Changes the data store this view uses and refresh the view.
18827 * @param {Store} store
18829 setStore : function(store, initial){
18830 if(!initial && this.store){
18831 this.store.un("datachanged", this.refresh);
18832 this.store.un("add", this.onAdd);
18833 this.store.un("remove", this.onRemove);
18834 this.store.un("update", this.onUpdate);
18835 this.store.un("clear", this.refresh);
18836 this.store.un("beforeload", this.onBeforeLoad);
18837 this.store.un("load", this.onLoad);
18838 this.store.un("loadexception", this.onLoad);
18842 store.on("datachanged", this.refresh, this);
18843 store.on("add", this.onAdd, this);
18844 store.on("remove", this.onRemove, this);
18845 store.on("update", this.onUpdate, this);
18846 store.on("clear", this.refresh, this);
18847 store.on("beforeload", this.onBeforeLoad, this);
18848 store.on("load", this.onLoad, this);
18849 store.on("loadexception", this.onLoad, this);
18857 * onbeforeLoad - masks the loading area.
18860 onBeforeLoad : function(store,opts)
18862 //Roo.log('onBeforeLoad');
18864 this.el.update("");
18866 this.el.mask(this.mask ? this.mask : "Loading" );
18868 onLoad : function ()
18875 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18876 * @param {HTMLElement} node
18877 * @return {HTMLElement} The template node
18879 findItemFromChild : function(node){
18880 var el = this.dataName ?
18881 this.el.child('.roo-tpl-' + this.dataName,true) :
18884 if(!node || node.parentNode == el){
18887 var p = node.parentNode;
18888 while(p && p != el){
18889 if(p.parentNode == el){
18898 onClick : function(e){
18899 var item = this.findItemFromChild(e.getTarget());
18901 var index = this.indexOf(item);
18902 if(this.onItemClick(item, index, e) !== false){
18903 this.fireEvent("click", this, index, item, e);
18906 this.clearSelections();
18911 onContextMenu : function(e){
18912 var item = this.findItemFromChild(e.getTarget());
18914 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18919 onDblClick : function(e){
18920 var item = this.findItemFromChild(e.getTarget());
18922 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18926 onItemClick : function(item, index, e)
18928 if(this.fireEvent("beforeclick", this, index, item, e) === false){
18931 if (this.toggleSelect) {
18932 var m = this.isSelected(item) ? 'unselect' : 'select';
18935 _t[m](item, true, false);
18938 if(this.multiSelect || this.singleSelect){
18939 if(this.multiSelect && e.shiftKey && this.lastSelection){
18940 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18942 this.select(item, this.multiSelect && e.ctrlKey);
18943 this.lastSelection = item;
18946 if(!this.tickable){
18947 e.preventDefault();
18955 * Get the number of selected nodes.
18958 getSelectionCount : function(){
18959 return this.selections.length;
18963 * Get the currently selected nodes.
18964 * @return {Array} An array of HTMLElements
18966 getSelectedNodes : function(){
18967 return this.selections;
18971 * Get the indexes of the selected nodes.
18974 getSelectedIndexes : function(){
18975 var indexes = [], s = this.selections;
18976 for(var i = 0, len = s.length; i < len; i++){
18977 indexes.push(s[i].nodeIndex);
18983 * Clear all selections
18984 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18986 clearSelections : function(suppressEvent){
18987 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18988 this.cmp.elements = this.selections;
18989 this.cmp.removeClass(this.selectedClass);
18990 this.selections = [];
18991 if(!suppressEvent){
18992 this.fireEvent("selectionchange", this, this.selections);
18998 * Returns true if the passed node is selected
18999 * @param {HTMLElement/Number} node The node or node index
19000 * @return {Boolean}
19002 isSelected : function(node){
19003 var s = this.selections;
19007 node = this.getNode(node);
19008 return s.indexOf(node) !== -1;
19013 * @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
19014 * @param {Boolean} keepExisting (optional) true to keep existing selections
19015 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19017 select : function(nodeInfo, keepExisting, suppressEvent){
19018 if(nodeInfo instanceof Array){
19020 this.clearSelections(true);
19022 for(var i = 0, len = nodeInfo.length; i < len; i++){
19023 this.select(nodeInfo[i], true, true);
19027 var node = this.getNode(nodeInfo);
19028 if(!node || this.isSelected(node)){
19029 return; // already selected.
19032 this.clearSelections(true);
19035 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19036 Roo.fly(node).addClass(this.selectedClass);
19037 this.selections.push(node);
19038 if(!suppressEvent){
19039 this.fireEvent("selectionchange", this, this.selections);
19047 * @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
19048 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19049 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19051 unselect : function(nodeInfo, keepExisting, suppressEvent)
19053 if(nodeInfo instanceof Array){
19054 Roo.each(this.selections, function(s) {
19055 this.unselect(s, nodeInfo);
19059 var node = this.getNode(nodeInfo);
19060 if(!node || !this.isSelected(node)){
19061 //Roo.log("not selected");
19062 return; // not selected.
19066 Roo.each(this.selections, function(s) {
19068 Roo.fly(node).removeClass(this.selectedClass);
19075 this.selections= ns;
19076 this.fireEvent("selectionchange", this, this.selections);
19080 * Gets a template node.
19081 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19082 * @return {HTMLElement} The node or null if it wasn't found
19084 getNode : function(nodeInfo){
19085 if(typeof nodeInfo == "string"){
19086 return document.getElementById(nodeInfo);
19087 }else if(typeof nodeInfo == "number"){
19088 return this.nodes[nodeInfo];
19094 * Gets a range template nodes.
19095 * @param {Number} startIndex
19096 * @param {Number} endIndex
19097 * @return {Array} An array of nodes
19099 getNodes : function(start, end){
19100 var ns = this.nodes;
19101 start = start || 0;
19102 end = typeof end == "undefined" ? ns.length - 1 : end;
19105 for(var i = start; i <= end; i++){
19109 for(var i = start; i >= end; i--){
19117 * Finds the index of the passed node
19118 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19119 * @return {Number} The index of the node or -1
19121 indexOf : function(node){
19122 node = this.getNode(node);
19123 if(typeof node.nodeIndex == "number"){
19124 return node.nodeIndex;
19126 var ns = this.nodes;
19127 for(var i = 0, len = ns.length; i < len; i++){
19138 * based on jquery fullcalendar
19142 Roo.bootstrap = Roo.bootstrap || {};
19144 * @class Roo.bootstrap.Calendar
19145 * @extends Roo.bootstrap.Component
19146 * Bootstrap Calendar class
19147 * @cfg {Boolean} loadMask (true|false) default false
19148 * @cfg {Object} header generate the user specific header of the calendar, default false
19151 * Create a new Container
19152 * @param {Object} config The config object
19157 Roo.bootstrap.Calendar = function(config){
19158 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19162 * Fires when a date is selected
19163 * @param {DatePicker} this
19164 * @param {Date} date The selected date
19168 * @event monthchange
19169 * Fires when the displayed month changes
19170 * @param {DatePicker} this
19171 * @param {Date} date The selected month
19173 'monthchange': true,
19175 * @event evententer
19176 * Fires when mouse over an event
19177 * @param {Calendar} this
19178 * @param {event} Event
19180 'evententer': true,
19182 * @event eventleave
19183 * Fires when the mouse leaves an
19184 * @param {Calendar} this
19187 'eventleave': true,
19189 * @event eventclick
19190 * Fires when the mouse click an
19191 * @param {Calendar} this
19200 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
19203 * @cfg {Number} startDay
19204 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19212 getAutoCreate : function(){
19215 var fc_button = function(name, corner, style, content ) {
19216 return Roo.apply({},{
19218 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
19220 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19223 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19234 style : 'width:100%',
19241 cls : 'fc-header-left',
19243 fc_button('prev', 'left', 'arrow', '‹' ),
19244 fc_button('next', 'right', 'arrow', '›' ),
19245 { tag: 'span', cls: 'fc-header-space' },
19246 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
19254 cls : 'fc-header-center',
19258 cls: 'fc-header-title',
19261 html : 'month / year'
19269 cls : 'fc-header-right',
19271 /* fc_button('month', 'left', '', 'month' ),
19272 fc_button('week', '', '', 'week' ),
19273 fc_button('day', 'right', '', 'day' )
19285 header = this.header;
19288 var cal_heads = function() {
19290 // fixme - handle this.
19292 for (var i =0; i < Date.dayNames.length; i++) {
19293 var d = Date.dayNames[i];
19296 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19297 html : d.substring(0,3)
19301 ret[0].cls += ' fc-first';
19302 ret[6].cls += ' fc-last';
19305 var cal_cell = function(n) {
19308 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19313 cls: 'fc-day-number',
19317 cls: 'fc-day-content',
19321 style: 'position: relative;' // height: 17px;
19333 var cal_rows = function() {
19336 for (var r = 0; r < 6; r++) {
19343 for (var i =0; i < Date.dayNames.length; i++) {
19344 var d = Date.dayNames[i];
19345 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19348 row.cn[0].cls+=' fc-first';
19349 row.cn[0].cn[0].style = 'min-height:90px';
19350 row.cn[6].cls+=' fc-last';
19354 ret[0].cls += ' fc-first';
19355 ret[4].cls += ' fc-prev-last';
19356 ret[5].cls += ' fc-last';
19363 cls: 'fc-border-separate',
19364 style : 'width:100%',
19372 cls : 'fc-first fc-last',
19390 cls : 'fc-content',
19391 style : "position: relative;",
19394 cls : 'fc-view fc-view-month fc-grid',
19395 style : 'position: relative',
19396 unselectable : 'on',
19399 cls : 'fc-event-container',
19400 style : 'position:absolute;z-index:8;top:0;left:0;'
19418 initEvents : function()
19421 throw "can not find store for calendar";
19427 style: "text-align:center",
19431 style: "background-color:white;width:50%;margin:250 auto",
19435 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
19446 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19448 var size = this.el.select('.fc-content', true).first().getSize();
19449 this.maskEl.setSize(size.width, size.height);
19450 this.maskEl.enableDisplayMode("block");
19451 if(!this.loadMask){
19452 this.maskEl.hide();
19455 this.store = Roo.factory(this.store, Roo.data);
19456 this.store.on('load', this.onLoad, this);
19457 this.store.on('beforeload', this.onBeforeLoad, this);
19461 this.cells = this.el.select('.fc-day',true);
19462 //Roo.log(this.cells);
19463 this.textNodes = this.el.query('.fc-day-number');
19464 this.cells.addClassOnOver('fc-state-hover');
19466 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19467 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19468 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19469 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19471 this.on('monthchange', this.onMonthChange, this);
19473 this.update(new Date().clearTime());
19476 resize : function() {
19477 var sz = this.el.getSize();
19479 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19480 this.el.select('.fc-day-content div',true).setHeight(34);
19485 showPrevMonth : function(e){
19486 this.update(this.activeDate.add("mo", -1));
19488 showToday : function(e){
19489 this.update(new Date().clearTime());
19492 showNextMonth : function(e){
19493 this.update(this.activeDate.add("mo", 1));
19497 showPrevYear : function(){
19498 this.update(this.activeDate.add("y", -1));
19502 showNextYear : function(){
19503 this.update(this.activeDate.add("y", 1));
19508 update : function(date)
19510 var vd = this.activeDate;
19511 this.activeDate = date;
19512 // if(vd && this.el){
19513 // var t = date.getTime();
19514 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19515 // Roo.log('using add remove');
19517 // this.fireEvent('monthchange', this, date);
19519 // this.cells.removeClass("fc-state-highlight");
19520 // this.cells.each(function(c){
19521 // if(c.dateValue == t){
19522 // c.addClass("fc-state-highlight");
19523 // setTimeout(function(){
19524 // try{c.dom.firstChild.focus();}catch(e){}
19534 var days = date.getDaysInMonth();
19536 var firstOfMonth = date.getFirstDateOfMonth();
19537 var startingPos = firstOfMonth.getDay()-this.startDay;
19539 if(startingPos < this.startDay){
19543 var pm = date.add(Date.MONTH, -1);
19544 var prevStart = pm.getDaysInMonth()-startingPos;
19546 this.cells = this.el.select('.fc-day',true);
19547 this.textNodes = this.el.query('.fc-day-number');
19548 this.cells.addClassOnOver('fc-state-hover');
19550 var cells = this.cells.elements;
19551 var textEls = this.textNodes;
19553 Roo.each(cells, function(cell){
19554 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19557 days += startingPos;
19559 // convert everything to numbers so it's fast
19560 var day = 86400000;
19561 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19564 //Roo.log(prevStart);
19566 var today = new Date().clearTime().getTime();
19567 var sel = date.clearTime().getTime();
19568 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19569 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19570 var ddMatch = this.disabledDatesRE;
19571 var ddText = this.disabledDatesText;
19572 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19573 var ddaysText = this.disabledDaysText;
19574 var format = this.format;
19576 var setCellClass = function(cal, cell){
19580 //Roo.log('set Cell Class');
19582 var t = d.getTime();
19586 cell.dateValue = t;
19588 cell.className += " fc-today";
19589 cell.className += " fc-state-highlight";
19590 cell.title = cal.todayText;
19593 // disable highlight in other month..
19594 //cell.className += " fc-state-highlight";
19599 cell.className = " fc-state-disabled";
19600 cell.title = cal.minText;
19604 cell.className = " fc-state-disabled";
19605 cell.title = cal.maxText;
19609 if(ddays.indexOf(d.getDay()) != -1){
19610 cell.title = ddaysText;
19611 cell.className = " fc-state-disabled";
19614 if(ddMatch && format){
19615 var fvalue = d.dateFormat(format);
19616 if(ddMatch.test(fvalue)){
19617 cell.title = ddText.replace("%0", fvalue);
19618 cell.className = " fc-state-disabled";
19622 if (!cell.initialClassName) {
19623 cell.initialClassName = cell.dom.className;
19626 cell.dom.className = cell.initialClassName + ' ' + cell.className;
19631 for(; i < startingPos; i++) {
19632 textEls[i].innerHTML = (++prevStart);
19633 d.setDate(d.getDate()+1);
19635 cells[i].className = "fc-past fc-other-month";
19636 setCellClass(this, cells[i]);
19641 for(; i < days; i++){
19642 intDay = i - startingPos + 1;
19643 textEls[i].innerHTML = (intDay);
19644 d.setDate(d.getDate()+1);
19646 cells[i].className = ''; // "x-date-active";
19647 setCellClass(this, cells[i]);
19651 for(; i < 42; i++) {
19652 textEls[i].innerHTML = (++extraDays);
19653 d.setDate(d.getDate()+1);
19655 cells[i].className = "fc-future fc-other-month";
19656 setCellClass(this, cells[i]);
19659 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19661 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19663 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19664 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19666 if(totalRows != 6){
19667 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19668 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19671 this.fireEvent('monthchange', this, date);
19675 if(!this.internalRender){
19676 var main = this.el.dom.firstChild;
19677 var w = main.offsetWidth;
19678 this.el.setWidth(w + this.el.getBorderWidth("lr"));
19679 Roo.fly(main).setWidth(w);
19680 this.internalRender = true;
19681 // opera does not respect the auto grow header center column
19682 // then, after it gets a width opera refuses to recalculate
19683 // without a second pass
19684 if(Roo.isOpera && !this.secondPass){
19685 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19686 this.secondPass = true;
19687 this.update.defer(10, this, [date]);
19694 findCell : function(dt) {
19695 dt = dt.clearTime().getTime();
19697 this.cells.each(function(c){
19698 //Roo.log("check " +c.dateValue + '?=' + dt);
19699 if(c.dateValue == dt){
19709 findCells : function(ev) {
19710 var s = ev.start.clone().clearTime().getTime();
19712 var e= ev.end.clone().clearTime().getTime();
19715 this.cells.each(function(c){
19716 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19718 if(c.dateValue > e){
19721 if(c.dateValue < s){
19730 // findBestRow: function(cells)
19734 // for (var i =0 ; i < cells.length;i++) {
19735 // ret = Math.max(cells[i].rows || 0,ret);
19742 addItem : function(ev)
19744 // look for vertical location slot in
19745 var cells = this.findCells(ev);
19747 // ev.row = this.findBestRow(cells);
19749 // work out the location.
19753 for(var i =0; i < cells.length; i++) {
19755 cells[i].row = cells[0].row;
19758 cells[i].row = cells[i].row + 1;
19768 if (crow.start.getY() == cells[i].getY()) {
19770 crow.end = cells[i];
19787 cells[0].events.push(ev);
19789 this.calevents.push(ev);
19792 clearEvents: function() {
19794 if(!this.calevents){
19798 Roo.each(this.cells.elements, function(c){
19804 Roo.each(this.calevents, function(e) {
19805 Roo.each(e.els, function(el) {
19806 el.un('mouseenter' ,this.onEventEnter, this);
19807 el.un('mouseleave' ,this.onEventLeave, this);
19812 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19818 renderEvents: function()
19822 this.cells.each(function(c) {
19831 if(c.row != c.events.length){
19832 r = 4 - (4 - (c.row - c.events.length));
19835 c.events = ev.slice(0, r);
19836 c.more = ev.slice(r);
19838 if(c.more.length && c.more.length == 1){
19839 c.events.push(c.more.pop());
19842 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19846 this.cells.each(function(c) {
19848 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19851 for (var e = 0; e < c.events.length; e++){
19852 var ev = c.events[e];
19853 var rows = ev.rows;
19855 for(var i = 0; i < rows.length; i++) {
19857 // how many rows should it span..
19860 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19861 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19863 unselectable : "on",
19866 cls: 'fc-event-inner',
19870 // cls: 'fc-event-time',
19871 // html : cells.length > 1 ? '' : ev.time
19875 cls: 'fc-event-title',
19876 html : String.format('{0}', ev.title)
19883 cls: 'ui-resizable-handle ui-resizable-e',
19884 html : '  '
19891 cfg.cls += ' fc-event-start';
19893 if ((i+1) == rows.length) {
19894 cfg.cls += ' fc-event-end';
19897 var ctr = _this.el.select('.fc-event-container',true).first();
19898 var cg = ctr.createChild(cfg);
19900 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19901 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19903 var r = (c.more.length) ? 1 : 0;
19904 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
19905 cg.setWidth(ebox.right - sbox.x -2);
19907 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19908 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19909 cg.on('click', _this.onEventClick, _this, ev);
19920 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19921 style : 'position: absolute',
19922 unselectable : "on",
19925 cls: 'fc-event-inner',
19929 cls: 'fc-event-title',
19937 cls: 'ui-resizable-handle ui-resizable-e',
19938 html : '  '
19944 var ctr = _this.el.select('.fc-event-container',true).first();
19945 var cg = ctr.createChild(cfg);
19947 var sbox = c.select('.fc-day-content',true).first().getBox();
19948 var ebox = c.select('.fc-day-content',true).first().getBox();
19950 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
19951 cg.setWidth(ebox.right - sbox.x -2);
19953 cg.on('click', _this.onMoreEventClick, _this, c.more);
19963 onEventEnter: function (e, el,event,d) {
19964 this.fireEvent('evententer', this, el, event);
19967 onEventLeave: function (e, el,event,d) {
19968 this.fireEvent('eventleave', this, el, event);
19971 onEventClick: function (e, el,event,d) {
19972 this.fireEvent('eventclick', this, el, event);
19975 onMonthChange: function () {
19979 onMoreEventClick: function(e, el, more)
19983 this.calpopover.placement = 'right';
19984 this.calpopover.setTitle('More');
19986 this.calpopover.setContent('');
19988 var ctr = this.calpopover.el.select('.popover-content', true).first();
19990 Roo.each(more, function(m){
19992 cls : 'fc-event-hori fc-event-draggable',
19995 var cg = ctr.createChild(cfg);
19997 cg.on('click', _this.onEventClick, _this, m);
20000 this.calpopover.show(el);
20005 onLoad: function ()
20007 this.calevents = [];
20010 if(this.store.getCount() > 0){
20011 this.store.data.each(function(d){
20014 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20015 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20016 time : d.data.start_time,
20017 title : d.data.title,
20018 description : d.data.description,
20019 venue : d.data.venue
20024 this.renderEvents();
20026 if(this.calevents.length && this.loadMask){
20027 this.maskEl.hide();
20031 onBeforeLoad: function()
20033 this.clearEvents();
20035 this.maskEl.show();
20049 * @class Roo.bootstrap.Popover
20050 * @extends Roo.bootstrap.Component
20051 * Bootstrap Popover class
20052 * @cfg {String} html contents of the popover (or false to use children..)
20053 * @cfg {String} title of popover (or false to hide)
20054 * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20055 * @cfg {String} trigger click || hover (or false to trigger manually)
20056 * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20057 * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20058 * - if false and it has a 'parent' then it will be automatically added to that element
20059 * - if string - Roo.get will be called
20060 * @cfg {Number} delay - delay before showing
20063 * Create a new Popover
20064 * @param {Object} config The config object
20067 Roo.bootstrap.Popover = function(config){
20068 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20074 * After the popover show
20076 * @param {Roo.bootstrap.Popover} this
20081 * After the popover hide
20083 * @param {Roo.bootstrap.Popover} this
20089 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
20094 placement : 'right',
20095 trigger : 'hover', // hover
20101 can_build_overlaid : false,
20103 maskEl : false, // the mask element
20106 alignEl : false, // when show is called with an element - this get's stored.
20108 getChildContainer : function()
20110 return this.contentEl;
20113 getPopoverHeader : function()
20115 this.title = true; // flag not to hide it..
20116 this.headerEl.addClass('p-0');
20117 return this.headerEl
20121 getAutoCreate : function(){
20124 cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20125 style: 'display:block',
20131 cls : 'popover-inner ',
20135 cls: 'popover-title popover-header',
20136 html : this.title === false ? '' : this.title
20139 cls : 'popover-content popover-body ' + (this.cls || ''),
20140 html : this.html || ''
20151 * @param {string} the title
20153 setTitle: function(str)
20157 this.headerEl.dom.innerHTML = str;
20162 * @param {string} the body content
20164 setContent: function(str)
20167 if (this.contentEl) {
20168 this.contentEl.dom.innerHTML = str;
20172 // as it get's added to the bottom of the page.
20173 onRender : function(ct, position)
20175 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20180 var cfg = Roo.apply({}, this.getAutoCreate());
20184 cfg.cls += ' ' + this.cls;
20187 cfg.style = this.style;
20189 //Roo.log("adding to ");
20190 this.el = Roo.get(document.body).createChild(cfg, position);
20191 // Roo.log(this.el);
20194 this.contentEl = this.el.select('.popover-content',true).first();
20195 this.headerEl = this.el.select('.popover-title',true).first();
20198 if(typeof(this.items) != 'undefined'){
20199 var items = this.items;
20202 for(var i =0;i < items.length;i++) {
20203 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20207 this.items = nitems;
20209 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20210 Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20217 resizeMask : function()
20219 this.maskEl.setSize(
20220 Roo.lib.Dom.getViewWidth(true),
20221 Roo.lib.Dom.getViewHeight(true)
20225 initEvents : function()
20229 Roo.bootstrap.Popover.register(this);
20232 this.arrowEl = this.el.select('.arrow',true).first();
20233 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20234 this.el.enableDisplayMode('block');
20238 if (this.over === false && !this.parent()) {
20241 if (this.triggers === false) {
20246 var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20247 var triggers = this.trigger ? this.trigger.split(' ') : [];
20248 Roo.each(triggers, function(trigger) {
20250 if (trigger == 'click') {
20251 on_el.on('click', this.toggle, this);
20252 } else if (trigger != 'manual') {
20253 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
20254 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20256 on_el.on(eventIn ,this.enter, this);
20257 on_el.on(eventOut, this.leave, this);
20267 toggle : function () {
20268 this.hoverState == 'in' ? this.leave() : this.enter();
20271 enter : function () {
20273 clearTimeout(this.timeout);
20275 this.hoverState = 'in';
20277 if (!this.delay || !this.delay.show) {
20282 this.timeout = setTimeout(function () {
20283 if (_t.hoverState == 'in') {
20286 }, this.delay.show)
20289 leave : function() {
20290 clearTimeout(this.timeout);
20292 this.hoverState = 'out';
20294 if (!this.delay || !this.delay.hide) {
20299 this.timeout = setTimeout(function () {
20300 if (_t.hoverState == 'out') {
20303 }, this.delay.hide)
20307 * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20308 * @param {string} (left|right|top|bottom) position
20310 show : function (on_el, placement)
20312 this.placement = typeof(placement) == 'undefined' ? this.placement : placement;
20313 on_el = on_el || false; // default to false
20316 if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20317 on_el = this.parent().el;
20318 } else if (this.over) {
20319 on_el = Roo.get(this.over);
20324 this.alignEl = Roo.get( on_el );
20327 this.render(document.body);
20333 if (this.title === false) {
20334 this.headerEl.hide();
20339 this.el.dom.style.display = 'block';
20342 if (this.alignEl) {
20343 this.updatePosition(this.placement, true);
20346 // this is usually just done by the builder = to show the popoup in the middle of the scren.
20347 var es = this.el.getSize();
20348 var x = Roo.lib.Dom.getViewWidth()/2;
20349 var y = Roo.lib.Dom.getViewHeight()/2;
20350 this.el.setXY([ x-(es.width/2), y-(es.height/2)] );
20355 //var arrow = this.el.select('.arrow',true).first();
20356 //arrow.set(align[2],
20358 this.el.addClass('in');
20362 this.hoverState = 'in';
20365 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
20366 this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20367 this.maskEl.dom.style.display = 'block';
20368 this.maskEl.addClass('show');
20370 this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20372 this.fireEvent('show', this);
20376 * fire this manually after loading a grid in the table for example
20377 * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20378 * @param {Boolean} try and move it if we cant get right position.
20380 updatePosition : function(placement, try_move)
20382 // allow for calling with no parameters
20383 placement = placement ? placement : this.placement;
20384 try_move = typeof(try_move) == 'undefined' ? true : try_move;
20386 this.el.removeClass([
20387 'fade','top','bottom', 'left', 'right','in',
20388 'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20390 this.el.addClass(placement + ' bs-popover-' + placement);
20392 if (!this.alignEl ) {
20396 switch (placement) {
20398 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20399 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20400 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20401 //normal display... or moved up/down.
20402 this.el.setXY(offset);
20403 var xy = this.alignEl.getAnchorXY('tr', false);
20405 this.arrowEl.setXY(xy);
20408 // continue through...
20409 return this.updatePosition('left', false);
20413 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20414 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20415 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20416 //normal display... or moved up/down.
20417 this.el.setXY(offset);
20418 var xy = this.alignEl.getAnchorXY('tl', false);
20419 xy[0]-=10;xy[1]+=5; // << fix me
20420 this.arrowEl.setXY(xy);
20424 return this.updatePosition('right', false);
20427 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20428 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20429 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20430 //normal display... or moved up/down.
20431 this.el.setXY(offset);
20432 var xy = this.alignEl.getAnchorXY('t', false);
20433 xy[1]-=10; // << fix me
20434 this.arrowEl.setXY(xy);
20438 return this.updatePosition('bottom', false);
20441 var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20442 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20443 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20444 //normal display... or moved up/down.
20445 this.el.setXY(offset);
20446 var xy = this.alignEl.getAnchorXY('b', false);
20447 xy[1]+=2; // << fix me
20448 this.arrowEl.setXY(xy);
20452 return this.updatePosition('top', false);
20463 this.el.setXY([0,0]);
20464 this.el.removeClass('in');
20466 this.hoverState = null;
20467 this.maskEl.hide(); // always..
20468 this.fireEvent('hide', this);
20474 Roo.apply(Roo.bootstrap.Popover, {
20477 'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20478 'right' : ['l-br', [10,0], 'right bs-popover-right'],
20479 'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20480 'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20485 clickHander : false,
20489 onMouseDown : function(e)
20491 if (this.popups.length && !e.getTarget(".roo-popover")) {
20492 /// what is nothing is showing..
20501 register : function(popup)
20503 if (!Roo.bootstrap.Popover.clickHandler) {
20504 Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20506 // hide other popups.
20507 popup.on('show', Roo.bootstrap.Popover.onShow, popup);
20508 popup.on('hide', Roo.bootstrap.Popover.onHide, popup);
20509 this.hideAll(); //<< why?
20510 //this.popups.push(popup);
20512 hideAll : function()
20514 this.popups.forEach(function(p) {
20518 onShow : function() {
20519 Roo.bootstrap.Popover.popups.push(this);
20521 onHide : function() {
20522 Roo.bootstrap.Popover.popups.remove(this);
20528 * Card header - holder for the card header elements.
20533 * @class Roo.bootstrap.PopoverNav
20534 * @extends Roo.bootstrap.NavGroup
20535 * Bootstrap Popover header navigation class
20537 * Create a new Popover Header Navigation
20538 * @param {Object} config The config object
20541 Roo.bootstrap.PopoverNav = function(config){
20542 Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20545 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar, {
20548 container_method : 'getPopoverHeader'
20566 * @class Roo.bootstrap.Progress
20567 * @extends Roo.bootstrap.Component
20568 * Bootstrap Progress class
20569 * @cfg {Boolean} striped striped of the progress bar
20570 * @cfg {Boolean} active animated of the progress bar
20574 * Create a new Progress
20575 * @param {Object} config The config object
20578 Roo.bootstrap.Progress = function(config){
20579 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20582 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
20587 getAutoCreate : function(){
20595 cfg.cls += ' progress-striped';
20599 cfg.cls += ' active';
20618 * @class Roo.bootstrap.ProgressBar
20619 * @extends Roo.bootstrap.Component
20620 * Bootstrap ProgressBar class
20621 * @cfg {Number} aria_valuenow aria-value now
20622 * @cfg {Number} aria_valuemin aria-value min
20623 * @cfg {Number} aria_valuemax aria-value max
20624 * @cfg {String} label label for the progress bar
20625 * @cfg {String} panel (success | info | warning | danger )
20626 * @cfg {String} role role of the progress bar
20627 * @cfg {String} sr_only text
20631 * Create a new ProgressBar
20632 * @param {Object} config The config object
20635 Roo.bootstrap.ProgressBar = function(config){
20636 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20639 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
20643 aria_valuemax : 100,
20649 getAutoCreate : function()
20654 cls: 'progress-bar',
20655 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20667 cfg.role = this.role;
20670 if(this.aria_valuenow){
20671 cfg['aria-valuenow'] = this.aria_valuenow;
20674 if(this.aria_valuemin){
20675 cfg['aria-valuemin'] = this.aria_valuemin;
20678 if(this.aria_valuemax){
20679 cfg['aria-valuemax'] = this.aria_valuemax;
20682 if(this.label && !this.sr_only){
20683 cfg.html = this.label;
20687 cfg.cls += ' progress-bar-' + this.panel;
20693 update : function(aria_valuenow)
20695 this.aria_valuenow = aria_valuenow;
20697 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20712 * @class Roo.bootstrap.TabGroup
20713 * @extends Roo.bootstrap.Column
20714 * Bootstrap Column class
20715 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20716 * @cfg {Boolean} carousel true to make the group behave like a carousel
20717 * @cfg {Boolean} bullets show bullets for the panels
20718 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20719 * @cfg {Number} timer auto slide timer .. default 0 millisecond
20720 * @cfg {Boolean} showarrow (true|false) show arrow default true
20723 * Create a new TabGroup
20724 * @param {Object} config The config object
20727 Roo.bootstrap.TabGroup = function(config){
20728 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20730 this.navId = Roo.id();
20733 Roo.bootstrap.TabGroup.register(this);
20737 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
20740 transition : false,
20745 slideOnTouch : false,
20748 getAutoCreate : function()
20750 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20752 cfg.cls += ' tab-content';
20754 if (this.carousel) {
20755 cfg.cls += ' carousel slide';
20758 cls : 'carousel-inner',
20762 if(this.bullets && !Roo.isTouch){
20765 cls : 'carousel-bullets',
20769 if(this.bullets_cls){
20770 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20777 cfg.cn[0].cn.push(bullets);
20780 if(this.showarrow){
20781 cfg.cn[0].cn.push({
20783 class : 'carousel-arrow',
20787 class : 'carousel-prev',
20791 class : 'fa fa-chevron-left'
20797 class : 'carousel-next',
20801 class : 'fa fa-chevron-right'
20814 initEvents: function()
20816 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20817 // this.el.on("touchstart", this.onTouchStart, this);
20820 if(this.autoslide){
20823 this.slideFn = window.setInterval(function() {
20824 _this.showPanelNext();
20828 if(this.showarrow){
20829 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20830 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20836 // onTouchStart : function(e, el, o)
20838 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20842 // this.showPanelNext();
20846 getChildContainer : function()
20848 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20852 * register a Navigation item
20853 * @param {Roo.bootstrap.NavItem} the navitem to add
20855 register : function(item)
20857 this.tabs.push( item);
20858 item.navId = this.navId; // not really needed..
20863 getActivePanel : function()
20866 Roo.each(this.tabs, function(t) {
20876 getPanelByName : function(n)
20879 Roo.each(this.tabs, function(t) {
20880 if (t.tabId == n) {
20888 indexOfPanel : function(p)
20891 Roo.each(this.tabs, function(t,i) {
20892 if (t.tabId == p.tabId) {
20901 * show a specific panel
20902 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20903 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20905 showPanel : function (pan)
20907 if(this.transition || typeof(pan) == 'undefined'){
20908 Roo.log("waiting for the transitionend");
20912 if (typeof(pan) == 'number') {
20913 pan = this.tabs[pan];
20916 if (typeof(pan) == 'string') {
20917 pan = this.getPanelByName(pan);
20920 var cur = this.getActivePanel();
20923 Roo.log('pan or acitve pan is undefined');
20927 if (pan.tabId == this.getActivePanel().tabId) {
20931 if (false === cur.fireEvent('beforedeactivate')) {
20935 if(this.bullets > 0 && !Roo.isTouch){
20936 this.setActiveBullet(this.indexOfPanel(pan));
20939 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20941 //class="carousel-item carousel-item-next carousel-item-left"
20943 this.transition = true;
20944 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
20945 var lr = dir == 'next' ? 'left' : 'right';
20946 pan.el.addClass(dir); // or prev
20947 pan.el.addClass('carousel-item-' + dir); // or prev
20948 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20949 cur.el.addClass(lr); // or right
20950 pan.el.addClass(lr);
20951 cur.el.addClass('carousel-item-' +lr); // or right
20952 pan.el.addClass('carousel-item-' +lr);
20956 cur.el.on('transitionend', function() {
20957 Roo.log("trans end?");
20959 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20960 pan.setActive(true);
20962 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20963 cur.setActive(false);
20965 _this.transition = false;
20967 }, this, { single: true } );
20972 cur.setActive(false);
20973 pan.setActive(true);
20978 showPanelNext : function()
20980 var i = this.indexOfPanel(this.getActivePanel());
20982 if (i >= this.tabs.length - 1 && !this.autoslide) {
20986 if (i >= this.tabs.length - 1 && this.autoslide) {
20990 this.showPanel(this.tabs[i+1]);
20993 showPanelPrev : function()
20995 var i = this.indexOfPanel(this.getActivePanel());
20997 if (i < 1 && !this.autoslide) {
21001 if (i < 1 && this.autoslide) {
21002 i = this.tabs.length;
21005 this.showPanel(this.tabs[i-1]);
21009 addBullet: function()
21011 if(!this.bullets || Roo.isTouch){
21014 var ctr = this.el.select('.carousel-bullets',true).first();
21015 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21016 var bullet = ctr.createChild({
21017 cls : 'bullet bullet-' + i
21018 },ctr.dom.lastChild);
21023 bullet.on('click', (function(e, el, o, ii, t){
21025 e.preventDefault();
21027 this.showPanel(ii);
21029 if(this.autoslide && this.slideFn){
21030 clearInterval(this.slideFn);
21031 this.slideFn = window.setInterval(function() {
21032 _this.showPanelNext();
21036 }).createDelegate(this, [i, bullet], true));
21041 setActiveBullet : function(i)
21047 Roo.each(this.el.select('.bullet', true).elements, function(el){
21048 el.removeClass('selected');
21051 var bullet = this.el.select('.bullet-' + i, true).first();
21057 bullet.addClass('selected');
21068 Roo.apply(Roo.bootstrap.TabGroup, {
21072 * register a Navigation Group
21073 * @param {Roo.bootstrap.NavGroup} the navgroup to add
21075 register : function(navgrp)
21077 this.groups[navgrp.navId] = navgrp;
21081 * fetch a Navigation Group based on the navigation ID
21082 * if one does not exist , it will get created.
21083 * @param {string} the navgroup to add
21084 * @returns {Roo.bootstrap.NavGroup} the navgroup
21086 get: function(navId) {
21087 if (typeof(this.groups[navId]) == 'undefined') {
21088 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21090 return this.groups[navId] ;
21105 * @class Roo.bootstrap.TabPanel
21106 * @extends Roo.bootstrap.Component
21107 * Bootstrap TabPanel class
21108 * @cfg {Boolean} active panel active
21109 * @cfg {String} html panel content
21110 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21111 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21112 * @cfg {String} href click to link..
21113 * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21117 * Create a new TabPanel
21118 * @param {Object} config The config object
21121 Roo.bootstrap.TabPanel = function(config){
21122 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21126 * Fires when the active status changes
21127 * @param {Roo.bootstrap.TabPanel} this
21128 * @param {Boolean} state the new state
21133 * @event beforedeactivate
21134 * Fires before a tab is de-activated - can be used to do validation on a form.
21135 * @param {Roo.bootstrap.TabPanel} this
21136 * @return {Boolean} false if there is an error
21139 'beforedeactivate': true
21142 this.tabId = this.tabId || Roo.id();
21146 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
21153 touchSlide : false,
21154 getAutoCreate : function(){
21159 // item is needed for carousel - not sure if it has any effect otherwise
21160 cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21161 html: this.html || ''
21165 cfg.cls += ' active';
21169 cfg.tabId = this.tabId;
21177 initEvents: function()
21179 var p = this.parent();
21181 this.navId = this.navId || p.navId;
21183 if (typeof(this.navId) != 'undefined') {
21184 // not really needed.. but just in case.. parent should be a NavGroup.
21185 var tg = Roo.bootstrap.TabGroup.get(this.navId);
21189 var i = tg.tabs.length - 1;
21191 if(this.active && tg.bullets > 0 && i < tg.bullets){
21192 tg.setActiveBullet(i);
21196 this.el.on('click', this.onClick, this);
21198 if(Roo.isTouch && this.touchSlide){
21199 this.el.on("touchstart", this.onTouchStart, this);
21200 this.el.on("touchmove", this.onTouchMove, this);
21201 this.el.on("touchend", this.onTouchEnd, this);
21206 onRender : function(ct, position)
21208 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21211 setActive : function(state)
21213 Roo.log("panel - set active " + this.tabId + "=" + state);
21215 this.active = state;
21217 this.el.removeClass('active');
21219 } else if (!this.el.hasClass('active')) {
21220 this.el.addClass('active');
21223 this.fireEvent('changed', this, state);
21226 onClick : function(e)
21228 e.preventDefault();
21230 if(!this.href.length){
21234 window.location.href = this.href;
21243 onTouchStart : function(e)
21245 this.swiping = false;
21247 this.startX = e.browserEvent.touches[0].clientX;
21248 this.startY = e.browserEvent.touches[0].clientY;
21251 onTouchMove : function(e)
21253 this.swiping = true;
21255 this.endX = e.browserEvent.touches[0].clientX;
21256 this.endY = e.browserEvent.touches[0].clientY;
21259 onTouchEnd : function(e)
21266 var tabGroup = this.parent();
21268 if(this.endX > this.startX){ // swiping right
21269 tabGroup.showPanelPrev();
21273 if(this.startX > this.endX){ // swiping left
21274 tabGroup.showPanelNext();
21293 * @class Roo.bootstrap.DateField
21294 * @extends Roo.bootstrap.Input
21295 * Bootstrap DateField class
21296 * @cfg {Number} weekStart default 0
21297 * @cfg {String} viewMode default empty, (months|years)
21298 * @cfg {String} minViewMode default empty, (months|years)
21299 * @cfg {Number} startDate default -Infinity
21300 * @cfg {Number} endDate default Infinity
21301 * @cfg {Boolean} todayHighlight default false
21302 * @cfg {Boolean} todayBtn default false
21303 * @cfg {Boolean} calendarWeeks default false
21304 * @cfg {Object} daysOfWeekDisabled default empty
21305 * @cfg {Boolean} singleMode default false (true | false)
21307 * @cfg {Boolean} keyboardNavigation default true
21308 * @cfg {String} language default en
21311 * Create a new DateField
21312 * @param {Object} config The config object
21315 Roo.bootstrap.DateField = function(config){
21316 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21320 * Fires when this field show.
21321 * @param {Roo.bootstrap.DateField} this
21322 * @param {Mixed} date The date value
21327 * Fires when this field hide.
21328 * @param {Roo.bootstrap.DateField} this
21329 * @param {Mixed} date The date value
21334 * Fires when select a date.
21335 * @param {Roo.bootstrap.DateField} this
21336 * @param {Mixed} date The date value
21340 * @event beforeselect
21341 * Fires when before select a date.
21342 * @param {Roo.bootstrap.DateField} this
21343 * @param {Mixed} date The date value
21345 beforeselect : true
21349 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
21352 * @cfg {String} format
21353 * The default date format string which can be overriden for localization support. The format must be
21354 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21358 * @cfg {String} altFormats
21359 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21360 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21362 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21370 todayHighlight : false,
21376 keyboardNavigation: true,
21378 calendarWeeks: false,
21380 startDate: -Infinity,
21384 daysOfWeekDisabled: [],
21388 singleMode : false,
21390 UTCDate: function()
21392 return new Date(Date.UTC.apply(Date, arguments));
21395 UTCToday: function()
21397 var today = new Date();
21398 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21401 getDate: function() {
21402 var d = this.getUTCDate();
21403 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21406 getUTCDate: function() {
21410 setDate: function(d) {
21411 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21414 setUTCDate: function(d) {
21416 this.setValue(this.formatDate(this.date));
21419 onRender: function(ct, position)
21422 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21424 this.language = this.language || 'en';
21425 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21426 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21428 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21429 this.format = this.format || 'm/d/y';
21430 this.isInline = false;
21431 this.isInput = true;
21432 this.component = this.el.select('.add-on', true).first() || false;
21433 this.component = (this.component && this.component.length === 0) ? false : this.component;
21434 this.hasInput = this.component && this.inputEl().length;
21436 if (typeof(this.minViewMode === 'string')) {
21437 switch (this.minViewMode) {
21439 this.minViewMode = 1;
21442 this.minViewMode = 2;
21445 this.minViewMode = 0;
21450 if (typeof(this.viewMode === 'string')) {
21451 switch (this.viewMode) {
21464 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21466 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21468 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21470 this.picker().on('mousedown', this.onMousedown, this);
21471 this.picker().on('click', this.onClick, this);
21473 this.picker().addClass('datepicker-dropdown');
21475 this.startViewMode = this.viewMode;
21477 if(this.singleMode){
21478 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21479 v.setVisibilityMode(Roo.Element.DISPLAY);
21483 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21484 v.setStyle('width', '189px');
21488 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21489 if(!this.calendarWeeks){
21494 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21495 v.attr('colspan', function(i, val){
21496 return parseInt(val) + 1;
21501 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21503 this.setStartDate(this.startDate);
21504 this.setEndDate(this.endDate);
21506 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21513 if(this.isInline) {
21518 picker : function()
21520 return this.pickerEl;
21521 // return this.el.select('.datepicker', true).first();
21524 fillDow: function()
21526 var dowCnt = this.weekStart;
21535 if(this.calendarWeeks){
21543 while (dowCnt < this.weekStart + 7) {
21547 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21551 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21554 fillMonths: function()
21557 var months = this.picker().select('>.datepicker-months td', true).first();
21559 months.dom.innerHTML = '';
21565 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21568 months.createChild(month);
21575 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;
21577 if (this.date < this.startDate) {
21578 this.viewDate = new Date(this.startDate);
21579 } else if (this.date > this.endDate) {
21580 this.viewDate = new Date(this.endDate);
21582 this.viewDate = new Date(this.date);
21590 var d = new Date(this.viewDate),
21591 year = d.getUTCFullYear(),
21592 month = d.getUTCMonth(),
21593 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21594 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21595 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21596 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21597 currentDate = this.date && this.date.valueOf(),
21598 today = this.UTCToday();
21600 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21602 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21604 // this.picker.select('>tfoot th.today').
21605 // .text(dates[this.language].today)
21606 // .toggle(this.todayBtn !== false);
21608 this.updateNavArrows();
21611 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21613 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21615 prevMonth.setUTCDate(day);
21617 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21619 var nextMonth = new Date(prevMonth);
21621 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21623 nextMonth = nextMonth.valueOf();
21625 var fillMonths = false;
21627 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21629 while(prevMonth.valueOf() <= nextMonth) {
21632 if (prevMonth.getUTCDay() === this.weekStart) {
21634 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21642 if(this.calendarWeeks){
21643 // ISO 8601: First week contains first thursday.
21644 // ISO also states week starts on Monday, but we can be more abstract here.
21646 // Start of current week: based on weekstart/current date
21647 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21648 // Thursday of this week
21649 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21650 // First Thursday of year, year from thursday
21651 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21652 // Calendar week: ms between thursdays, div ms per day, div 7 days
21653 calWeek = (th - yth) / 864e5 / 7 + 1;
21655 fillMonths.cn.push({
21663 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21665 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21668 if (this.todayHighlight &&
21669 prevMonth.getUTCFullYear() == today.getFullYear() &&
21670 prevMonth.getUTCMonth() == today.getMonth() &&
21671 prevMonth.getUTCDate() == today.getDate()) {
21672 clsName += ' today';
21675 if (currentDate && prevMonth.valueOf() === currentDate) {
21676 clsName += ' active';
21679 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21680 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21681 clsName += ' disabled';
21684 fillMonths.cn.push({
21686 cls: 'day ' + clsName,
21687 html: prevMonth.getDate()
21690 prevMonth.setDate(prevMonth.getDate()+1);
21693 var currentYear = this.date && this.date.getUTCFullYear();
21694 var currentMonth = this.date && this.date.getUTCMonth();
21696 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21698 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21699 v.removeClass('active');
21701 if(currentYear === year && k === currentMonth){
21702 v.addClass('active');
21705 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21706 v.addClass('disabled');
21712 year = parseInt(year/10, 10) * 10;
21714 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21716 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21719 for (var i = -1; i < 11; i++) {
21720 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21722 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21730 showMode: function(dir)
21733 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21736 Roo.each(this.picker().select('>div',true).elements, function(v){
21737 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21740 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21745 if(this.isInline) {
21749 this.picker().removeClass(['bottom', 'top']);
21751 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21753 * place to the top of element!
21757 this.picker().addClass('top');
21758 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21763 this.picker().addClass('bottom');
21765 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21768 parseDate : function(value)
21770 if(!value || value instanceof Date){
21773 var v = Date.parseDate(value, this.format);
21774 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21775 v = Date.parseDate(value, 'Y-m-d');
21777 if(!v && this.altFormats){
21778 if(!this.altFormatsArray){
21779 this.altFormatsArray = this.altFormats.split("|");
21781 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21782 v = Date.parseDate(value, this.altFormatsArray[i]);
21788 formatDate : function(date, fmt)
21790 return (!date || !(date instanceof Date)) ?
21791 date : date.dateFormat(fmt || this.format);
21794 onFocus : function()
21796 Roo.bootstrap.DateField.superclass.onFocus.call(this);
21800 onBlur : function()
21802 Roo.bootstrap.DateField.superclass.onBlur.call(this);
21804 var d = this.inputEl().getValue();
21811 showPopup : function()
21813 this.picker().show();
21817 this.fireEvent('showpopup', this, this.date);
21820 hidePopup : function()
21822 if(this.isInline) {
21825 this.picker().hide();
21826 this.viewMode = this.startViewMode;
21829 this.fireEvent('hidepopup', this, this.date);
21833 onMousedown: function(e)
21835 e.stopPropagation();
21836 e.preventDefault();
21841 Roo.bootstrap.DateField.superclass.keyup.call(this);
21845 setValue: function(v)
21847 if(this.fireEvent('beforeselect', this, v) !== false){
21848 var d = new Date(this.parseDate(v) ).clearTime();
21850 if(isNaN(d.getTime())){
21851 this.date = this.viewDate = '';
21852 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21856 v = this.formatDate(d);
21858 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21860 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21864 this.fireEvent('select', this, this.date);
21868 getValue: function()
21870 return this.formatDate(this.date);
21873 fireKey: function(e)
21875 if (!this.picker().isVisible()){
21876 if (e.keyCode == 27) { // allow escape to hide and re-show picker
21882 var dateChanged = false,
21884 newDate, newViewDate;
21889 e.preventDefault();
21893 if (!this.keyboardNavigation) {
21896 dir = e.keyCode == 37 ? -1 : 1;
21899 newDate = this.moveYear(this.date, dir);
21900 newViewDate = this.moveYear(this.viewDate, dir);
21901 } else if (e.shiftKey){
21902 newDate = this.moveMonth(this.date, dir);
21903 newViewDate = this.moveMonth(this.viewDate, dir);
21905 newDate = new Date(this.date);
21906 newDate.setUTCDate(this.date.getUTCDate() + dir);
21907 newViewDate = new Date(this.viewDate);
21908 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21910 if (this.dateWithinRange(newDate)){
21911 this.date = newDate;
21912 this.viewDate = newViewDate;
21913 this.setValue(this.formatDate(this.date));
21915 e.preventDefault();
21916 dateChanged = true;
21921 if (!this.keyboardNavigation) {
21924 dir = e.keyCode == 38 ? -1 : 1;
21926 newDate = this.moveYear(this.date, dir);
21927 newViewDate = this.moveYear(this.viewDate, dir);
21928 } else if (e.shiftKey){
21929 newDate = this.moveMonth(this.date, dir);
21930 newViewDate = this.moveMonth(this.viewDate, dir);
21932 newDate = new Date(this.date);
21933 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21934 newViewDate = new Date(this.viewDate);
21935 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21937 if (this.dateWithinRange(newDate)){
21938 this.date = newDate;
21939 this.viewDate = newViewDate;
21940 this.setValue(this.formatDate(this.date));
21942 e.preventDefault();
21943 dateChanged = true;
21947 this.setValue(this.formatDate(this.date));
21949 e.preventDefault();
21952 this.setValue(this.formatDate(this.date));
21966 onClick: function(e)
21968 e.stopPropagation();
21969 e.preventDefault();
21971 var target = e.getTarget();
21973 if(target.nodeName.toLowerCase() === 'i'){
21974 target = Roo.get(target).dom.parentNode;
21977 var nodeName = target.nodeName;
21978 var className = target.className;
21979 var html = target.innerHTML;
21980 //Roo.log(nodeName);
21982 switch(nodeName.toLowerCase()) {
21984 switch(className) {
21990 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21991 switch(this.viewMode){
21993 this.viewDate = this.moveMonth(this.viewDate, dir);
21997 this.viewDate = this.moveYear(this.viewDate, dir);
22003 var date = new Date();
22004 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22006 this.setValue(this.formatDate(this.date));
22013 if (className.indexOf('disabled') < 0) {
22014 if (!this.viewDate) {
22015 this.viewDate = new Date();
22017 this.viewDate.setUTCDate(1);
22018 if (className.indexOf('month') > -1) {
22019 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22021 var year = parseInt(html, 10) || 0;
22022 this.viewDate.setUTCFullYear(year);
22026 if(this.singleMode){
22027 this.setValue(this.formatDate(this.viewDate));
22038 //Roo.log(className);
22039 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22040 var day = parseInt(html, 10) || 1;
22041 var year = (this.viewDate || new Date()).getUTCFullYear(),
22042 month = (this.viewDate || new Date()).getUTCMonth();
22044 if (className.indexOf('old') > -1) {
22051 } else if (className.indexOf('new') > -1) {
22059 //Roo.log([year,month,day]);
22060 this.date = this.UTCDate(year, month, day,0,0,0,0);
22061 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22063 //Roo.log(this.formatDate(this.date));
22064 this.setValue(this.formatDate(this.date));
22071 setStartDate: function(startDate)
22073 this.startDate = startDate || -Infinity;
22074 if (this.startDate !== -Infinity) {
22075 this.startDate = this.parseDate(this.startDate);
22078 this.updateNavArrows();
22081 setEndDate: function(endDate)
22083 this.endDate = endDate || Infinity;
22084 if (this.endDate !== Infinity) {
22085 this.endDate = this.parseDate(this.endDate);
22088 this.updateNavArrows();
22091 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22093 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22094 if (typeof(this.daysOfWeekDisabled) !== 'object') {
22095 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22097 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22098 return parseInt(d, 10);
22101 this.updateNavArrows();
22104 updateNavArrows: function()
22106 if(this.singleMode){
22110 var d = new Date(this.viewDate),
22111 year = d.getUTCFullYear(),
22112 month = d.getUTCMonth();
22114 Roo.each(this.picker().select('.prev', true).elements, function(v){
22116 switch (this.viewMode) {
22119 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22125 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22132 Roo.each(this.picker().select('.next', true).elements, function(v){
22134 switch (this.viewMode) {
22137 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22143 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22151 moveMonth: function(date, dir)
22156 var new_date = new Date(date.valueOf()),
22157 day = new_date.getUTCDate(),
22158 month = new_date.getUTCMonth(),
22159 mag = Math.abs(dir),
22161 dir = dir > 0 ? 1 : -1;
22164 // If going back one month, make sure month is not current month
22165 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22167 return new_date.getUTCMonth() == month;
22169 // If going forward one month, make sure month is as expected
22170 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22172 return new_date.getUTCMonth() != new_month;
22174 new_month = month + dir;
22175 new_date.setUTCMonth(new_month);
22176 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22177 if (new_month < 0 || new_month > 11) {
22178 new_month = (new_month + 12) % 12;
22181 // For magnitudes >1, move one month at a time...
22182 for (var i=0; i<mag; i++) {
22183 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22184 new_date = this.moveMonth(new_date, dir);
22186 // ...then reset the day, keeping it in the new month
22187 new_month = new_date.getUTCMonth();
22188 new_date.setUTCDate(day);
22190 return new_month != new_date.getUTCMonth();
22193 // Common date-resetting loop -- if date is beyond end of month, make it
22196 new_date.setUTCDate(--day);
22197 new_date.setUTCMonth(new_month);
22202 moveYear: function(date, dir)
22204 return this.moveMonth(date, dir*12);
22207 dateWithinRange: function(date)
22209 return date >= this.startDate && date <= this.endDate;
22215 this.picker().remove();
22218 validateValue : function(value)
22220 if(this.getVisibilityEl().hasClass('hidden')){
22224 if(value.length < 1) {
22225 if(this.allowBlank){
22231 if(value.length < this.minLength){
22234 if(value.length > this.maxLength){
22238 var vt = Roo.form.VTypes;
22239 if(!vt[this.vtype](value, this)){
22243 if(typeof this.validator == "function"){
22244 var msg = this.validator(value);
22250 if(this.regex && !this.regex.test(value)){
22254 if(typeof(this.parseDate(value)) == 'undefined'){
22258 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22262 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22272 this.date = this.viewDate = '';
22274 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22279 Roo.apply(Roo.bootstrap.DateField, {
22290 html: '<i class="fa fa-arrow-left"/>'
22300 html: '<i class="fa fa-arrow-right"/>'
22342 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22343 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22344 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22345 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22346 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22359 navFnc: 'FullYear',
22364 navFnc: 'FullYear',
22369 Roo.apply(Roo.bootstrap.DateField, {
22373 cls: 'datepicker dropdown-menu roo-dynamic shadow',
22377 cls: 'datepicker-days',
22381 cls: 'table-condensed',
22383 Roo.bootstrap.DateField.head,
22387 Roo.bootstrap.DateField.footer
22394 cls: 'datepicker-months',
22398 cls: 'table-condensed',
22400 Roo.bootstrap.DateField.head,
22401 Roo.bootstrap.DateField.content,
22402 Roo.bootstrap.DateField.footer
22409 cls: 'datepicker-years',
22413 cls: 'table-condensed',
22415 Roo.bootstrap.DateField.head,
22416 Roo.bootstrap.DateField.content,
22417 Roo.bootstrap.DateField.footer
22436 * @class Roo.bootstrap.TimeField
22437 * @extends Roo.bootstrap.Input
22438 * Bootstrap DateField class
22442 * Create a new TimeField
22443 * @param {Object} config The config object
22446 Roo.bootstrap.TimeField = function(config){
22447 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22451 * Fires when this field show.
22452 * @param {Roo.bootstrap.DateField} thisthis
22453 * @param {Mixed} date The date value
22458 * Fires when this field hide.
22459 * @param {Roo.bootstrap.DateField} this
22460 * @param {Mixed} date The date value
22465 * Fires when select a date.
22466 * @param {Roo.bootstrap.DateField} this
22467 * @param {Mixed} date The date value
22473 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
22476 * @cfg {String} format
22477 * The default time format string which can be overriden for localization support. The format must be
22478 * valid according to {@link Date#parseDate} (defaults to 'H:i').
22482 getAutoCreate : function()
22484 this.after = '<i class="fa far fa-clock"></i>';
22485 return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22489 onRender: function(ct, position)
22492 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22494 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22496 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22498 this.pop = this.picker().select('>.datepicker-time',true).first();
22499 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22501 this.picker().on('mousedown', this.onMousedown, this);
22502 this.picker().on('click', this.onClick, this);
22504 this.picker().addClass('datepicker-dropdown');
22509 this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22510 this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22511 this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22512 this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22513 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22514 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22518 fireKey: function(e){
22519 if (!this.picker().isVisible()){
22520 if (e.keyCode == 27) { // allow escape to hide and re-show picker
22526 e.preventDefault();
22534 this.onTogglePeriod();
22537 this.onIncrementMinutes();
22540 this.onDecrementMinutes();
22549 onClick: function(e) {
22550 e.stopPropagation();
22551 e.preventDefault();
22554 picker : function()
22556 return this.pickerEl;
22559 fillTime: function()
22561 var time = this.pop.select('tbody', true).first();
22563 time.dom.innerHTML = '';
22578 cls: 'hours-up fa fas fa-chevron-up'
22598 cls: 'minutes-up fa fas fa-chevron-up'
22619 cls: 'timepicker-hour',
22634 cls: 'timepicker-minute',
22649 cls: 'btn btn-primary period',
22671 cls: 'hours-down fa fas fa-chevron-down'
22691 cls: 'minutes-down fa fas fa-chevron-down'
22709 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22716 var hours = this.time.getHours();
22717 var minutes = this.time.getMinutes();
22730 hours = hours - 12;
22734 hours = '0' + hours;
22738 minutes = '0' + minutes;
22741 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22742 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22743 this.pop.select('button', true).first().dom.innerHTML = period;
22749 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22751 var cls = ['bottom'];
22753 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22760 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22764 //this.picker().setXY(20000,20000);
22765 this.picker().addClass(cls.join('-'));
22769 Roo.each(cls, function(c){
22774 _this.picker().alignTo(_this.inputEl(), "tr-br", [0, 10], false);
22775 //_this.picker().setTop(_this.inputEl().getHeight());
22779 _this.picker().alignTo(_this.inputEl(), "br-tr", [0, 10], false);
22781 //_this.picker().setTop(0 - _this.picker().getHeight());
22786 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22790 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22798 onFocus : function()
22800 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22804 onBlur : function()
22806 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22812 this.picker().show();
22817 this.fireEvent('show', this, this.date);
22822 this.picker().hide();
22825 this.fireEvent('hide', this, this.date);
22828 setTime : function()
22831 this.setValue(this.time.format(this.format));
22833 this.fireEvent('select', this, this.date);
22838 onMousedown: function(e){
22839 e.stopPropagation();
22840 e.preventDefault();
22843 onIncrementHours: function()
22845 Roo.log('onIncrementHours');
22846 this.time = this.time.add(Date.HOUR, 1);
22851 onDecrementHours: function()
22853 Roo.log('onDecrementHours');
22854 this.time = this.time.add(Date.HOUR, -1);
22858 onIncrementMinutes: function()
22860 Roo.log('onIncrementMinutes');
22861 this.time = this.time.add(Date.MINUTE, 1);
22865 onDecrementMinutes: function()
22867 Roo.log('onDecrementMinutes');
22868 this.time = this.time.add(Date.MINUTE, -1);
22872 onTogglePeriod: function()
22874 Roo.log('onTogglePeriod');
22875 this.time = this.time.add(Date.HOUR, 12);
22883 Roo.apply(Roo.bootstrap.TimeField, {
22887 cls: 'datepicker dropdown-menu',
22891 cls: 'datepicker-time',
22895 cls: 'table-condensed',
22924 cls: 'btn btn-info ok',
22952 * @class Roo.bootstrap.MonthField
22953 * @extends Roo.bootstrap.Input
22954 * Bootstrap MonthField class
22956 * @cfg {String} language default en
22959 * Create a new MonthField
22960 * @param {Object} config The config object
22963 Roo.bootstrap.MonthField = function(config){
22964 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22969 * Fires when this field show.
22970 * @param {Roo.bootstrap.MonthField} this
22971 * @param {Mixed} date The date value
22976 * Fires when this field hide.
22977 * @param {Roo.bootstrap.MonthField} this
22978 * @param {Mixed} date The date value
22983 * Fires when select a date.
22984 * @param {Roo.bootstrap.MonthField} this
22985 * @param {String} oldvalue The old value
22986 * @param {String} newvalue The new value
22992 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
22994 onRender: function(ct, position)
22997 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22999 this.language = this.language || 'en';
23000 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23001 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23003 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23004 this.isInline = false;
23005 this.isInput = true;
23006 this.component = this.el.select('.add-on', true).first() || false;
23007 this.component = (this.component && this.component.length === 0) ? false : this.component;
23008 this.hasInput = this.component && this.inputEL().length;
23010 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23012 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23014 this.picker().on('mousedown', this.onMousedown, this);
23015 this.picker().on('click', this.onClick, this);
23017 this.picker().addClass('datepicker-dropdown');
23019 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23020 v.setStyle('width', '189px');
23027 if(this.isInline) {
23033 setValue: function(v, suppressEvent)
23035 var o = this.getValue();
23037 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23041 if(suppressEvent !== true){
23042 this.fireEvent('select', this, o, v);
23047 getValue: function()
23052 onClick: function(e)
23054 e.stopPropagation();
23055 e.preventDefault();
23057 var target = e.getTarget();
23059 if(target.nodeName.toLowerCase() === 'i'){
23060 target = Roo.get(target).dom.parentNode;
23063 var nodeName = target.nodeName;
23064 var className = target.className;
23065 var html = target.innerHTML;
23067 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23071 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23073 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23079 picker : function()
23081 return this.pickerEl;
23084 fillMonths: function()
23087 var months = this.picker().select('>.datepicker-months td', true).first();
23089 months.dom.innerHTML = '';
23095 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23098 months.createChild(month);
23107 if(typeof(this.vIndex) == 'undefined' && this.value.length){
23108 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23111 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23112 e.removeClass('active');
23114 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23115 e.addClass('active');
23122 if(this.isInline) {
23126 this.picker().removeClass(['bottom', 'top']);
23128 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23130 * place to the top of element!
23134 this.picker().addClass('top');
23135 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23140 this.picker().addClass('bottom');
23142 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23145 onFocus : function()
23147 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23151 onBlur : function()
23153 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23155 var d = this.inputEl().getValue();
23164 this.picker().show();
23165 this.picker().select('>.datepicker-months', true).first().show();
23169 this.fireEvent('show', this, this.date);
23174 if(this.isInline) {
23177 this.picker().hide();
23178 this.fireEvent('hide', this, this.date);
23182 onMousedown: function(e)
23184 e.stopPropagation();
23185 e.preventDefault();
23190 Roo.bootstrap.MonthField.superclass.keyup.call(this);
23194 fireKey: function(e)
23196 if (!this.picker().isVisible()){
23197 if (e.keyCode == 27) {// allow escape to hide and re-show picker
23208 e.preventDefault();
23212 dir = e.keyCode == 37 ? -1 : 1;
23214 this.vIndex = this.vIndex + dir;
23216 if(this.vIndex < 0){
23220 if(this.vIndex > 11){
23224 if(isNaN(this.vIndex)){
23228 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23234 dir = e.keyCode == 38 ? -1 : 1;
23236 this.vIndex = this.vIndex + dir * 4;
23238 if(this.vIndex < 0){
23242 if(this.vIndex > 11){
23246 if(isNaN(this.vIndex)){
23250 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23255 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23256 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23260 e.preventDefault();
23263 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23264 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23280 this.picker().remove();
23285 Roo.apply(Roo.bootstrap.MonthField, {
23304 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23305 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23310 Roo.apply(Roo.bootstrap.MonthField, {
23314 cls: 'datepicker dropdown-menu roo-dynamic',
23318 cls: 'datepicker-months',
23322 cls: 'table-condensed',
23324 Roo.bootstrap.DateField.content
23344 * @class Roo.bootstrap.CheckBox
23345 * @extends Roo.bootstrap.Input
23346 * Bootstrap CheckBox class
23348 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23349 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23350 * @cfg {String} boxLabel The text that appears beside the checkbox
23351 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23352 * @cfg {Boolean} checked initnal the element
23353 * @cfg {Boolean} inline inline the element (default false)
23354 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23355 * @cfg {String} tooltip label tooltip
23358 * Create a new CheckBox
23359 * @param {Object} config The config object
23362 Roo.bootstrap.CheckBox = function(config){
23363 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23368 * Fires when the element is checked or unchecked.
23369 * @param {Roo.bootstrap.CheckBox} this This input
23370 * @param {Boolean} checked The new checked value
23375 * Fires when the element is click.
23376 * @param {Roo.bootstrap.CheckBox} this This input
23383 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
23385 inputType: 'checkbox',
23394 // checkbox success does not make any sense really..
23399 getAutoCreate : function()
23401 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23407 cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23410 cfg.cls += ' ' + this.inputType + '-inline form-check-inline';
23416 type : this.inputType,
23417 value : this.inputValue,
23418 cls : 'roo-' + this.inputType, //'form-box',
23419 placeholder : this.placeholder || ''
23423 if(this.inputType != 'radio'){
23427 cls : 'roo-hidden-value',
23428 value : this.checked ? this.inputValue : this.valueOff
23433 if (this.weight) { // Validity check?
23434 cfg.cls += " " + this.inputType + "-" + this.weight;
23437 if (this.disabled) {
23438 input.disabled=true;
23442 input.checked = this.checked;
23447 input.name = this.name;
23449 if(this.inputType != 'radio'){
23450 hidden.name = this.name;
23451 input.name = '_hidden_' + this.name;
23456 input.cls += ' input-' + this.size;
23461 ['xs','sm','md','lg'].map(function(size){
23462 if (settings[size]) {
23463 cfg.cls += ' col-' + size + '-' + settings[size];
23467 var inputblock = input;
23469 if (this.before || this.after) {
23472 cls : 'input-group',
23477 inputblock.cn.push({
23479 cls : 'input-group-addon',
23484 inputblock.cn.push(input);
23486 if(this.inputType != 'radio'){
23487 inputblock.cn.push(hidden);
23491 inputblock.cn.push({
23493 cls : 'input-group-addon',
23499 var boxLabelCfg = false;
23505 //'for': id, // box label is handled by onclick - so no for...
23507 html: this.boxLabel
23510 boxLabelCfg.tooltip = this.tooltip;
23516 if (align ==='left' && this.fieldLabel.length) {
23517 // Roo.log("left and has label");
23522 cls : 'control-label',
23523 html : this.fieldLabel
23534 cfg.cn[1].cn.push(boxLabelCfg);
23537 if(this.labelWidth > 12){
23538 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23541 if(this.labelWidth < 13 && this.labelmd == 0){
23542 this.labelmd = this.labelWidth;
23545 if(this.labellg > 0){
23546 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23547 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23550 if(this.labelmd > 0){
23551 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23552 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23555 if(this.labelsm > 0){
23556 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23557 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23560 if(this.labelxs > 0){
23561 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23562 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23565 } else if ( this.fieldLabel.length) {
23566 // Roo.log(" label");
23570 tag: this.boxLabel ? 'span' : 'label',
23572 cls: 'control-label box-input-label',
23573 //cls : 'input-group-addon',
23574 html : this.fieldLabel
23581 cfg.cn.push(boxLabelCfg);
23586 // Roo.log(" no label && no align");
23587 cfg.cn = [ inputblock ] ;
23589 cfg.cn.push(boxLabelCfg);
23597 if(this.inputType != 'radio'){
23598 cfg.cn.push(hidden);
23606 * return the real input element.
23608 inputEl: function ()
23610 return this.el.select('input.roo-' + this.inputType,true).first();
23612 hiddenEl: function ()
23614 return this.el.select('input.roo-hidden-value',true).first();
23617 labelEl: function()
23619 return this.el.select('label.control-label',true).first();
23621 /* depricated... */
23625 return this.labelEl();
23628 boxLabelEl: function()
23630 return this.el.select('label.box-label',true).first();
23633 initEvents : function()
23635 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23637 this.inputEl().on('click', this.onClick, this);
23639 if (this.boxLabel) {
23640 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
23643 this.startValue = this.getValue();
23646 Roo.bootstrap.CheckBox.register(this);
23650 onClick : function(e)
23652 if(this.fireEvent('click', this, e) !== false){
23653 this.setChecked(!this.checked);
23658 setChecked : function(state,suppressEvent)
23660 this.startValue = this.getValue();
23662 if(this.inputType == 'radio'){
23664 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23665 e.dom.checked = false;
23668 this.inputEl().dom.checked = true;
23670 this.inputEl().dom.value = this.inputValue;
23672 if(suppressEvent !== true){
23673 this.fireEvent('check', this, true);
23681 this.checked = state;
23683 this.inputEl().dom.checked = state;
23686 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23688 if(suppressEvent !== true){
23689 this.fireEvent('check', this, state);
23695 getValue : function()
23697 if(this.inputType == 'radio'){
23698 return this.getGroupValue();
23701 return this.hiddenEl().dom.value;
23705 getGroupValue : function()
23707 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23711 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23714 setValue : function(v,suppressEvent)
23716 if(this.inputType == 'radio'){
23717 this.setGroupValue(v, suppressEvent);
23721 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23726 setGroupValue : function(v, suppressEvent)
23728 this.startValue = this.getValue();
23730 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23731 e.dom.checked = false;
23733 if(e.dom.value == v){
23734 e.dom.checked = true;
23738 if(suppressEvent !== true){
23739 this.fireEvent('check', this, true);
23747 validate : function()
23749 if(this.getVisibilityEl().hasClass('hidden')){
23755 (this.inputType == 'radio' && this.validateRadio()) ||
23756 (this.inputType == 'checkbox' && this.validateCheckbox())
23762 this.markInvalid();
23766 validateRadio : function()
23768 if(this.getVisibilityEl().hasClass('hidden')){
23772 if(this.allowBlank){
23778 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23779 if(!e.dom.checked){
23791 validateCheckbox : function()
23794 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23795 //return (this.getValue() == this.inputValue) ? true : false;
23798 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23806 for(var i in group){
23807 if(group[i].el.isVisible(true)){
23815 for(var i in group){
23820 r = (group[i].getValue() == group[i].inputValue) ? true : false;
23827 * Mark this field as valid
23829 markValid : function()
23833 this.fireEvent('valid', this);
23835 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23838 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23845 if(this.inputType == 'radio'){
23846 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23847 var fg = e.findParent('.form-group', false, true);
23848 if (Roo.bootstrap.version == 3) {
23849 fg.removeClass([_this.invalidClass, _this.validClass]);
23850 fg.addClass(_this.validClass);
23852 fg.removeClass(['is-valid', 'is-invalid']);
23853 fg.addClass('is-valid');
23861 var fg = this.el.findParent('.form-group', false, true);
23862 if (Roo.bootstrap.version == 3) {
23863 fg.removeClass([this.invalidClass, this.validClass]);
23864 fg.addClass(this.validClass);
23866 fg.removeClass(['is-valid', 'is-invalid']);
23867 fg.addClass('is-valid');
23872 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23878 for(var i in group){
23879 var fg = group[i].el.findParent('.form-group', false, true);
23880 if (Roo.bootstrap.version == 3) {
23881 fg.removeClass([this.invalidClass, this.validClass]);
23882 fg.addClass(this.validClass);
23884 fg.removeClass(['is-valid', 'is-invalid']);
23885 fg.addClass('is-valid');
23891 * Mark this field as invalid
23892 * @param {String} msg The validation message
23894 markInvalid : function(msg)
23896 if(this.allowBlank){
23902 this.fireEvent('invalid', this, msg);
23904 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23907 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23911 label.markInvalid();
23914 if(this.inputType == 'radio'){
23916 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23917 var fg = e.findParent('.form-group', false, true);
23918 if (Roo.bootstrap.version == 3) {
23919 fg.removeClass([_this.invalidClass, _this.validClass]);
23920 fg.addClass(_this.invalidClass);
23922 fg.removeClass(['is-invalid', 'is-valid']);
23923 fg.addClass('is-invalid');
23931 var fg = this.el.findParent('.form-group', false, true);
23932 if (Roo.bootstrap.version == 3) {
23933 fg.removeClass([_this.invalidClass, _this.validClass]);
23934 fg.addClass(_this.invalidClass);
23936 fg.removeClass(['is-invalid', 'is-valid']);
23937 fg.addClass('is-invalid');
23942 var group = Roo.bootstrap.CheckBox.get(this.groupId);
23948 for(var i in group){
23949 var fg = group[i].el.findParent('.form-group', false, true);
23950 if (Roo.bootstrap.version == 3) {
23951 fg.removeClass([_this.invalidClass, _this.validClass]);
23952 fg.addClass(_this.invalidClass);
23954 fg.removeClass(['is-invalid', 'is-valid']);
23955 fg.addClass('is-invalid');
23961 clearInvalid : function()
23963 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23965 // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23967 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23969 if (label && label.iconEl) {
23970 label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23971 label.iconEl.removeClass(['is-invalid', 'is-valid']);
23975 disable : function()
23977 if(this.inputType != 'radio'){
23978 Roo.bootstrap.CheckBox.superclass.disable.call(this);
23985 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23986 _this.getActionEl().addClass(this.disabledClass);
23987 e.dom.disabled = true;
23991 this.disabled = true;
23992 this.fireEvent("disable", this);
23996 enable : function()
23998 if(this.inputType != 'radio'){
23999 Roo.bootstrap.CheckBox.superclass.enable.call(this);
24006 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24007 _this.getActionEl().removeClass(this.disabledClass);
24008 e.dom.disabled = false;
24012 this.disabled = false;
24013 this.fireEvent("enable", this);
24017 setBoxLabel : function(v)
24022 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24028 Roo.apply(Roo.bootstrap.CheckBox, {
24033 * register a CheckBox Group
24034 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24036 register : function(checkbox)
24038 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24039 this.groups[checkbox.groupId] = {};
24042 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24046 this.groups[checkbox.groupId][checkbox.name] = checkbox;
24050 * fetch a CheckBox Group based on the group ID
24051 * @param {string} the group ID
24052 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24054 get: function(groupId) {
24055 if (typeof(this.groups[groupId]) == 'undefined') {
24059 return this.groups[groupId] ;
24072 * @class Roo.bootstrap.Radio
24073 * @extends Roo.bootstrap.Component
24074 * Bootstrap Radio class
24075 * @cfg {String} boxLabel - the label associated
24076 * @cfg {String} value - the value of radio
24079 * Create a new Radio
24080 * @param {Object} config The config object
24082 Roo.bootstrap.Radio = function(config){
24083 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24087 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24093 getAutoCreate : function()
24097 cls : 'form-group radio',
24102 html : this.boxLabel
24110 initEvents : function()
24112 this.parent().register(this);
24114 this.el.on('click', this.onClick, this);
24118 onClick : function(e)
24120 if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24121 this.setChecked(true);
24125 setChecked : function(state, suppressEvent)
24127 this.parent().setValue(this.value, suppressEvent);
24131 setBoxLabel : function(v)
24136 this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24151 * @class Roo.bootstrap.SecurePass
24152 * @extends Roo.bootstrap.Input
24153 * Bootstrap SecurePass class
24157 * Create a new SecurePass
24158 * @param {Object} config The config object
24161 Roo.bootstrap.SecurePass = function (config) {
24162 // these go here, so the translation tool can replace them..
24164 PwdEmpty: "Please type a password, and then retype it to confirm.",
24165 PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24166 PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24167 PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24168 IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24169 FNInPwd: "Your password can't contain your first name. Please type a different password.",
24170 LNInPwd: "Your password can't contain your last name. Please type a different password.",
24171 TooWeak: "Your password is Too Weak."
24173 this.meterLabel = "Password strength:";
24174 this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24175 this.meterClass = [
24176 "roo-password-meter-tooweak",
24177 "roo-password-meter-weak",
24178 "roo-password-meter-medium",
24179 "roo-password-meter-strong",
24180 "roo-password-meter-grey"
24185 Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24188 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24190 * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24192 * PwdEmpty: "Please type a password, and then retype it to confirm.",
24193 * PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24194 * PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24195 * PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24196 * IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24197 * FNInPwd: "Your password can't contain your first name. Please type a different password.",
24198 * LNInPwd: "Your password can't contain your last name. Please type a different password."
24208 * @cfg {String/Object} Label for the strength meter (defaults to
24209 * 'Password strength:')
24214 * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24215 * ['Weak', 'Medium', 'Strong'])
24218 pwdStrengths: false,
24231 initEvents: function ()
24233 Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24235 if (this.el.is('input[type=password]') && Roo.isSafari) {
24236 this.el.on('keydown', this.SafariOnKeyDown, this);
24239 this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24242 onRender: function (ct, position)
24244 Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24245 this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24246 this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24248 this.trigger.createChild({
24253 cls: 'roo-password-meter-grey col-xs-12',
24256 //width: this.meterWidth + 'px'
24260 cls: 'roo-password-meter-text'
24266 if (this.hideTrigger) {
24267 this.trigger.setDisplayed(false);
24269 this.setSize(this.width || '', this.height || '');
24272 onDestroy: function ()
24274 if (this.trigger) {
24275 this.trigger.removeAllListeners();
24276 this.trigger.remove();
24279 this.wrap.remove();
24281 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24284 checkStrength: function ()
24286 var pwd = this.inputEl().getValue();
24287 if (pwd == this._lastPwd) {
24292 if (this.ClientSideStrongPassword(pwd)) {
24294 } else if (this.ClientSideMediumPassword(pwd)) {
24296 } else if (this.ClientSideWeakPassword(pwd)) {
24302 Roo.log('strength1: ' + strength);
24304 //var pm = this.trigger.child('div/div/div').dom;
24305 var pm = this.trigger.child('div/div');
24306 pm.removeClass(this.meterClass);
24307 pm.addClass(this.meterClass[strength]);
24310 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24312 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24314 this._lastPwd = pwd;
24318 Roo.bootstrap.SecurePass.superclass.reset.call(this);
24320 this._lastPwd = '';
24322 var pm = this.trigger.child('div/div');
24323 pm.removeClass(this.meterClass);
24324 pm.addClass('roo-password-meter-grey');
24327 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24330 this.inputEl().dom.type='password';
24333 validateValue: function (value)
24335 if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24338 if (value.length == 0) {
24339 if (this.allowBlank) {
24340 this.clearInvalid();
24344 this.markInvalid(this.errors.PwdEmpty);
24345 this.errorMsg = this.errors.PwdEmpty;
24353 if (!value.match(/[\x21-\x7e]+/)) {
24354 this.markInvalid(this.errors.PwdBadChar);
24355 this.errorMsg = this.errors.PwdBadChar;
24358 if (value.length < 6) {
24359 this.markInvalid(this.errors.PwdShort);
24360 this.errorMsg = this.errors.PwdShort;
24363 if (value.length > 16) {
24364 this.markInvalid(this.errors.PwdLong);
24365 this.errorMsg = this.errors.PwdLong;
24369 if (this.ClientSideStrongPassword(value)) {
24371 } else if (this.ClientSideMediumPassword(value)) {
24373 } else if (this.ClientSideWeakPassword(value)) {
24380 if (strength < 2) {
24381 //this.markInvalid(this.errors.TooWeak);
24382 this.errorMsg = this.errors.TooWeak;
24387 console.log('strength2: ' + strength);
24389 //var pm = this.trigger.child('div/div/div').dom;
24391 var pm = this.trigger.child('div/div');
24392 pm.removeClass(this.meterClass);
24393 pm.addClass(this.meterClass[strength]);
24395 var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;
24397 pt.innerHTML = this.meterLabel + ' ' + this.pwdStrengths[strength];
24399 this.errorMsg = '';
24403 CharacterSetChecks: function (type)
24406 this.fResult = false;
24409 isctype: function (character, type)
24412 case this.kCapitalLetter:
24413 if (character >= 'A' && character <= 'Z') {
24418 case this.kSmallLetter:
24419 if (character >= 'a' && character <= 'z') {
24425 if (character >= '0' && character <= '9') {
24430 case this.kPunctuation:
24431 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24442 IsLongEnough: function (pwd, size)
24444 return !(pwd == null || isNaN(size) || pwd.length < size);
24447 SpansEnoughCharacterSets: function (word, nb)
24449 if (!this.IsLongEnough(word, nb))
24454 var characterSetChecks = new Array(
24455 new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24456 new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24459 for (var index = 0; index < word.length; ++index) {
24460 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24461 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24462 characterSetChecks[nCharSet].fResult = true;
24469 for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24470 if (characterSetChecks[nCharSet].fResult) {
24475 if (nCharSets < nb) {
24481 ClientSideStrongPassword: function (pwd)
24483 return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24486 ClientSideMediumPassword: function (pwd)
24488 return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24491 ClientSideWeakPassword: function (pwd)
24493 return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24496 })//<script type="text/javascript">
24499 * Based Ext JS Library 1.1.1
24500 * Copyright(c) 2006-2007, Ext JS, LLC.
24506 * @class Roo.HtmlEditorCore
24507 * @extends Roo.Component
24508 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24510 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24513 Roo.HtmlEditorCore = function(config){
24516 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24521 * @event initialize
24522 * Fires when the editor is fully initialized (including the iframe)
24523 * @param {Roo.HtmlEditorCore} this
24528 * Fires when the editor is first receives the focus. Any insertion must wait
24529 * until after this event.
24530 * @param {Roo.HtmlEditorCore} this
24534 * @event beforesync
24535 * Fires before the textarea is updated with content from the editor iframe. Return false
24536 * to cancel the sync.
24537 * @param {Roo.HtmlEditorCore} this
24538 * @param {String} html
24542 * @event beforepush
24543 * Fires before the iframe editor is updated with content from the textarea. Return false
24544 * to cancel the push.
24545 * @param {Roo.HtmlEditorCore} this
24546 * @param {String} html
24551 * Fires when the textarea is updated with content from the editor iframe.
24552 * @param {Roo.HtmlEditorCore} this
24553 * @param {String} html
24558 * Fires when the iframe editor is updated with content from the textarea.
24559 * @param {Roo.HtmlEditorCore} this
24560 * @param {String} html
24565 * @event editorevent
24566 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24567 * @param {Roo.HtmlEditorCore} this
24573 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24575 // defaults : white / black...
24576 this.applyBlacklists();
24583 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
24587 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
24593 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
24598 * @cfg {Number} height (in pixels)
24602 * @cfg {Number} width (in pixels)
24607 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24610 stylesheets: false,
24615 // private properties
24616 validationEvent : false,
24618 initialized : false,
24620 sourceEditMode : false,
24621 onFocus : Roo.emptyFn,
24623 hideMode:'offsets',
24627 // blacklist + whitelisted elements..
24634 * Protected method that will not generally be called directly. It
24635 * is called when the editor initializes the iframe with HTML contents. Override this method if you
24636 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24638 getDocMarkup : function(){
24642 // inherit styels from page...??
24643 if (this.stylesheets === false) {
24645 Roo.get(document.head).select('style').each(function(node) {
24646 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24649 Roo.get(document.head).select('link').each(function(node) {
24650 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24653 } else if (!this.stylesheets.length) {
24655 st = '<style type="text/css">' +
24656 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24659 for (var i in this.stylesheets) {
24660 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24665 st += '<style type="text/css">' +
24666 'IMG { cursor: pointer } ' +
24669 var cls = 'roo-htmleditor-body';
24671 if(this.bodyCls.length){
24672 cls += ' ' + this.bodyCls;
24675 return '<html><head>' + st +
24676 //<style type="text/css">' +
24677 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24679 ' </head><body contenteditable="true" data-enable-grammerly="true" class="' + cls + '"></body></html>';
24683 onRender : function(ct, position)
24686 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24687 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24690 this.el.dom.style.border = '0 none';
24691 this.el.dom.setAttribute('tabIndex', -1);
24692 this.el.addClass('x-hidden hide');
24696 if(Roo.isIE){ // fix IE 1px bogus margin
24697 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24701 this.frameId = Roo.id();
24705 var iframe = this.owner.wrap.createChild({
24707 cls: 'form-control', // bootstrap..
24709 name: this.frameId,
24710 frameBorder : 'no',
24711 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
24716 this.iframe = iframe.dom;
24718 this.assignDocWin();
24720 this.doc.designMode = 'on';
24723 this.doc.write(this.getDocMarkup());
24727 var task = { // must defer to wait for browser to be ready
24729 //console.log("run task?" + this.doc.readyState);
24730 this.assignDocWin();
24731 if(this.doc.body || this.doc.readyState == 'complete'){
24733 this.doc.designMode="on";
24737 Roo.TaskMgr.stop(task);
24738 this.initEditor.defer(10, this);
24745 Roo.TaskMgr.start(task);
24750 onResize : function(w, h)
24752 Roo.log('resize: ' +w + ',' + h );
24753 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24757 if(typeof w == 'number'){
24759 this.iframe.style.width = w + 'px';
24761 if(typeof h == 'number'){
24763 this.iframe.style.height = h + 'px';
24765 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24772 * Toggles the editor between standard and source edit mode.
24773 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24775 toggleSourceEdit : function(sourceEditMode){
24777 this.sourceEditMode = sourceEditMode === true;
24779 if(this.sourceEditMode){
24781 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
24784 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24785 //this.iframe.className = '';
24788 //this.setSize(this.owner.wrap.getSize());
24789 //this.fireEvent('editmodechange', this, this.sourceEditMode);
24796 * Protected method that will not generally be called directly. If you need/want
24797 * custom HTML cleanup, this is the method you should override.
24798 * @param {String} html The HTML to be cleaned
24799 * return {String} The cleaned HTML
24801 cleanHtml : function(html){
24802 html = String(html);
24803 if(html.length > 5){
24804 if(Roo.isSafari){ // strip safari nonsense
24805 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24808 if(html == ' '){
24815 * HTML Editor -> Textarea
24816 * Protected method that will not generally be called directly. Syncs the contents
24817 * of the editor iframe with the textarea.
24819 syncValue : function(){
24820 if(this.initialized){
24821 var bd = (this.doc.body || this.doc.documentElement);
24822 //this.cleanUpPaste(); -- this is done else where and causes havoc..
24823 var html = bd.innerHTML;
24825 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24826 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24828 html = '<div style="'+m[0]+'">' + html + '</div>';
24831 html = this.cleanHtml(html);
24832 // fix up the special chars.. normaly like back quotes in word...
24833 // however we do not want to do this with chinese..
24834 html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24836 var cc = match.charCodeAt();
24838 // Get the character value, handling surrogate pairs
24839 if (match.length == 2) {
24840 // It's a surrogate pair, calculate the Unicode code point
24841 var high = match.charCodeAt(0) - 0xD800;
24842 var low = match.charCodeAt(1) - 0xDC00;
24843 cc = (high * 0x400) + low + 0x10000;
24845 (cc >= 0x4E00 && cc < 0xA000 ) ||
24846 (cc >= 0x3400 && cc < 0x4E00 ) ||
24847 (cc >= 0xf900 && cc < 0xfb00 )
24852 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24853 return "&#" + cc + ";";
24860 if(this.owner.fireEvent('beforesync', this, html) !== false){
24861 this.el.dom.value = html;
24862 this.owner.fireEvent('sync', this, html);
24868 * Protected method that will not generally be called directly. Pushes the value of the textarea
24869 * into the iframe editor.
24871 pushValue : function(){
24872 if(this.initialized){
24873 var v = this.el.dom.value.trim();
24875 // if(v.length < 1){
24879 if(this.owner.fireEvent('beforepush', this, v) !== false){
24880 var d = (this.doc.body || this.doc.documentElement);
24882 this.cleanUpPaste();
24883 this.el.dom.value = d.innerHTML;
24884 this.owner.fireEvent('push', this, v);
24890 deferFocus : function(){
24891 this.focus.defer(10, this);
24895 focus : function(){
24896 if(this.win && !this.sourceEditMode){
24903 assignDocWin: function()
24905 var iframe = this.iframe;
24908 this.doc = iframe.contentWindow.document;
24909 this.win = iframe.contentWindow;
24911 // if (!Roo.get(this.frameId)) {
24914 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24915 // this.win = Roo.get(this.frameId).dom.contentWindow;
24917 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24921 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24922 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24927 initEditor : function(){
24928 //console.log("INIT EDITOR");
24929 this.assignDocWin();
24933 this.doc.designMode="on";
24935 this.doc.write(this.getDocMarkup());
24938 var dbody = (this.doc.body || this.doc.documentElement);
24939 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24940 // this copies styles from the containing element into thsi one..
24941 // not sure why we need all of this..
24942 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24944 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24945 //ss['background-attachment'] = 'fixed'; // w3c
24946 dbody.bgProperties = 'fixed'; // ie
24947 //Roo.DomHelper.applyStyles(dbody, ss);
24948 Roo.EventManager.on(this.doc, {
24949 //'mousedown': this.onEditorEvent,
24950 'mouseup': this.onEditorEvent,
24951 'dblclick': this.onEditorEvent,
24952 'click': this.onEditorEvent,
24953 'keyup': this.onEditorEvent,
24958 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24960 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24961 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24963 this.initialized = true;
24965 this.owner.fireEvent('initialize', this);
24970 onDestroy : function(){
24976 //for (var i =0; i < this.toolbars.length;i++) {
24977 // // fixme - ask toolbars for heights?
24978 // this.toolbars[i].onDestroy();
24981 //this.wrap.dom.innerHTML = '';
24982 //this.wrap.remove();
24987 onFirstFocus : function(){
24989 this.assignDocWin();
24992 this.activated = true;
24995 if(Roo.isGecko){ // prevent silly gecko errors
24997 var s = this.win.getSelection();
24998 if(!s.focusNode || s.focusNode.nodeType != 3){
24999 var r = s.getRangeAt(0);
25000 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25005 this.execCmd('useCSS', true);
25006 this.execCmd('styleWithCSS', false);
25009 this.owner.fireEvent('activate', this);
25013 adjustFont: function(btn){
25014 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25015 //if(Roo.isSafari){ // safari
25018 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25019 if(Roo.isSafari){ // safari
25020 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25021 v = (v < 10) ? 10 : v;
25022 v = (v > 48) ? 48 : v;
25023 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25028 v = Math.max(1, v+adjust);
25030 this.execCmd('FontSize', v );
25033 onEditorEvent : function(e)
25035 this.owner.fireEvent('editorevent', this, e);
25036 // this.updateToolbar();
25037 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25040 insertTag : function(tg)
25042 // could be a bit smarter... -> wrap the current selected tRoo..
25043 if (tg.toLowerCase() == 'span' ||
25044 tg.toLowerCase() == 'code' ||
25045 tg.toLowerCase() == 'sup' ||
25046 tg.toLowerCase() == 'sub'
25049 range = this.createRange(this.getSelection());
25050 var wrappingNode = this.doc.createElement(tg.toLowerCase());
25051 wrappingNode.appendChild(range.extractContents());
25052 range.insertNode(wrappingNode);
25059 this.execCmd("formatblock", tg);
25063 insertText : function(txt)
25067 var range = this.createRange();
25068 range.deleteContents();
25069 //alert(Sender.getAttribute('label'));
25071 range.insertNode(this.doc.createTextNode(txt));
25077 * Executes a Midas editor command on the editor document and performs necessary focus and
25078 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25079 * @param {String} cmd The Midas command
25080 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25082 relayCmd : function(cmd, value){
25084 this.execCmd(cmd, value);
25085 this.owner.fireEvent('editorevent', this);
25086 //this.updateToolbar();
25087 this.owner.deferFocus();
25091 * Executes a Midas editor command directly on the editor document.
25092 * For visual commands, you should use {@link #relayCmd} instead.
25093 * <b>This should only be called after the editor is initialized.</b>
25094 * @param {String} cmd The Midas command
25095 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25097 execCmd : function(cmd, value){
25098 this.doc.execCommand(cmd, false, value === undefined ? null : value);
25105 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25107 * @param {String} text | dom node..
25109 insertAtCursor : function(text)
25112 if(!this.activated){
25118 var r = this.doc.selection.createRange();
25129 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25133 // from jquery ui (MIT licenced)
25135 var win = this.win;
25137 if (win.getSelection && win.getSelection().getRangeAt) {
25138 range = win.getSelection().getRangeAt(0);
25139 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25140 range.insertNode(node);
25141 } else if (win.document.selection && win.document.selection.createRange) {
25142 // no firefox support
25143 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25144 win.document.selection.createRange().pasteHTML(txt);
25146 // no firefox support
25147 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25148 this.execCmd('InsertHTML', txt);
25157 mozKeyPress : function(e){
25159 var c = e.getCharCode(), cmd;
25162 c = String.fromCharCode(c).toLowerCase();
25176 this.cleanUpPaste.defer(100, this);
25184 e.preventDefault();
25192 fixKeys : function(){ // load time branching for fastest keydown performance
25194 return function(e){
25195 var k = e.getKey(), r;
25198 r = this.doc.selection.createRange();
25201 r.pasteHTML('    ');
25208 r = this.doc.selection.createRange();
25210 var target = r.parentElement();
25211 if(!target || target.tagName.toLowerCase() != 'li'){
25213 r.pasteHTML('<br />');
25219 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25220 this.cleanUpPaste.defer(100, this);
25226 }else if(Roo.isOpera){
25227 return function(e){
25228 var k = e.getKey();
25232 this.execCmd('InsertHTML','    ');
25235 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25236 this.cleanUpPaste.defer(100, this);
25241 }else if(Roo.isSafari){
25242 return function(e){
25243 var k = e.getKey();
25247 this.execCmd('InsertText','\t');
25251 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25252 this.cleanUpPaste.defer(100, this);
25260 getAllAncestors: function()
25262 var p = this.getSelectedNode();
25265 a.push(p); // push blank onto stack..
25266 p = this.getParentElement();
25270 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25274 a.push(this.doc.body);
25278 lastSelNode : false,
25281 getSelection : function()
25283 this.assignDocWin();
25284 return Roo.isIE ? this.doc.selection : this.win.getSelection();
25287 getSelectedNode: function()
25289 // this may only work on Gecko!!!
25291 // should we cache this!!!!
25296 var range = this.createRange(this.getSelection()).cloneRange();
25299 var parent = range.parentElement();
25301 var testRange = range.duplicate();
25302 testRange.moveToElementText(parent);
25303 if (testRange.inRange(range)) {
25306 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25309 parent = parent.parentElement;
25314 // is ancestor a text element.
25315 var ac = range.commonAncestorContainer;
25316 if (ac.nodeType == 3) {
25317 ac = ac.parentNode;
25320 var ar = ac.childNodes;
25323 var other_nodes = [];
25324 var has_other_nodes = false;
25325 for (var i=0;i<ar.length;i++) {
25326 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
25329 // fullly contained node.
25331 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25336 // probably selected..
25337 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25338 other_nodes.push(ar[i]);
25342 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
25347 has_other_nodes = true;
25349 if (!nodes.length && other_nodes.length) {
25350 nodes= other_nodes;
25352 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25358 createRange: function(sel)
25360 // this has strange effects when using with
25361 // top toolbar - not sure if it's a great idea.
25362 //this.editor.contentWindow.focus();
25363 if (typeof sel != "undefined") {
25365 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25367 return this.doc.createRange();
25370 return this.doc.createRange();
25373 getParentElement: function()
25376 this.assignDocWin();
25377 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25379 var range = this.createRange(sel);
25382 var p = range.commonAncestorContainer;
25383 while (p.nodeType == 3) { // text node
25394 * Range intersection.. the hard stuff...
25398 * [ -- selected range --- ]
25402 * if end is before start or hits it. fail.
25403 * if start is after end or hits it fail.
25405 * if either hits (but other is outside. - then it's not
25411 // @see http://www.thismuchiknow.co.uk/?p=64.
25412 rangeIntersectsNode : function(range, node)
25414 var nodeRange = node.ownerDocument.createRange();
25416 nodeRange.selectNode(node);
25418 nodeRange.selectNodeContents(node);
25421 var rangeStartRange = range.cloneRange();
25422 rangeStartRange.collapse(true);
25424 var rangeEndRange = range.cloneRange();
25425 rangeEndRange.collapse(false);
25427 var nodeStartRange = nodeRange.cloneRange();
25428 nodeStartRange.collapse(true);
25430 var nodeEndRange = nodeRange.cloneRange();
25431 nodeEndRange.collapse(false);
25433 return rangeStartRange.compareBoundaryPoints(
25434 Range.START_TO_START, nodeEndRange) == -1 &&
25435 rangeEndRange.compareBoundaryPoints(
25436 Range.START_TO_START, nodeStartRange) == 1;
25440 rangeCompareNode : function(range, node)
25442 var nodeRange = node.ownerDocument.createRange();
25444 nodeRange.selectNode(node);
25446 nodeRange.selectNodeContents(node);
25450 range.collapse(true);
25452 nodeRange.collapse(true);
25454 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25455 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
25457 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25459 var nodeIsBefore = ss == 1;
25460 var nodeIsAfter = ee == -1;
25462 if (nodeIsBefore && nodeIsAfter) {
25465 if (!nodeIsBefore && nodeIsAfter) {
25466 return 1; //right trailed.
25469 if (nodeIsBefore && !nodeIsAfter) {
25470 return 2; // left trailed.
25476 // private? - in a new class?
25477 cleanUpPaste : function()
25479 // cleans up the whole document..
25480 Roo.log('cleanuppaste');
25482 this.cleanUpChildren(this.doc.body);
25483 var clean = this.cleanWordChars(this.doc.body.innerHTML);
25484 if (clean != this.doc.body.innerHTML) {
25485 this.doc.body.innerHTML = clean;
25490 cleanWordChars : function(input) {// change the chars to hex code
25491 var he = Roo.HtmlEditorCore;
25493 var output = input;
25494 Roo.each(he.swapCodes, function(sw) {
25495 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25497 output = output.replace(swapper, sw[1]);
25504 cleanUpChildren : function (n)
25506 if (!n.childNodes.length) {
25509 for (var i = n.childNodes.length-1; i > -1 ; i--) {
25510 this.cleanUpChild(n.childNodes[i]);
25517 cleanUpChild : function (node)
25520 //console.log(node);
25521 if (node.nodeName == "#text") {
25522 // clean up silly Windows -- stuff?
25525 if (node.nodeName == "#comment") {
25526 node.parentNode.removeChild(node);
25527 // clean up silly Windows -- stuff?
25530 var lcname = node.tagName.toLowerCase();
25531 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25532 // whitelist of tags..
25534 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25536 node.parentNode.removeChild(node);
25541 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25543 // spans with no attributes - just remove them..
25544 if ((!node.attributes || !node.attributes.length) && lcname == 'span') {
25545 remove_keep_children = true;
25548 // remove <a name=....> as rendering on yahoo mailer is borked with this.
25549 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25551 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25552 // remove_keep_children = true;
25555 if (remove_keep_children) {
25556 this.cleanUpChildren(node);
25557 // inserts everything just before this node...
25558 while (node.childNodes.length) {
25559 var cn = node.childNodes[0];
25560 node.removeChild(cn);
25561 node.parentNode.insertBefore(cn, node);
25563 node.parentNode.removeChild(node);
25567 if (!node.attributes || !node.attributes.length) {
25572 this.cleanUpChildren(node);
25576 function cleanAttr(n,v)
25579 if (v.match(/^\./) || v.match(/^\//)) {
25582 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25585 if (v.match(/^#/)) {
25588 if (v.match(/^\{/)) { // allow template editing.
25591 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25592 node.removeAttribute(n);
25596 var cwhite = this.cwhite;
25597 var cblack = this.cblack;
25599 function cleanStyle(n,v)
25601 if (v.match(/expression/)) { //XSS?? should we even bother..
25602 node.removeAttribute(n);
25606 var parts = v.split(/;/);
25609 Roo.each(parts, function(p) {
25610 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25614 var l = p.split(':').shift().replace(/\s+/g,'');
25615 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25617 if ( cwhite.length && cblack.indexOf(l) > -1) {
25618 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25619 //node.removeAttribute(n);
25623 // only allow 'c whitelisted system attributes'
25624 if ( cwhite.length && cwhite.indexOf(l) < 0) {
25625 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25626 //node.removeAttribute(n);
25636 if (clean.length) {
25637 node.setAttribute(n, clean.join(';'));
25639 node.removeAttribute(n);
25645 for (var i = node.attributes.length-1; i > -1 ; i--) {
25646 var a = node.attributes[i];
25649 if (a.name.toLowerCase().substr(0,2)=='on') {
25650 node.removeAttribute(a.name);
25653 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25654 node.removeAttribute(a.name);
25657 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25658 cleanAttr(a.name,a.value); // fixme..
25661 if (a.name == 'style') {
25662 cleanStyle(a.name,a.value);
25665 /// clean up MS crap..
25666 // tecnically this should be a list of valid class'es..
25669 if (a.name == 'class') {
25670 if (a.value.match(/^Mso/)) {
25671 node.removeAttribute('class');
25674 if (a.value.match(/^body$/)) {
25675 node.removeAttribute('class');
25686 this.cleanUpChildren(node);
25692 * Clean up MS wordisms...
25694 cleanWord : function(node)
25697 this.cleanWord(this.doc.body);
25702 node.nodeName == 'SPAN' &&
25703 !node.hasAttributes() &&
25704 node.childNodes.length == 1 &&
25705 node.firstChild.nodeName == "#text"
25707 var textNode = node.firstChild;
25708 node.removeChild(textNode);
25709 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25710 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25712 node.parentNode.insertBefore(textNode, node);
25713 if (node.getAttribute('lang') != 'zh-CN') { // do not space pad on chinese characters..
25714 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25716 node.parentNode.removeChild(node);
25719 if (node.nodeName == "#text") {
25720 // clean up silly Windows -- stuff?
25723 if (node.nodeName == "#comment") {
25724 node.parentNode.removeChild(node);
25725 // clean up silly Windows -- stuff?
25729 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25730 node.parentNode.removeChild(node);
25733 //Roo.log(node.tagName);
25734 // remove - but keep children..
25735 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25736 //Roo.log('-- removed');
25737 while (node.childNodes.length) {
25738 var cn = node.childNodes[0];
25739 node.removeChild(cn);
25740 node.parentNode.insertBefore(cn, node);
25741 // move node to parent - and clean it..
25742 this.cleanWord(cn);
25744 node.parentNode.removeChild(node);
25745 /// no need to iterate chidlren = it's got none..
25746 //this.iterateChildren(node, this.cleanWord);
25750 if (node.className.length) {
25752 var cn = node.className.split(/\W+/);
25754 Roo.each(cn, function(cls) {
25755 if (cls.match(/Mso[a-zA-Z]+/)) {
25760 node.className = cna.length ? cna.join(' ') : '';
25762 node.removeAttribute("class");
25766 if (node.hasAttribute("lang")) {
25767 node.removeAttribute("lang");
25770 if (node.hasAttribute("style")) {
25772 var styles = node.getAttribute("style").split(";");
25774 Roo.each(styles, function(s) {
25775 if (!s.match(/:/)) {
25778 var kv = s.split(":");
25779 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25782 // what ever is left... we allow.
25785 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25786 if (!nstyle.length) {
25787 node.removeAttribute('style');
25790 this.iterateChildren(node, this.cleanWord);
25796 * iterateChildren of a Node, calling fn each time, using this as the scole..
25797 * @param {DomNode} node node to iterate children of.
25798 * @param {Function} fn method of this class to call on each item.
25800 iterateChildren : function(node, fn)
25802 if (!node.childNodes.length) {
25805 for (var i = node.childNodes.length-1; i > -1 ; i--) {
25806 fn.call(this, node.childNodes[i])
25812 * cleanTableWidths.
25814 * Quite often pasting from word etc.. results in tables with column and widths.
25815 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25818 cleanTableWidths : function(node)
25823 this.cleanTableWidths(this.doc.body);
25828 if (node.nodeName == "#text" || node.nodeName == "#comment") {
25831 Roo.log(node.tagName);
25832 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25833 this.iterateChildren(node, this.cleanTableWidths);
25836 if (node.hasAttribute('width')) {
25837 node.removeAttribute('width');
25841 if (node.hasAttribute("style")) {
25844 var styles = node.getAttribute("style").split(";");
25846 Roo.each(styles, function(s) {
25847 if (!s.match(/:/)) {
25850 var kv = s.split(":");
25851 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25854 // what ever is left... we allow.
25857 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25858 if (!nstyle.length) {
25859 node.removeAttribute('style');
25863 this.iterateChildren(node, this.cleanTableWidths);
25871 domToHTML : function(currentElement, depth, nopadtext) {
25873 depth = depth || 0;
25874 nopadtext = nopadtext || false;
25876 if (!currentElement) {
25877 return this.domToHTML(this.doc.body);
25880 //Roo.log(currentElement);
25882 var allText = false;
25883 var nodeName = currentElement.nodeName;
25884 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25886 if (nodeName == '#text') {
25888 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25893 if (nodeName != 'BODY') {
25896 // Prints the node tagName, such as <A>, <IMG>, etc
25899 for(i = 0; i < currentElement.attributes.length;i++) {
25901 var aname = currentElement.attributes.item(i).name;
25902 if (!currentElement.attributes.item(i).value.length) {
25905 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25908 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25917 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25920 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25925 // Traverse the tree
25927 var currentElementChild = currentElement.childNodes.item(i);
25928 var allText = true;
25929 var innerHTML = '';
25931 while (currentElementChild) {
25932 // Formatting code (indent the tree so it looks nice on the screen)
25933 var nopad = nopadtext;
25934 if (lastnode == 'SPAN') {
25938 if (currentElementChild.nodeName == '#text') {
25939 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25940 toadd = nopadtext ? toadd : toadd.trim();
25941 if (!nopad && toadd.length > 80) {
25942 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
25944 innerHTML += toadd;
25947 currentElementChild = currentElement.childNodes.item(i);
25953 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
25955 // Recursively traverse the tree structure of the child node
25956 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
25957 lastnode = currentElementChild.nodeName;
25959 currentElementChild=currentElement.childNodes.item(i);
25965 // The remaining code is mostly for formatting the tree
25966 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
25971 ret+= "</"+tagName+">";
25977 applyBlacklists : function()
25979 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
25980 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
25984 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25985 if (b.indexOf(tag) > -1) {
25988 this.white.push(tag);
25992 Roo.each(w, function(tag) {
25993 if (b.indexOf(tag) > -1) {
25996 if (this.white.indexOf(tag) > -1) {
25999 this.white.push(tag);
26004 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26005 if (w.indexOf(tag) > -1) {
26008 this.black.push(tag);
26012 Roo.each(b, function(tag) {
26013 if (w.indexOf(tag) > -1) {
26016 if (this.black.indexOf(tag) > -1) {
26019 this.black.push(tag);
26024 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
26025 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
26029 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26030 if (b.indexOf(tag) > -1) {
26033 this.cwhite.push(tag);
26037 Roo.each(w, function(tag) {
26038 if (b.indexOf(tag) > -1) {
26041 if (this.cwhite.indexOf(tag) > -1) {
26044 this.cwhite.push(tag);
26049 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26050 if (w.indexOf(tag) > -1) {
26053 this.cblack.push(tag);
26057 Roo.each(b, function(tag) {
26058 if (w.indexOf(tag) > -1) {
26061 if (this.cblack.indexOf(tag) > -1) {
26064 this.cblack.push(tag);
26069 setStylesheets : function(stylesheets)
26071 if(typeof(stylesheets) == 'string'){
26072 Roo.get(this.iframe.contentDocument.head).createChild({
26074 rel : 'stylesheet',
26083 Roo.each(stylesheets, function(s) {
26088 Roo.get(_this.iframe.contentDocument.head).createChild({
26090 rel : 'stylesheet',
26099 removeStylesheets : function()
26103 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26108 setStyle : function(style)
26110 Roo.get(this.iframe.contentDocument.head).createChild({
26119 // hide stuff that is not compatible
26133 * @event specialkey
26137 * @cfg {String} fieldClass @hide
26140 * @cfg {String} focusClass @hide
26143 * @cfg {String} autoCreate @hide
26146 * @cfg {String} inputType @hide
26149 * @cfg {String} invalidClass @hide
26152 * @cfg {String} invalidText @hide
26155 * @cfg {String} msgFx @hide
26158 * @cfg {String} validateOnBlur @hide
26162 Roo.HtmlEditorCore.white = [
26163 'area', 'br', 'img', 'input', 'hr', 'wbr',
26165 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
26166 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
26167 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
26168 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
26169 'table', 'ul', 'xmp',
26171 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
26174 'dir', 'menu', 'ol', 'ul', 'dl',
26180 Roo.HtmlEditorCore.black = [
26181 // 'embed', 'object', // enable - backend responsiblity to clean thiese
26183 'base', 'basefont', 'bgsound', 'blink', 'body',
26184 'frame', 'frameset', 'head', 'html', 'ilayer',
26185 'iframe', 'layer', 'link', 'meta', 'object',
26186 'script', 'style' ,'title', 'xml' // clean later..
26188 Roo.HtmlEditorCore.clean = [
26189 'script', 'style', 'title', 'xml'
26191 Roo.HtmlEditorCore.remove = [
26196 Roo.HtmlEditorCore.ablack = [
26200 Roo.HtmlEditorCore.aclean = [
26201 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
26205 Roo.HtmlEditorCore.pwhite= [
26206 'http', 'https', 'mailto'
26209 // white listed style attributes.
26210 Roo.HtmlEditorCore.cwhite= [
26211 // 'text-align', /// default is to allow most things..
26217 // black listed style attributes.
26218 Roo.HtmlEditorCore.cblack= [
26219 // 'font-size' -- this can be set by the project
26223 Roo.HtmlEditorCore.swapCodes =[
26224 [ 8211, "–" ],
26225 [ 8212, "—" ],
26242 * @class Roo.bootstrap.HtmlEditor
26243 * @extends Roo.bootstrap.TextArea
26244 * Bootstrap HtmlEditor class
26247 * Create a new HtmlEditor
26248 * @param {Object} config The config object
26251 Roo.bootstrap.HtmlEditor = function(config){
26252 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26253 if (!this.toolbars) {
26254 this.toolbars = [];
26257 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26260 * @event initialize
26261 * Fires when the editor is fully initialized (including the iframe)
26262 * @param {HtmlEditor} this
26267 * Fires when the editor is first receives the focus. Any insertion must wait
26268 * until after this event.
26269 * @param {HtmlEditor} this
26273 * @event beforesync
26274 * Fires before the textarea is updated with content from the editor iframe. Return false
26275 * to cancel the sync.
26276 * @param {HtmlEditor} this
26277 * @param {String} html
26281 * @event beforepush
26282 * Fires before the iframe editor is updated with content from the textarea. Return false
26283 * to cancel the push.
26284 * @param {HtmlEditor} this
26285 * @param {String} html
26290 * Fires when the textarea is updated with content from the editor iframe.
26291 * @param {HtmlEditor} this
26292 * @param {String} html
26297 * Fires when the iframe editor is updated with content from the textarea.
26298 * @param {HtmlEditor} this
26299 * @param {String} html
26303 * @event editmodechange
26304 * Fires when the editor switches edit modes
26305 * @param {HtmlEditor} this
26306 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26308 editmodechange: true,
26310 * @event editorevent
26311 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26312 * @param {HtmlEditor} this
26316 * @event firstfocus
26317 * Fires when on first focus - needed by toolbars..
26318 * @param {HtmlEditor} this
26323 * Auto save the htmlEditor value as a file into Events
26324 * @param {HtmlEditor} this
26328 * @event savedpreview
26329 * preview the saved version of htmlEditor
26330 * @param {HtmlEditor} this
26337 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
26341 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26346 * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26351 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
26356 * @cfg {Number} height (in pixels)
26360 * @cfg {Number} width (in pixels)
26365 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26368 stylesheets: false,
26373 // private properties
26374 validationEvent : false,
26376 initialized : false,
26379 onFocus : Roo.emptyFn,
26381 hideMode:'offsets',
26383 tbContainer : false,
26387 toolbarContainer :function() {
26388 return this.wrap.select('.x-html-editor-tb',true).first();
26392 * Protected method that will not generally be called directly. It
26393 * is called when the editor creates its toolbar. Override this method if you need to
26394 * add custom toolbar buttons.
26395 * @param {HtmlEditor} editor
26397 createToolbar : function(){
26398 Roo.log('renewing');
26399 Roo.log("create toolbars");
26401 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26402 this.toolbars[0].render(this.toolbarContainer());
26406 // if (!editor.toolbars || !editor.toolbars.length) {
26407 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26410 // for (var i =0 ; i < editor.toolbars.length;i++) {
26411 // editor.toolbars[i] = Roo.factory(
26412 // typeof(editor.toolbars[i]) == 'string' ?
26413 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
26414 // Roo.bootstrap.HtmlEditor);
26415 // editor.toolbars[i].init(editor);
26421 onRender : function(ct, position)
26423 // Roo.log("Call onRender: " + this.xtype);
26425 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26427 this.wrap = this.inputEl().wrap({
26428 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26431 this.editorcore.onRender(ct, position);
26433 if (this.resizable) {
26434 this.resizeEl = new Roo.Resizable(this.wrap, {
26438 minHeight : this.height,
26439 height: this.height,
26440 handles : this.resizable,
26443 resize : function(r, w, h) {
26444 _t.onResize(w,h); // -something
26450 this.createToolbar(this);
26453 if(!this.width && this.resizable){
26454 this.setSize(this.wrap.getSize());
26456 if (this.resizeEl) {
26457 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26458 // should trigger onReize..
26464 onResize : function(w, h)
26466 Roo.log('resize: ' +w + ',' + h );
26467 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26471 if(this.inputEl() ){
26472 if(typeof w == 'number'){
26473 var aw = w - this.wrap.getFrameWidth('lr');
26474 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26477 if(typeof h == 'number'){
26478 var tbh = -11; // fixme it needs to tool bar size!
26479 for (var i =0; i < this.toolbars.length;i++) {
26480 // fixme - ask toolbars for heights?
26481 tbh += this.toolbars[i].el.getHeight();
26482 //if (this.toolbars[i].footer) {
26483 // tbh += this.toolbars[i].footer.el.getHeight();
26491 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26492 ah -= 5; // knock a few pixes off for look..
26493 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26497 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26498 this.editorcore.onResize(ew,eh);
26503 * Toggles the editor between standard and source edit mode.
26504 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26506 toggleSourceEdit : function(sourceEditMode)
26508 this.editorcore.toggleSourceEdit(sourceEditMode);
26510 if(this.editorcore.sourceEditMode){
26511 Roo.log('editor - showing textarea');
26514 // Roo.log(this.syncValue());
26516 this.inputEl().removeClass(['hide', 'x-hidden']);
26517 this.inputEl().dom.removeAttribute('tabIndex');
26518 this.inputEl().focus();
26520 Roo.log('editor - hiding textarea');
26522 // Roo.log(this.pushValue());
26525 this.inputEl().addClass(['hide', 'x-hidden']);
26526 this.inputEl().dom.setAttribute('tabIndex', -1);
26527 //this.deferFocus();
26530 if(this.resizable){
26531 this.setSize(this.wrap.getSize());
26534 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26537 // private (for BoxComponent)
26538 adjustSize : Roo.BoxComponent.prototype.adjustSize,
26540 // private (for BoxComponent)
26541 getResizeEl : function(){
26545 // private (for BoxComponent)
26546 getPositionEl : function(){
26551 initEvents : function(){
26552 this.originalValue = this.getValue();
26556 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26559 // markInvalid : Roo.emptyFn,
26561 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26564 // clearInvalid : Roo.emptyFn,
26566 setValue : function(v){
26567 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26568 this.editorcore.pushValue();
26573 deferFocus : function(){
26574 this.focus.defer(10, this);
26578 focus : function(){
26579 this.editorcore.focus();
26585 onDestroy : function(){
26591 for (var i =0; i < this.toolbars.length;i++) {
26592 // fixme - ask toolbars for heights?
26593 this.toolbars[i].onDestroy();
26596 this.wrap.dom.innerHTML = '';
26597 this.wrap.remove();
26602 onFirstFocus : function(){
26603 //Roo.log("onFirstFocus");
26604 this.editorcore.onFirstFocus();
26605 for (var i =0; i < this.toolbars.length;i++) {
26606 this.toolbars[i].onFirstFocus();
26612 syncValue : function()
26614 this.editorcore.syncValue();
26617 pushValue : function()
26619 this.editorcore.pushValue();
26623 // hide stuff that is not compatible
26637 * @event specialkey
26641 * @cfg {String} fieldClass @hide
26644 * @cfg {String} focusClass @hide
26647 * @cfg {String} autoCreate @hide
26650 * @cfg {String} inputType @hide
26654 * @cfg {String} invalidText @hide
26657 * @cfg {String} msgFx @hide
26660 * @cfg {String} validateOnBlur @hide
26669 Roo.namespace('Roo.bootstrap.htmleditor');
26671 * @class Roo.bootstrap.HtmlEditorToolbar1
26677 new Roo.bootstrap.HtmlEditor({
26680 new Roo.bootstrap.HtmlEditorToolbar1({
26681 disable : { fonts: 1 , format: 1, ..., ... , ...],
26687 * @cfg {Object} disable List of elements to disable..
26688 * @cfg {Array} btns List of additional buttons.
26692 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26695 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26698 Roo.apply(this, config);
26700 // default disabled, based on 'good practice'..
26701 this.disable = this.disable || {};
26702 Roo.applyIf(this.disable, {
26705 specialElements : true
26707 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26709 this.editor = config.editor;
26710 this.editorcore = config.editor.editorcore;
26712 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26714 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26715 // dont call parent... till later.
26717 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
26722 editorcore : false,
26727 "h1","h2","h3","h4","h5","h6",
26729 "abbr", "acronym", "address", "cite", "samp", "var",
26733 onRender : function(ct, position)
26735 // Roo.log("Call onRender: " + this.xtype);
26737 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26739 this.el.dom.style.marginBottom = '0';
26741 var editorcore = this.editorcore;
26742 var editor= this.editor;
26745 var btn = function(id,cmd , toggle, handler, html){
26747 var event = toggle ? 'toggle' : 'click';
26752 xns: Roo.bootstrap,
26756 enableToggle:toggle !== false,
26758 pressed : toggle ? false : null,
26761 a.listeners[toggle ? 'toggle' : 'click'] = function() {
26762 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
26768 // var cb_box = function...
26773 xns: Roo.bootstrap,
26778 xns: Roo.bootstrap,
26782 Roo.each(this.formats, function(f) {
26783 style.menu.items.push({
26785 xns: Roo.bootstrap,
26786 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26791 editorcore.insertTag(this.tagname);
26798 children.push(style);
26800 btn('bold',false,true);
26801 btn('italic',false,true);
26802 btn('align-left', 'justifyleft',true);
26803 btn('align-center', 'justifycenter',true);
26804 btn('align-right' , 'justifyright',true);
26805 btn('link', false, false, function(btn) {
26806 //Roo.log("create link?");
26807 var url = prompt(this.createLinkText, this.defaultLinkValue);
26808 if(url && url != 'http:/'+'/'){
26809 this.editorcore.relayCmd('createlink', url);
26812 btn('list','insertunorderedlist',true);
26813 btn('pencil', false,true, function(btn){
26815 this.toggleSourceEdit(btn.pressed);
26818 if (this.editor.btns.length > 0) {
26819 for (var i = 0; i<this.editor.btns.length; i++) {
26820 children.push(this.editor.btns[i]);
26828 xns: Roo.bootstrap,
26833 xns: Roo.bootstrap,
26838 cog.menu.items.push({
26840 xns: Roo.bootstrap,
26841 html : Clean styles,
26846 editorcore.insertTag(this.tagname);
26855 this.xtype = 'NavSimplebar';
26857 for(var i=0;i< children.length;i++) {
26859 this.buttons.add(this.addxtypeChild(children[i]));
26863 editor.on('editorevent', this.updateToolbar, this);
26865 onBtnClick : function(id)
26867 this.editorcore.relayCmd(id);
26868 this.editorcore.focus();
26872 * Protected method that will not generally be called directly. It triggers
26873 * a toolbar update by reading the markup state of the current selection in the editor.
26875 updateToolbar: function(){
26877 if(!this.editorcore.activated){
26878 this.editor.onFirstFocus(); // is this neeed?
26882 var btns = this.buttons;
26883 var doc = this.editorcore.doc;
26884 btns.get('bold').setActive(doc.queryCommandState('bold'));
26885 btns.get('italic').setActive(doc.queryCommandState('italic'));
26886 //btns.get('underline').setActive(doc.queryCommandState('underline'));
26888 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26889 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26890 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26892 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26893 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26896 var ans = this.editorcore.getAllAncestors();
26897 if (this.formatCombo) {
26900 var store = this.formatCombo.store;
26901 this.formatCombo.setValue("");
26902 for (var i =0; i < ans.length;i++) {
26903 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26905 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26913 // hides menus... - so this cant be on a menu...
26914 Roo.bootstrap.MenuMgr.hideAll();
26916 Roo.bootstrap.MenuMgr.hideAll();
26917 //this.editorsyncValue();
26919 onFirstFocus: function() {
26920 this.buttons.each(function(item){
26924 toggleSourceEdit : function(sourceEditMode){
26927 if(sourceEditMode){
26928 Roo.log("disabling buttons");
26929 this.buttons.each( function(item){
26930 if(item.cmd != 'pencil'){
26936 Roo.log("enabling buttons");
26937 if(this.editorcore.initialized){
26938 this.buttons.each( function(item){
26944 Roo.log("calling toggole on editor");
26945 // tell the editor that it's been pressed..
26946 this.editor.toggleSourceEdit(sourceEditMode);
26960 * @class Roo.bootstrap.Markdown
26961 * @extends Roo.bootstrap.TextArea
26962 * Bootstrap Showdown editable area
26963 * @cfg {string} content
26966 * Create a new Showdown
26969 Roo.bootstrap.Markdown = function(config){
26970 Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26974 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea, {
26978 initEvents : function()
26981 Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26982 this.markdownEl = this.el.createChild({
26983 cls : 'roo-markdown-area'
26985 this.inputEl().addClass('d-none');
26986 if (this.getValue() == '') {
26987 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26990 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26992 this.markdownEl.on('click', this.toggleTextEdit, this);
26993 this.on('blur', this.toggleTextEdit, this);
26994 this.on('specialkey', this.resizeTextArea, this);
26997 toggleTextEdit : function()
26999 var sh = this.markdownEl.getHeight();
27000 this.inputEl().addClass('d-none');
27001 this.markdownEl.addClass('d-none');
27002 if (!this.editing) {
27004 this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27005 this.inputEl().removeClass('d-none');
27006 this.inputEl().focus();
27007 this.editing = true;
27010 // show showdown...
27011 this.updateMarkdown();
27012 this.markdownEl.removeClass('d-none');
27013 this.editing = false;
27016 updateMarkdown : function()
27018 if (this.getValue() == '') {
27019 this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27023 this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27026 resizeTextArea: function () {
27029 Roo.log([sh, this.getValue().split("\n").length * 30]);
27030 this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27032 setValue : function(val)
27034 Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27035 if (!this.editing) {
27036 this.updateMarkdown();
27042 if (!this.editing) {
27043 this.toggleTextEdit();
27051 * Ext JS Library 1.1.1
27052 * Copyright(c) 2006-2007, Ext JS, LLC.
27054 * Originally Released Under LGPL - original licence link has changed is not relivant.
27057 * <script type="text/javascript">
27061 * @class Roo.grid.AbstractSelectionModel
27062 * @extends Roo.util.Observable
27063 * Abstract base class for grid SelectionModels. It provides the interface that should be
27064 * implemented by descendant classes. This class should not be directly instantiated.
27067 Roo.grid.AbstractSelectionModel = function(){
27068 this.locked = false;
27069 Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
27072 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable, {
27073 /** @ignore Called by the grid automatically. Do not call directly. */
27074 init : function(grid){
27080 * Locks the selections.
27083 this.locked = true;
27087 * Unlocks the selections.
27089 unlock : function(){
27090 this.locked = false;
27094 * Returns true if the selections are locked.
27095 * @return {Boolean}
27097 isLocked : function(){
27098 return this.locked;
27102 * Ext JS Library 1.1.1
27103 * Copyright(c) 2006-2007, Ext JS, LLC.
27105 * Originally Released Under LGPL - original licence link has changed is not relivant.
27108 * <script type="text/javascript">
27111 * @extends Roo.grid.AbstractSelectionModel
27112 * @class Roo.grid.RowSelectionModel
27113 * The default SelectionModel used by {@link Roo.grid.Grid}.
27114 * It supports multiple selections and keyboard selection/navigation.
27116 * @param {Object} config
27118 Roo.grid.RowSelectionModel = function(config){
27119 Roo.apply(this, config);
27120 this.selections = new Roo.util.MixedCollection(false, function(o){
27125 this.lastActive = false;
27129 * @event selectionchange
27130 * Fires when the selection changes
27131 * @param {SelectionModel} this
27133 "selectionchange" : true,
27135 * @event afterselectionchange
27136 * Fires after the selection changes (eg. by key press or clicking)
27137 * @param {SelectionModel} this
27139 "afterselectionchange" : true,
27141 * @event beforerowselect
27142 * Fires when a row is selected being selected, return false to cancel.
27143 * @param {SelectionModel} this
27144 * @param {Number} rowIndex The selected index
27145 * @param {Boolean} keepExisting False if other selections will be cleared
27147 "beforerowselect" : true,
27150 * Fires when a row is selected.
27151 * @param {SelectionModel} this
27152 * @param {Number} rowIndex The selected index
27153 * @param {Roo.data.Record} r The record
27155 "rowselect" : true,
27157 * @event rowdeselect
27158 * Fires when a row is deselected.
27159 * @param {SelectionModel} this
27160 * @param {Number} rowIndex The selected index
27162 "rowdeselect" : true
27164 Roo.grid.RowSelectionModel.superclass.constructor.call(this);
27165 this.locked = false;
27168 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel, {
27170 * @cfg {Boolean} singleSelect
27171 * True to allow selection of only one row at a time (defaults to false)
27173 singleSelect : false,
27176 initEvents : function(){
27178 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27179 this.grid.on("mousedown", this.handleMouseDown, this);
27180 }else{ // allow click to work like normal
27181 this.grid.on("rowclick", this.handleDragableRowClick, this);
27184 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27185 "up" : function(e){
27187 this.selectPrevious(e.shiftKey);
27188 }else if(this.last !== false && this.lastActive !== false){
27189 var last = this.last;
27190 this.selectRange(this.last, this.lastActive-1);
27191 this.grid.getView().focusRow(this.lastActive);
27192 if(last !== false){
27196 this.selectFirstRow();
27198 this.fireEvent("afterselectionchange", this);
27200 "down" : function(e){
27202 this.selectNext(e.shiftKey);
27203 }else if(this.last !== false && this.lastActive !== false){
27204 var last = this.last;
27205 this.selectRange(this.last, this.lastActive+1);
27206 this.grid.getView().focusRow(this.lastActive);
27207 if(last !== false){
27211 this.selectFirstRow();
27213 this.fireEvent("afterselectionchange", this);
27218 var view = this.grid.view;
27219 view.on("refresh", this.onRefresh, this);
27220 view.on("rowupdated", this.onRowUpdated, this);
27221 view.on("rowremoved", this.onRemove, this);
27225 onRefresh : function(){
27226 var ds = this.grid.dataSource, i, v = this.grid.view;
27227 var s = this.selections;
27228 s.each(function(r){
27229 if((i = ds.indexOfId(r.id)) != -1){
27231 s.add(ds.getAt(i)); // updating the selection relate data
27239 onRemove : function(v, index, r){
27240 this.selections.remove(r);
27244 onRowUpdated : function(v, index, r){
27245 if(this.isSelected(r)){
27246 v.onRowSelect(index);
27252 * @param {Array} records The records to select
27253 * @param {Boolean} keepExisting (optional) True to keep existing selections
27255 selectRecords : function(records, keepExisting){
27257 this.clearSelections();
27259 var ds = this.grid.dataSource;
27260 for(var i = 0, len = records.length; i < len; i++){
27261 this.selectRow(ds.indexOf(records[i]), true);
27266 * Gets the number of selected rows.
27269 getCount : function(){
27270 return this.selections.length;
27274 * Selects the first row in the grid.
27276 selectFirstRow : function(){
27281 * Select the last row.
27282 * @param {Boolean} keepExisting (optional) True to keep existing selections
27284 selectLastRow : function(keepExisting){
27285 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27289 * Selects the row immediately following the last selected row.
27290 * @param {Boolean} keepExisting (optional) True to keep existing selections
27292 selectNext : function(keepExisting){
27293 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
27294 this.selectRow(this.last+1, keepExisting);
27295 this.grid.getView().focusRow(this.last);
27300 * Selects the row that precedes the last selected row.
27301 * @param {Boolean} keepExisting (optional) True to keep existing selections
27303 selectPrevious : function(keepExisting){
27305 this.selectRow(this.last-1, keepExisting);
27306 this.grid.getView().focusRow(this.last);
27311 * Returns the selected records
27312 * @return {Array} Array of selected records
27314 getSelections : function(){
27315 return [].concat(this.selections.items);
27319 * Returns the first selected record.
27322 getSelected : function(){
27323 return this.selections.itemAt(0);
27328 * Clears all selections.
27330 clearSelections : function(fast){
27335 var ds = this.grid.dataSource;
27336 var s = this.selections;
27337 s.each(function(r){
27338 this.deselectRow(ds.indexOfId(r.id));
27342 this.selections.clear();
27349 * Selects all rows.
27351 selectAll : function(){
27355 this.selections.clear();
27356 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
27357 this.selectRow(i, true);
27362 * Returns True if there is a selection.
27363 * @return {Boolean}
27365 hasSelection : function(){
27366 return this.selections.length > 0;
27370 * Returns True if the specified row is selected.
27371 * @param {Number/Record} record The record or index of the record to check
27372 * @return {Boolean}
27374 isSelected : function(index){
27375 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
27376 return (r && this.selections.key(r.id) ? true : false);
27380 * Returns True if the specified record id is selected.
27381 * @param {String} id The id of record to check
27382 * @return {Boolean}
27384 isIdSelected : function(id){
27385 return (this.selections.key(id) ? true : false);
27389 handleMouseDown : function(e, t){
27390 var view = this.grid.getView(), rowIndex;
27391 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
27394 if(e.shiftKey && this.last !== false){
27395 var last = this.last;
27396 this.selectRange(last, rowIndex, e.ctrlKey);
27397 this.last = last; // reset the last
27398 view.focusRow(rowIndex);
27400 var isSelected = this.isSelected(rowIndex);
27401 if(e.button !== 0 && isSelected){
27402 view.focusRow(rowIndex);
27403 }else if(e.ctrlKey && isSelected){
27404 this.deselectRow(rowIndex);
27405 }else if(!isSelected){
27406 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27407 view.focusRow(rowIndex);
27410 this.fireEvent("afterselectionchange", this);
27413 handleDragableRowClick : function(grid, rowIndex, e)
27415 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27416 this.selectRow(rowIndex, false);
27417 grid.view.focusRow(rowIndex);
27418 this.fireEvent("afterselectionchange", this);
27423 * Selects multiple rows.
27424 * @param {Array} rows Array of the indexes of the row to select
27425 * @param {Boolean} keepExisting (optional) True to keep existing selections
27427 selectRows : function(rows, keepExisting){
27429 this.clearSelections();
27431 for(var i = 0, len = rows.length; i < len; i++){
27432 this.selectRow(rows[i], true);
27437 * Selects a range of rows. All rows in between startRow and endRow are also selected.
27438 * @param {Number} startRow The index of the first row in the range
27439 * @param {Number} endRow The index of the last row in the range
27440 * @param {Boolean} keepExisting (optional) True to retain existing selections
27442 selectRange : function(startRow, endRow, keepExisting){
27447 this.clearSelections();
27449 if(startRow <= endRow){
27450 for(var i = startRow; i <= endRow; i++){
27451 this.selectRow(i, true);
27454 for(var i = startRow; i >= endRow; i--){
27455 this.selectRow(i, true);
27461 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27462 * @param {Number} startRow The index of the first row in the range
27463 * @param {Number} endRow The index of the last row in the range
27465 deselectRange : function(startRow, endRow, preventViewNotify){
27469 for(var i = startRow; i <= endRow; i++){
27470 this.deselectRow(i, preventViewNotify);
27476 * @param {Number} row The index of the row to select
27477 * @param {Boolean} keepExisting (optional) True to keep existing selections
27479 selectRow : function(index, keepExisting, preventViewNotify){
27480 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
27483 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27484 if(!keepExisting || this.singleSelect){
27485 this.clearSelections();
27487 var r = this.grid.dataSource.getAt(index);
27488 this.selections.add(r);
27489 this.last = this.lastActive = index;
27490 if(!preventViewNotify){
27491 this.grid.getView().onRowSelect(index);
27493 this.fireEvent("rowselect", this, index, r);
27494 this.fireEvent("selectionchange", this);
27500 * @param {Number} row The index of the row to deselect
27502 deselectRow : function(index, preventViewNotify){
27506 if(this.last == index){
27509 if(this.lastActive == index){
27510 this.lastActive = false;
27512 var r = this.grid.dataSource.getAt(index);
27513 this.selections.remove(r);
27514 if(!preventViewNotify){
27515 this.grid.getView().onRowDeselect(index);
27517 this.fireEvent("rowdeselect", this, index);
27518 this.fireEvent("selectionchange", this);
27522 restoreLast : function(){
27524 this.last = this._last;
27529 acceptsNav : function(row, col, cm){
27530 return !cm.isHidden(col) && cm.isCellEditable(col, row);
27534 onEditorKey : function(field, e){
27535 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27540 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27542 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27544 }else if(k == e.ENTER && !e.ctrlKey){
27548 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27550 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27552 }else if(k == e.ESC){
27556 g.startEditing(newCell[0], newCell[1]);
27561 * Ext JS Library 1.1.1
27562 * Copyright(c) 2006-2007, Ext JS, LLC.
27564 * Originally Released Under LGPL - original licence link has changed is not relivant.
27567 * <script type="text/javascript">
27571 * @class Roo.bootstrap.PagingToolbar
27572 * @extends Roo.bootstrap.NavSimplebar
27573 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27575 * Create a new PagingToolbar
27576 * @param {Object} config The config object
27577 * @param {Roo.data.Store} store
27579 Roo.bootstrap.PagingToolbar = function(config)
27581 // old args format still supported... - xtype is prefered..
27582 // created from xtype...
27584 this.ds = config.dataSource;
27586 if (config.store && !this.ds) {
27587 this.store= Roo.factory(config.store, Roo.data);
27588 this.ds = this.store;
27589 this.ds.xmodule = this.xmodule || false;
27592 this.toolbarItems = [];
27593 if (config.items) {
27594 this.toolbarItems = config.items;
27597 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27602 this.bind(this.ds);
27605 if (Roo.bootstrap.version == 4) {
27606 this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27608 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27613 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27615 * @cfg {Roo.data.Store} dataSource
27616 * The underlying data store providing the paged data
27619 * @cfg {String/HTMLElement/Element} container
27620 * container The id or element that will contain the toolbar
27623 * @cfg {Boolean} displayInfo
27624 * True to display the displayMsg (defaults to false)
27627 * @cfg {Number} pageSize
27628 * The number of records to display per page (defaults to 20)
27632 * @cfg {String} displayMsg
27633 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27635 displayMsg : 'Displaying {0} - {1} of {2}',
27637 * @cfg {String} emptyMsg
27638 * The message to display when no records are found (defaults to "No data to display")
27640 emptyMsg : 'No data to display',
27642 * Customizable piece of the default paging text (defaults to "Page")
27645 beforePageText : "Page",
27647 * Customizable piece of the default paging text (defaults to "of %0")
27650 afterPageText : "of {0}",
27652 * Customizable piece of the default paging text (defaults to "First Page")
27655 firstText : "First Page",
27657 * Customizable piece of the default paging text (defaults to "Previous Page")
27660 prevText : "Previous Page",
27662 * Customizable piece of the default paging text (defaults to "Next Page")
27665 nextText : "Next Page",
27667 * Customizable piece of the default paging text (defaults to "Last Page")
27670 lastText : "Last Page",
27672 * Customizable piece of the default paging text (defaults to "Refresh")
27675 refreshText : "Refresh",
27679 onRender : function(ct, position)
27681 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27682 this.navgroup.parentId = this.id;
27683 this.navgroup.onRender(this.el, null);
27684 // add the buttons to the navgroup
27686 if(this.displayInfo){
27687 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27688 this.displayEl = this.el.select('.x-paging-info', true).first();
27689 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27690 // this.displayEl = navel.el.select('span',true).first();
27696 Roo.each(_this.buttons, function(e){ // this might need to use render????
27697 Roo.factory(e).render(_this.el);
27701 Roo.each(_this.toolbarItems, function(e) {
27702 _this.navgroup.addItem(e);
27706 this.first = this.navgroup.addItem({
27707 tooltip: this.firstText,
27708 cls: "prev btn-outline-secondary",
27709 html : ' <i class="fa fa-step-backward"></i>',
27711 preventDefault: true,
27712 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27715 this.prev = this.navgroup.addItem({
27716 tooltip: this.prevText,
27717 cls: "prev btn-outline-secondary",
27718 html : ' <i class="fa fa-backward"></i>',
27720 preventDefault: true,
27721 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
27723 //this.addSeparator();
27726 var field = this.navgroup.addItem( {
27728 cls : 'x-paging-position btn-outline-secondary',
27730 html : this.beforePageText +
27731 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27732 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
27735 this.field = field.el.select('input', true).first();
27736 this.field.on("keydown", this.onPagingKeydown, this);
27737 this.field.on("focus", function(){this.dom.select();});
27740 this.afterTextEl = field.el.select('.x-paging-after',true).first();
27741 //this.field.setHeight(18);
27742 //this.addSeparator();
27743 this.next = this.navgroup.addItem({
27744 tooltip: this.nextText,
27745 cls: "next btn-outline-secondary",
27746 html : ' <i class="fa fa-forward"></i>',
27748 preventDefault: true,
27749 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
27751 this.last = this.navgroup.addItem({
27752 tooltip: this.lastText,
27753 html : ' <i class="fa fa-step-forward"></i>',
27754 cls: "next btn-outline-secondary",
27756 preventDefault: true,
27757 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
27759 //this.addSeparator();
27760 this.loading = this.navgroup.addItem({
27761 tooltip: this.refreshText,
27762 cls: "btn-outline-secondary",
27763 html : ' <i class="fa fa-refresh"></i>',
27764 preventDefault: true,
27765 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27771 updateInfo : function(){
27772 if(this.displayEl){
27773 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27774 var msg = count == 0 ?
27778 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
27780 this.displayEl.update(msg);
27785 onLoad : function(ds, r, o)
27787 this.cursor = o.params && o.params.start ? o.params.start : 0;
27789 var d = this.getPageData(),
27794 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27795 this.field.dom.value = ap;
27796 this.first.setDisabled(ap == 1);
27797 this.prev.setDisabled(ap == 1);
27798 this.next.setDisabled(ap == ps);
27799 this.last.setDisabled(ap == ps);
27800 this.loading.enable();
27805 getPageData : function(){
27806 var total = this.ds.getTotalCount();
27809 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27810 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27815 onLoadError : function(){
27816 this.loading.enable();
27820 onPagingKeydown : function(e){
27821 var k = e.getKey();
27822 var d = this.getPageData();
27824 var v = this.field.dom.value, pageNum;
27825 if(!v || isNaN(pageNum = parseInt(v, 10))){
27826 this.field.dom.value = d.activePage;
27829 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27830 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27833 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))
27835 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27836 this.field.dom.value = pageNum;
27837 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27840 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27842 var v = this.field.dom.value, pageNum;
27843 var increment = (e.shiftKey) ? 10 : 1;
27844 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27847 if(!v || isNaN(pageNum = parseInt(v, 10))) {
27848 this.field.dom.value = d.activePage;
27851 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27853 this.field.dom.value = parseInt(v, 10) + increment;
27854 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27855 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27862 beforeLoad : function(){
27864 this.loading.disable();
27869 onClick : function(which){
27878 ds.load({params:{start: 0, limit: this.pageSize}});
27881 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27884 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27887 var total = ds.getTotalCount();
27888 var extra = total % this.pageSize;
27889 var lastStart = extra ? (total - extra) : total-this.pageSize;
27890 ds.load({params:{start: lastStart, limit: this.pageSize}});
27893 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27899 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27900 * @param {Roo.data.Store} store The data store to unbind
27902 unbind : function(ds){
27903 ds.un("beforeload", this.beforeLoad, this);
27904 ds.un("load", this.onLoad, this);
27905 ds.un("loadexception", this.onLoadError, this);
27906 ds.un("remove", this.updateInfo, this);
27907 ds.un("add", this.updateInfo, this);
27908 this.ds = undefined;
27912 * Binds the paging toolbar to the specified {@link Roo.data.Store}
27913 * @param {Roo.data.Store} store The data store to bind
27915 bind : function(ds){
27916 ds.on("beforeload", this.beforeLoad, this);
27917 ds.on("load", this.onLoad, this);
27918 ds.on("loadexception", this.onLoadError, this);
27919 ds.on("remove", this.updateInfo, this);
27920 ds.on("add", this.updateInfo, this);
27931 * @class Roo.bootstrap.MessageBar
27932 * @extends Roo.bootstrap.Component
27933 * Bootstrap MessageBar class
27934 * @cfg {String} html contents of the MessageBar
27935 * @cfg {String} weight (info | success | warning | danger) default info
27936 * @cfg {String} beforeClass insert the bar before the given class
27937 * @cfg {Boolean} closable (true | false) default false
27938 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27941 * Create a new Element
27942 * @param {Object} config The config object
27945 Roo.bootstrap.MessageBar = function(config){
27946 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27949 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
27955 beforeClass: 'bootstrap-sticky-wrap',
27957 getAutoCreate : function(){
27961 cls: 'alert alert-dismissable alert-' + this.weight,
27966 html: this.html || ''
27972 cfg.cls += ' alert-messages-fixed';
27986 onRender : function(ct, position)
27988 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27991 var cfg = Roo.apply({}, this.getAutoCreate());
27995 cfg.cls += ' ' + this.cls;
27998 cfg.style = this.style;
28000 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28002 this.el.setVisibilityMode(Roo.Element.DISPLAY);
28005 this.el.select('>button.close').on('click', this.hide, this);
28011 if (!this.rendered) {
28017 this.fireEvent('show', this);
28023 if (!this.rendered) {
28029 this.fireEvent('hide', this);
28032 update : function()
28034 // var e = this.el.dom.firstChild;
28036 // if(this.closable){
28037 // e = e.nextSibling;
28040 // e.data = this.html || '';
28042 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28058 * @class Roo.bootstrap.Graph
28059 * @extends Roo.bootstrap.Component
28060 * Bootstrap Graph class
28064 @cfg {String} graphtype bar | vbar | pie
28065 @cfg {number} g_x coodinator | centre x (pie)
28066 @cfg {number} g_y coodinator | centre y (pie)
28067 @cfg {number} g_r radius (pie)
28068 @cfg {number} g_height height of the chart (respected by all elements in the set)
28069 @cfg {number} g_width width of the chart (respected by all elements in the set)
28070 @cfg {Object} title The title of the chart
28073 -opts (object) options for the chart
28075 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28076 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28078 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.
28079 o stacked (boolean) whether or not to tread values as in a stacked bar chart
28081 o stretch (boolean)
28083 -opts (object) options for the pie
28086 o startAngle (number)
28087 o endAngle (number)
28091 * Create a new Input
28092 * @param {Object} config The config object
28095 Roo.bootstrap.Graph = function(config){
28096 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28102 * The img click event for the img.
28103 * @param {Roo.EventObject} e
28109 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
28120 //g_colors: this.colors,
28127 getAutoCreate : function(){
28138 onRender : function(ct,position){
28141 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28143 if (typeof(Raphael) == 'undefined') {
28144 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28148 this.raphael = Raphael(this.el.dom);
28150 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28151 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28152 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28153 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28155 r.text(160, 10, "Single Series Chart").attr(txtattr);
28156 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28157 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28158 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28160 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28161 r.barchart(330, 10, 300, 220, data1);
28162 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28163 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28166 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28167 // r.barchart(30, 30, 560, 250, xdata, {
28168 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28169 // axis : "0 0 1 1",
28170 // axisxlabels : xdata
28171 // //yvalues : cols,
28174 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28176 // this.load(null,xdata,{
28177 // axis : "0 0 1 1",
28178 // axisxlabels : xdata
28183 load : function(graphtype,xdata,opts)
28185 this.raphael.clear();
28187 graphtype = this.graphtype;
28192 var r = this.raphael,
28193 fin = function () {
28194 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28196 fout = function () {
28197 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28199 pfin = function() {
28200 this.sector.stop();
28201 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28204 this.label[0].stop();
28205 this.label[0].attr({ r: 7.5 });
28206 this.label[1].attr({ "font-weight": 800 });
28209 pfout = function() {
28210 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28213 this.label[0].animate({ r: 5 }, 500, "bounce");
28214 this.label[1].attr({ "font-weight": 400 });
28220 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28223 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28226 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
28227 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28229 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28236 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28241 setTitle: function(o)
28246 initEvents: function() {
28249 this.el.on('click', this.onClick, this);
28253 onClick : function(e)
28255 Roo.log('img onclick');
28256 this.fireEvent('click', this, e);
28268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28271 * @class Roo.bootstrap.dash.NumberBox
28272 * @extends Roo.bootstrap.Component
28273 * Bootstrap NumberBox class
28274 * @cfg {String} headline Box headline
28275 * @cfg {String} content Box content
28276 * @cfg {String} icon Box icon
28277 * @cfg {String} footer Footer text
28278 * @cfg {String} fhref Footer href
28281 * Create a new NumberBox
28282 * @param {Object} config The config object
28286 Roo.bootstrap.dash.NumberBox = function(config){
28287 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28291 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
28300 getAutoCreate : function(){
28304 cls : 'small-box ',
28312 cls : 'roo-headline',
28313 html : this.headline
28317 cls : 'roo-content',
28318 html : this.content
28332 cls : 'ion ' + this.icon
28341 cls : 'small-box-footer',
28342 href : this.fhref || '#',
28346 cfg.cn.push(footer);
28353 onRender : function(ct,position){
28354 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28361 setHeadline: function (value)
28363 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28366 setFooter: function (value, href)
28368 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28371 this.el.select('a.small-box-footer',true).first().attr('href', href);
28376 setContent: function (value)
28378 this.el.select('.roo-content',true).first().dom.innerHTML = value;
28381 initEvents: function()
28395 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28398 * @class Roo.bootstrap.dash.TabBox
28399 * @extends Roo.bootstrap.Component
28400 * Bootstrap TabBox class
28401 * @cfg {String} title Title of the TabBox
28402 * @cfg {String} icon Icon of the TabBox
28403 * @cfg {Boolean} showtabs (true|false) show the tabs default true
28404 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28407 * Create a new TabBox
28408 * @param {Object} config The config object
28412 Roo.bootstrap.dash.TabBox = function(config){
28413 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28418 * When a pane is added
28419 * @param {Roo.bootstrap.dash.TabPane} pane
28423 * @event activatepane
28424 * When a pane is activated
28425 * @param {Roo.bootstrap.dash.TabPane} pane
28427 "activatepane" : true
28435 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
28440 tabScrollable : false,
28442 getChildContainer : function()
28444 return this.el.select('.tab-content', true).first();
28447 getAutoCreate : function(){
28451 cls: 'pull-left header',
28459 cls: 'fa ' + this.icon
28465 cls: 'nav nav-tabs pull-right',
28471 if(this.tabScrollable){
28478 cls: 'nav nav-tabs pull-right',
28489 cls: 'nav-tabs-custom',
28494 cls: 'tab-content no-padding',
28502 initEvents : function()
28504 //Roo.log('add add pane handler');
28505 this.on('addpane', this.onAddPane, this);
28508 * Updates the box title
28509 * @param {String} html to set the title to.
28511 setTitle : function(value)
28513 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28515 onAddPane : function(pane)
28517 this.panes.push(pane);
28518 //Roo.log('addpane');
28520 // tabs are rendere left to right..
28521 if(!this.showtabs){
28525 var ctr = this.el.select('.nav-tabs', true).first();
28528 var existing = ctr.select('.nav-tab',true);
28529 var qty = existing.getCount();;
28532 var tab = ctr.createChild({
28534 cls : 'nav-tab' + (qty ? '' : ' active'),
28542 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28545 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28547 pane.el.addClass('active');
28552 onTabClick : function(ev,un,ob,pane)
28554 //Roo.log('tab - prev default');
28555 ev.preventDefault();
28558 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28559 pane.tab.addClass('active');
28560 //Roo.log(pane.title);
28561 this.getChildContainer().select('.tab-pane',true).removeClass('active');
28562 // technically we should have a deactivate event.. but maybe add later.
28563 // and it should not de-activate the selected tab...
28564 this.fireEvent('activatepane', pane);
28565 pane.el.addClass('active');
28566 pane.fireEvent('activate');
28571 getActivePane : function()
28574 Roo.each(this.panes, function(p) {
28575 if(p.el.hasClass('active')){
28596 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28598 * @class Roo.bootstrap.TabPane
28599 * @extends Roo.bootstrap.Component
28600 * Bootstrap TabPane class
28601 * @cfg {Boolean} active (false | true) Default false
28602 * @cfg {String} title title of panel
28606 * Create a new TabPane
28607 * @param {Object} config The config object
28610 Roo.bootstrap.dash.TabPane = function(config){
28611 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28617 * When a pane is activated
28618 * @param {Roo.bootstrap.dash.TabPane} pane
28625 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
28630 // the tabBox that this is attached to.
28633 getAutoCreate : function()
28641 cfg.cls += ' active';
28646 initEvents : function()
28648 //Roo.log('trigger add pane handler');
28649 this.parent().fireEvent('addpane', this)
28653 * Updates the tab title
28654 * @param {String} html to set the title to.
28656 setTitle: function(str)
28662 this.tab.select('a', true).first().dom.innerHTML = str;
28679 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28682 * @class Roo.bootstrap.menu.Menu
28683 * @extends Roo.bootstrap.Component
28684 * Bootstrap Menu class - container for Menu
28685 * @cfg {String} html Text of the menu
28686 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28687 * @cfg {String} icon Font awesome icon
28688 * @cfg {String} pos Menu align to (top | bottom) default bottom
28692 * Create a new Menu
28693 * @param {Object} config The config object
28697 Roo.bootstrap.menu.Menu = function(config){
28698 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28702 * @event beforeshow
28703 * Fires before this menu is displayed
28704 * @param {Roo.bootstrap.menu.Menu} this
28708 * @event beforehide
28709 * Fires before this menu is hidden
28710 * @param {Roo.bootstrap.menu.Menu} this
28715 * Fires after this menu is displayed
28716 * @param {Roo.bootstrap.menu.Menu} this
28721 * Fires after this menu is hidden
28722 * @param {Roo.bootstrap.menu.Menu} this
28727 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28728 * @param {Roo.bootstrap.menu.Menu} this
28729 * @param {Roo.EventObject} e
28736 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
28740 weight : 'default',
28745 getChildContainer : function() {
28746 if(this.isSubMenu){
28750 return this.el.select('ul.dropdown-menu', true).first();
28753 getAutoCreate : function()
28758 cls : 'roo-menu-text',
28766 cls : 'fa ' + this.icon
28777 cls : 'dropdown-button btn btn-' + this.weight,
28782 cls : 'dropdown-toggle btn btn-' + this.weight,
28792 cls : 'dropdown-menu'
28798 if(this.pos == 'top'){
28799 cfg.cls += ' dropup';
28802 if(this.isSubMenu){
28805 cls : 'dropdown-menu'
28812 onRender : function(ct, position)
28814 this.isSubMenu = ct.hasClass('dropdown-submenu');
28816 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28819 initEvents : function()
28821 if(this.isSubMenu){
28825 this.hidden = true;
28827 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28828 this.triggerEl.on('click', this.onTriggerPress, this);
28830 this.buttonEl = this.el.select('button.dropdown-button', true).first();
28831 this.buttonEl.on('click', this.onClick, this);
28837 if(this.isSubMenu){
28841 return this.el.select('ul.dropdown-menu', true).first();
28844 onClick : function(e)
28846 this.fireEvent("click", this, e);
28849 onTriggerPress : function(e)
28851 if (this.isVisible()) {
28858 isVisible : function(){
28859 return !this.hidden;
28864 this.fireEvent("beforeshow", this);
28866 this.hidden = false;
28867 this.el.addClass('open');
28869 Roo.get(document).on("mouseup", this.onMouseUp, this);
28871 this.fireEvent("show", this);
28878 this.fireEvent("beforehide", this);
28880 this.hidden = true;
28881 this.el.removeClass('open');
28883 Roo.get(document).un("mouseup", this.onMouseUp);
28885 this.fireEvent("hide", this);
28888 onMouseUp : function()
28902 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28905 * @class Roo.bootstrap.menu.Item
28906 * @extends Roo.bootstrap.Component
28907 * Bootstrap MenuItem class
28908 * @cfg {Boolean} submenu (true | false) default false
28909 * @cfg {String} html text of the item
28910 * @cfg {String} href the link
28911 * @cfg {Boolean} disable (true | false) default false
28912 * @cfg {Boolean} preventDefault (true | false) default true
28913 * @cfg {String} icon Font awesome icon
28914 * @cfg {String} pos Submenu align to (left | right) default right
28918 * Create a new Item
28919 * @param {Object} config The config object
28923 Roo.bootstrap.menu.Item = function(config){
28924 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28928 * Fires when the mouse is hovering over this menu
28929 * @param {Roo.bootstrap.menu.Item} this
28930 * @param {Roo.EventObject} e
28935 * Fires when the mouse exits this menu
28936 * @param {Roo.bootstrap.menu.Item} this
28937 * @param {Roo.EventObject} e
28943 * The raw click event for the entire grid.
28944 * @param {Roo.EventObject} e
28950 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
28955 preventDefault: true,
28960 getAutoCreate : function()
28965 cls : 'roo-menu-item-text',
28973 cls : 'fa ' + this.icon
28982 href : this.href || '#',
28989 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28993 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28995 if(this.pos == 'left'){
28996 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29003 initEvents : function()
29005 this.el.on('mouseover', this.onMouseOver, this);
29006 this.el.on('mouseout', this.onMouseOut, this);
29008 this.el.select('a', true).first().on('click', this.onClick, this);
29012 onClick : function(e)
29014 if(this.preventDefault){
29015 e.preventDefault();
29018 this.fireEvent("click", this, e);
29021 onMouseOver : function(e)
29023 if(this.submenu && this.pos == 'left'){
29024 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29027 this.fireEvent("mouseover", this, e);
29030 onMouseOut : function(e)
29032 this.fireEvent("mouseout", this, e);
29044 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29047 * @class Roo.bootstrap.menu.Separator
29048 * @extends Roo.bootstrap.Component
29049 * Bootstrap Separator class
29052 * Create a new Separator
29053 * @param {Object} config The config object
29057 Roo.bootstrap.menu.Separator = function(config){
29058 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29061 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
29063 getAutoCreate : function(){
29066 cls: 'dropdown-divider divider'
29084 * @class Roo.bootstrap.Tooltip
29085 * Bootstrap Tooltip class
29086 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29087 * to determine which dom element triggers the tooltip.
29089 * It needs to add support for additional attributes like tooltip-position
29092 * Create a new Toolti
29093 * @param {Object} config The config object
29096 Roo.bootstrap.Tooltip = function(config){
29097 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29099 this.alignment = Roo.bootstrap.Tooltip.alignment;
29101 if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29102 this.alignment = config.alignment;
29107 Roo.apply(Roo.bootstrap.Tooltip, {
29109 * @function init initialize tooltip monitoring.
29113 currentTip : false,
29114 currentRegion : false,
29120 Roo.get(document).on('mouseover', this.enter ,this);
29121 Roo.get(document).on('mouseout', this.leave, this);
29124 this.currentTip = new Roo.bootstrap.Tooltip();
29127 enter : function(ev)
29129 var dom = ev.getTarget();
29131 //Roo.log(['enter',dom]);
29132 var el = Roo.fly(dom);
29133 if (this.currentEl) {
29135 //Roo.log(this.currentEl);
29136 //Roo.log(this.currentEl.contains(dom));
29137 if (this.currentEl == el) {
29140 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29146 if (this.currentTip.el) {
29147 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29151 if(!el || el.dom == document){
29157 if (!el.attr('tooltip')) {
29158 pel = el.findParent("[tooltip]");
29160 bindEl = Roo.get(pel);
29166 // you can not look for children, as if el is the body.. then everythign is the child..
29167 if (!pel && !el.attr('tooltip')) { //
29168 if (!el.select("[tooltip]").elements.length) {
29171 // is the mouse over this child...?
29172 bindEl = el.select("[tooltip]").first();
29173 var xy = ev.getXY();
29174 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29175 //Roo.log("not in region.");
29178 //Roo.log("child element over..");
29181 this.currentEl = el;
29182 this.currentTip.bind(bindEl);
29183 this.currentRegion = Roo.lib.Region.getRegion(dom);
29184 this.currentTip.enter();
29187 leave : function(ev)
29189 var dom = ev.getTarget();
29190 //Roo.log(['leave',dom]);
29191 if (!this.currentEl) {
29196 if (dom != this.currentEl.dom) {
29199 var xy = ev.getXY();
29200 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
29203 // only activate leave if mouse cursor is outside... bounding box..
29208 if (this.currentTip) {
29209 this.currentTip.leave();
29211 //Roo.log('clear currentEl');
29212 this.currentEl = false;
29217 'left' : ['r-l', [-2,0], 'right'],
29218 'right' : ['l-r', [2,0], 'left'],
29219 'bottom' : ['t-b', [0,2], 'top'],
29220 'top' : [ 'b-t', [0,-2], 'bottom']
29226 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
29231 delay : null, // can be { show : 300 , hide: 500}
29235 hoverState : null, //???
29237 placement : 'bottom',
29241 getAutoCreate : function(){
29248 cls : 'tooltip-arrow arrow'
29251 cls : 'tooltip-inner'
29258 bind : function(el)
29263 initEvents : function()
29265 this.arrowEl = this.el.select('.arrow', true).first();
29266 this.innerEl = this.el.select('.tooltip-inner', true).first();
29269 enter : function () {
29271 if (this.timeout != null) {
29272 clearTimeout(this.timeout);
29275 this.hoverState = 'in';
29276 //Roo.log("enter - show");
29277 if (!this.delay || !this.delay.show) {
29282 this.timeout = setTimeout(function () {
29283 if (_t.hoverState == 'in') {
29286 }, this.delay.show);
29290 clearTimeout(this.timeout);
29292 this.hoverState = 'out';
29293 if (!this.delay || !this.delay.hide) {
29299 this.timeout = setTimeout(function () {
29300 //Roo.log("leave - timeout");
29302 if (_t.hoverState == 'out') {
29304 Roo.bootstrap.Tooltip.currentEl = false;
29309 show : function (msg)
29312 this.render(document.body);
29315 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29317 var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29319 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29321 this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29322 'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29324 var placement = typeof this.placement == 'function' ?
29325 this.placement.call(this, this.el, on_el) :
29328 var autoToken = /\s?auto?\s?/i;
29329 var autoPlace = autoToken.test(placement);
29331 placement = placement.replace(autoToken, '') || 'top';
29335 //this.el.setXY([0,0]);
29337 //this.el.dom.style.display='block';
29339 //this.el.appendTo(on_el);
29341 var p = this.getPosition();
29342 var box = this.el.getBox();
29348 var align = this.alignment[placement];
29350 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29352 if(placement == 'top' || placement == 'bottom'){
29354 placement = 'right';
29357 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29358 placement = 'left';
29361 var scroll = Roo.select('body', true).first().getScroll();
29363 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29367 align = this.alignment[placement];
29369 this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29373 var elems = document.getElementsByTagName('div');
29374 var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29375 for (var i = 0; i < elems.length; i++) {
29376 var zindex = Number.parseInt(
29377 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29380 if (zindex > highest) {
29387 this.el.dom.style.zIndex = highest;
29389 this.el.alignTo(this.bindEl, align[0],align[1]);
29390 //var arrow = this.el.select('.arrow',true).first();
29391 //arrow.set(align[2],
29393 this.el.addClass(placement);
29394 this.el.addClass("bs-tooltip-"+ placement);
29396 this.el.addClass('in fade show');
29398 this.hoverState = null;
29400 if (this.el.hasClass('fade')) {
29415 //this.el.setXY([0,0]);
29416 this.el.removeClass(['show', 'in']);
29432 * @class Roo.bootstrap.LocationPicker
29433 * @extends Roo.bootstrap.Component
29434 * Bootstrap LocationPicker class
29435 * @cfg {Number} latitude Position when init default 0
29436 * @cfg {Number} longitude Position when init default 0
29437 * @cfg {Number} zoom default 15
29438 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29439 * @cfg {Boolean} mapTypeControl default false
29440 * @cfg {Boolean} disableDoubleClickZoom default false
29441 * @cfg {Boolean} scrollwheel default true
29442 * @cfg {Boolean} streetViewControl default false
29443 * @cfg {Number} radius default 0
29444 * @cfg {String} locationName
29445 * @cfg {Boolean} draggable default true
29446 * @cfg {Boolean} enableAutocomplete default false
29447 * @cfg {Boolean} enableReverseGeocode default true
29448 * @cfg {String} markerTitle
29451 * Create a new LocationPicker
29452 * @param {Object} config The config object
29456 Roo.bootstrap.LocationPicker = function(config){
29458 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29463 * Fires when the picker initialized.
29464 * @param {Roo.bootstrap.LocationPicker} this
29465 * @param {Google Location} location
29469 * @event positionchanged
29470 * Fires when the picker position changed.
29471 * @param {Roo.bootstrap.LocationPicker} this
29472 * @param {Google Location} location
29474 positionchanged : true,
29477 * Fires when the map resize.
29478 * @param {Roo.bootstrap.LocationPicker} this
29483 * Fires when the map show.
29484 * @param {Roo.bootstrap.LocationPicker} this
29489 * Fires when the map hide.
29490 * @param {Roo.bootstrap.LocationPicker} this
29495 * Fires when click the map.
29496 * @param {Roo.bootstrap.LocationPicker} this
29497 * @param {Map event} e
29501 * @event mapRightClick
29502 * Fires when right click the map.
29503 * @param {Roo.bootstrap.LocationPicker} this
29504 * @param {Map event} e
29506 mapRightClick : true,
29508 * @event markerClick
29509 * Fires when click the marker.
29510 * @param {Roo.bootstrap.LocationPicker} this
29511 * @param {Map event} e
29513 markerClick : true,
29515 * @event markerRightClick
29516 * Fires when right click the marker.
29517 * @param {Roo.bootstrap.LocationPicker} this
29518 * @param {Map event} e
29520 markerRightClick : true,
29522 * @event OverlayViewDraw
29523 * Fires when OverlayView Draw
29524 * @param {Roo.bootstrap.LocationPicker} this
29526 OverlayViewDraw : true,
29528 * @event OverlayViewOnAdd
29529 * Fires when OverlayView Draw
29530 * @param {Roo.bootstrap.LocationPicker} this
29532 OverlayViewOnAdd : true,
29534 * @event OverlayViewOnRemove
29535 * Fires when OverlayView Draw
29536 * @param {Roo.bootstrap.LocationPicker} this
29538 OverlayViewOnRemove : true,
29540 * @event OverlayViewShow
29541 * Fires when OverlayView Draw
29542 * @param {Roo.bootstrap.LocationPicker} this
29543 * @param {Pixel} cpx
29545 OverlayViewShow : true,
29547 * @event OverlayViewHide
29548 * Fires when OverlayView Draw
29549 * @param {Roo.bootstrap.LocationPicker} this
29551 OverlayViewHide : true,
29553 * @event loadexception
29554 * Fires when load google lib failed.
29555 * @param {Roo.bootstrap.LocationPicker} this
29557 loadexception : true
29562 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
29564 gMapContext: false,
29570 mapTypeControl: false,
29571 disableDoubleClickZoom: false,
29573 streetViewControl: false,
29577 enableAutocomplete: false,
29578 enableReverseGeocode: true,
29581 getAutoCreate: function()
29586 cls: 'roo-location-picker'
29592 initEvents: function(ct, position)
29594 if(!this.el.getWidth() || this.isApplied()){
29598 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29603 initial: function()
29605 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29606 this.fireEvent('loadexception', this);
29610 if(!this.mapTypeId){
29611 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29614 this.gMapContext = this.GMapContext();
29616 this.initOverlayView();
29618 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29622 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29623 _this.setPosition(_this.gMapContext.marker.position);
29626 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29627 _this.fireEvent('mapClick', this, event);
29631 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29632 _this.fireEvent('mapRightClick', this, event);
29636 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29637 _this.fireEvent('markerClick', this, event);
29641 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29642 _this.fireEvent('markerRightClick', this, event);
29646 this.setPosition(this.gMapContext.location);
29648 this.fireEvent('initial', this, this.gMapContext.location);
29651 initOverlayView: function()
29655 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29659 _this.fireEvent('OverlayViewDraw', _this);
29664 _this.fireEvent('OverlayViewOnAdd', _this);
29667 onRemove: function()
29669 _this.fireEvent('OverlayViewOnRemove', _this);
29672 show: function(cpx)
29674 _this.fireEvent('OverlayViewShow', _this, cpx);
29679 _this.fireEvent('OverlayViewHide', _this);
29685 fromLatLngToContainerPixel: function(event)
29687 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29690 isApplied: function()
29692 return this.getGmapContext() == false ? false : true;
29695 getGmapContext: function()
29697 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29700 GMapContext: function()
29702 var position = new google.maps.LatLng(this.latitude, this.longitude);
29704 var _map = new google.maps.Map(this.el.dom, {
29707 mapTypeId: this.mapTypeId,
29708 mapTypeControl: this.mapTypeControl,
29709 disableDoubleClickZoom: this.disableDoubleClickZoom,
29710 scrollwheel: this.scrollwheel,
29711 streetViewControl: this.streetViewControl,
29712 locationName: this.locationName,
29713 draggable: this.draggable,
29714 enableAutocomplete: this.enableAutocomplete,
29715 enableReverseGeocode: this.enableReverseGeocode
29718 var _marker = new google.maps.Marker({
29719 position: position,
29721 title: this.markerTitle,
29722 draggable: this.draggable
29729 location: position,
29730 radius: this.radius,
29731 locationName: this.locationName,
29732 addressComponents: {
29733 formatted_address: null,
29734 addressLine1: null,
29735 addressLine2: null,
29737 streetNumber: null,
29741 stateOrProvince: null
29744 domContainer: this.el.dom,
29745 geodecoder: new google.maps.Geocoder()
29749 drawCircle: function(center, radius, options)
29751 if (this.gMapContext.circle != null) {
29752 this.gMapContext.circle.setMap(null);
29756 options = Roo.apply({}, options, {
29757 strokeColor: "#0000FF",
29758 strokeOpacity: .35,
29760 fillColor: "#0000FF",
29764 options.map = this.gMapContext.map;
29765 options.radius = radius;
29766 options.center = center;
29767 this.gMapContext.circle = new google.maps.Circle(options);
29768 return this.gMapContext.circle;
29774 setPosition: function(location)
29776 this.gMapContext.location = location;
29777 this.gMapContext.marker.setPosition(location);
29778 this.gMapContext.map.panTo(location);
29779 this.drawCircle(location, this.gMapContext.radius, {});
29783 if (this.gMapContext.settings.enableReverseGeocode) {
29784 this.gMapContext.geodecoder.geocode({
29785 latLng: this.gMapContext.location
29786 }, function(results, status) {
29788 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29789 _this.gMapContext.locationName = results[0].formatted_address;
29790 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29792 _this.fireEvent('positionchanged', this, location);
29799 this.fireEvent('positionchanged', this, location);
29804 google.maps.event.trigger(this.gMapContext.map, "resize");
29806 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29808 this.fireEvent('resize', this);
29811 setPositionByLatLng: function(latitude, longitude)
29813 this.setPosition(new google.maps.LatLng(latitude, longitude));
29816 getCurrentPosition: function()
29819 latitude: this.gMapContext.location.lat(),
29820 longitude: this.gMapContext.location.lng()
29824 getAddressName: function()
29826 return this.gMapContext.locationName;
29829 getAddressComponents: function()
29831 return this.gMapContext.addressComponents;
29834 address_component_from_google_geocode: function(address_components)
29838 for (var i = 0; i < address_components.length; i++) {
29839 var component = address_components[i];
29840 if (component.types.indexOf("postal_code") >= 0) {
29841 result.postalCode = component.short_name;
29842 } else if (component.types.indexOf("street_number") >= 0) {
29843 result.streetNumber = component.short_name;
29844 } else if (component.types.indexOf("route") >= 0) {
29845 result.streetName = component.short_name;
29846 } else if (component.types.indexOf("neighborhood") >= 0) {
29847 result.city = component.short_name;
29848 } else if (component.types.indexOf("locality") >= 0) {
29849 result.city = component.short_name;
29850 } else if (component.types.indexOf("sublocality") >= 0) {
29851 result.district = component.short_name;
29852 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29853 result.stateOrProvince = component.short_name;
29854 } else if (component.types.indexOf("country") >= 0) {
29855 result.country = component.short_name;
29859 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29860 result.addressLine2 = "";
29864 setZoomLevel: function(zoom)
29866 this.gMapContext.map.setZoom(zoom);
29879 this.fireEvent('show', this);
29890 this.fireEvent('hide', this);
29895 Roo.apply(Roo.bootstrap.LocationPicker, {
29897 OverlayView : function(map, options)
29899 options = options || {};
29906 * @class Roo.bootstrap.Alert
29907 * @extends Roo.bootstrap.Component
29908 * Bootstrap Alert class - shows an alert area box
29910 * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29911 Enter a valid email address
29914 * @cfg {String} title The title of alert
29915 * @cfg {String} html The content of alert
29916 * @cfg {String} weight (success|info|warning|danger) Weight of the message
29917 * @cfg {String} fa font-awesomeicon
29918 * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29919 * @cfg {Boolean} close true to show a x closer
29923 * Create a new alert
29924 * @param {Object} config The config object
29928 Roo.bootstrap.Alert = function(config){
29929 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29933 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
29939 faicon: false, // BC
29943 getAutoCreate : function()
29955 style : this.close ? '' : 'display:none'
29959 cls : 'roo-alert-icon'
29964 cls : 'roo-alert-title',
29969 cls : 'roo-alert-text',
29976 cfg.cn[0].cls += ' fa ' + this.faicon;
29979 cfg.cn[0].cls += ' fa ' + this.fa;
29983 cfg.cls += ' alert-' + this.weight;
29989 initEvents: function()
29991 this.el.setVisibilityMode(Roo.Element.DISPLAY);
29992 this.titleEl = this.el.select('.roo-alert-title',true).first();
29993 this.iconEl = this.el.select('.roo-alert-icon',true).first();
29994 this.htmlEl = this.el.select('.roo-alert-text',true).first();
29995 if (this.seconds > 0) {
29996 this.hide.defer(this.seconds, this);
30000 * Set the Title Message HTML
30001 * @param {String} html
30003 setTitle : function(str)
30005 this.titleEl.dom.innerHTML = str;
30009 * Set the Body Message HTML
30010 * @param {String} html
30012 setHtml : function(str)
30014 this.htmlEl.dom.innerHTML = str;
30017 * Set the Weight of the alert
30018 * @param {String} (success|info|warning|danger) weight
30021 setWeight : function(weight)
30024 this.el.removeClass('alert-' + this.weight);
30027 this.weight = weight;
30029 this.el.addClass('alert-' + this.weight);
30032 * Set the Icon of the alert
30033 * @param {String} see fontawsome names (name without the 'fa-' bit)
30035 setIcon : function(icon)
30038 this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30041 this.faicon = icon;
30043 this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30068 * @class Roo.bootstrap.UploadCropbox
30069 * @extends Roo.bootstrap.Component
30070 * Bootstrap UploadCropbox class
30071 * @cfg {String} emptyText show when image has been loaded
30072 * @cfg {String} rotateNotify show when image too small to rotate
30073 * @cfg {Number} errorTimeout default 3000
30074 * @cfg {Number} minWidth default 300
30075 * @cfg {Number} minHeight default 300
30076 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30077 * @cfg {Boolean} isDocument (true|false) default false
30078 * @cfg {String} url action url
30079 * @cfg {String} paramName default 'imageUpload'
30080 * @cfg {String} method default POST
30081 * @cfg {Boolean} loadMask (true|false) default true
30082 * @cfg {Boolean} loadingText default 'Loading...'
30085 * Create a new UploadCropbox
30086 * @param {Object} config The config object
30089 Roo.bootstrap.UploadCropbox = function(config){
30090 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30094 * @event beforeselectfile
30095 * Fire before select file
30096 * @param {Roo.bootstrap.UploadCropbox} this
30098 "beforeselectfile" : true,
30101 * Fire after initEvent
30102 * @param {Roo.bootstrap.UploadCropbox} this
30107 * Fire after initEvent
30108 * @param {Roo.bootstrap.UploadCropbox} this
30109 * @param {String} data
30114 * Fire when preparing the file data
30115 * @param {Roo.bootstrap.UploadCropbox} this
30116 * @param {Object} file
30121 * Fire when get exception
30122 * @param {Roo.bootstrap.UploadCropbox} this
30123 * @param {XMLHttpRequest} xhr
30125 "exception" : true,
30127 * @event beforeloadcanvas
30128 * Fire before load the canvas
30129 * @param {Roo.bootstrap.UploadCropbox} this
30130 * @param {String} src
30132 "beforeloadcanvas" : true,
30135 * Fire when trash image
30136 * @param {Roo.bootstrap.UploadCropbox} this
30141 * Fire when download the image
30142 * @param {Roo.bootstrap.UploadCropbox} this
30146 * @event footerbuttonclick
30147 * Fire when footerbuttonclick
30148 * @param {Roo.bootstrap.UploadCropbox} this
30149 * @param {String} type
30151 "footerbuttonclick" : true,
30155 * @param {Roo.bootstrap.UploadCropbox} this
30160 * Fire when rotate the image
30161 * @param {Roo.bootstrap.UploadCropbox} this
30162 * @param {String} pos
30167 * Fire when inspect the file
30168 * @param {Roo.bootstrap.UploadCropbox} this
30169 * @param {Object} file
30174 * Fire when xhr upload the file
30175 * @param {Roo.bootstrap.UploadCropbox} this
30176 * @param {Object} data
30181 * Fire when arrange the file data
30182 * @param {Roo.bootstrap.UploadCropbox} this
30183 * @param {Object} formData
30188 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30191 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
30193 emptyText : 'Click to upload image',
30194 rotateNotify : 'Image is too small to rotate',
30195 errorTimeout : 3000,
30209 cropType : 'image/jpeg',
30211 canvasLoaded : false,
30212 isDocument : false,
30214 paramName : 'imageUpload',
30216 loadingText : 'Loading...',
30219 getAutoCreate : function()
30223 cls : 'roo-upload-cropbox',
30227 cls : 'roo-upload-cropbox-selector',
30232 cls : 'roo-upload-cropbox-body',
30233 style : 'cursor:pointer',
30237 cls : 'roo-upload-cropbox-preview'
30241 cls : 'roo-upload-cropbox-thumb'
30245 cls : 'roo-upload-cropbox-empty-notify',
30246 html : this.emptyText
30250 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30251 html : this.rotateNotify
30257 cls : 'roo-upload-cropbox-footer',
30260 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30270 onRender : function(ct, position)
30272 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30274 if (this.buttons.length) {
30276 Roo.each(this.buttons, function(bb) {
30278 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30280 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30286 this.maskEl = this.el;
30290 initEvents : function()
30292 this.urlAPI = (window.createObjectURL && window) ||
30293 (window.URL && URL.revokeObjectURL && URL) ||
30294 (window.webkitURL && webkitURL);
30296 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30297 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30299 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30300 this.selectorEl.hide();
30302 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30303 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30305 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30306 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30307 this.thumbEl.hide();
30309 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30310 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30312 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30313 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30314 this.errorEl.hide();
30316 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30317 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30318 this.footerEl.hide();
30320 this.setThumbBoxSize();
30326 this.fireEvent('initial', this);
30333 window.addEventListener("resize", function() { _this.resize(); } );
30335 this.bodyEl.on('click', this.beforeSelectFile, this);
30338 this.bodyEl.on('touchstart', this.onTouchStart, this);
30339 this.bodyEl.on('touchmove', this.onTouchMove, this);
30340 this.bodyEl.on('touchend', this.onTouchEnd, this);
30344 this.bodyEl.on('mousedown', this.onMouseDown, this);
30345 this.bodyEl.on('mousemove', this.onMouseMove, this);
30346 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30347 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30348 Roo.get(document).on('mouseup', this.onMouseUp, this);
30351 this.selectorEl.on('change', this.onFileSelected, this);
30357 this.baseScale = 1;
30359 this.baseRotate = 1;
30360 this.dragable = false;
30361 this.pinching = false;
30364 this.cropData = false;
30365 this.notifyEl.dom.innerHTML = this.emptyText;
30367 this.selectorEl.dom.value = '';
30371 resize : function()
30373 if(this.fireEvent('resize', this) != false){
30374 this.setThumbBoxPosition();
30375 this.setCanvasPosition();
30379 onFooterButtonClick : function(e, el, o, type)
30382 case 'rotate-left' :
30383 this.onRotateLeft(e);
30385 case 'rotate-right' :
30386 this.onRotateRight(e);
30389 this.beforeSelectFile(e);
30404 this.fireEvent('footerbuttonclick', this, type);
30407 beforeSelectFile : function(e)
30409 e.preventDefault();
30411 if(this.fireEvent('beforeselectfile', this) != false){
30412 this.selectorEl.dom.click();
30416 onFileSelected : function(e)
30418 e.preventDefault();
30420 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30424 var file = this.selectorEl.dom.files[0];
30426 if(this.fireEvent('inspect', this, file) != false){
30427 this.prepare(file);
30432 trash : function(e)
30434 this.fireEvent('trash', this);
30437 download : function(e)
30439 this.fireEvent('download', this);
30442 loadCanvas : function(src)
30444 if(this.fireEvent('beforeloadcanvas', this, src) != false){
30448 this.imageEl = document.createElement('img');
30452 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30454 this.imageEl.src = src;
30458 onLoadCanvas : function()
30460 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30461 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30463 this.bodyEl.un('click', this.beforeSelectFile, this);
30465 this.notifyEl.hide();
30466 this.thumbEl.show();
30467 this.footerEl.show();
30469 this.baseRotateLevel();
30471 if(this.isDocument){
30472 this.setThumbBoxSize();
30475 this.setThumbBoxPosition();
30477 this.baseScaleLevel();
30483 this.canvasLoaded = true;
30486 this.maskEl.unmask();
30491 setCanvasPosition : function()
30493 if(!this.canvasEl){
30497 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30498 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30500 this.previewEl.setLeft(pw);
30501 this.previewEl.setTop(ph);
30505 onMouseDown : function(e)
30509 this.dragable = true;
30510 this.pinching = false;
30512 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30513 this.dragable = false;
30517 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30518 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30522 onMouseMove : function(e)
30526 if(!this.canvasLoaded){
30530 if (!this.dragable){
30534 var minX = Math.ceil(this.thumbEl.getLeft(true));
30535 var minY = Math.ceil(this.thumbEl.getTop(true));
30537 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30538 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30540 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30541 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30543 x = x - this.mouseX;
30544 y = y - this.mouseY;
30546 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30547 var bgY = Math.ceil(y + this.previewEl.getTop(true));
30549 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30550 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30552 this.previewEl.setLeft(bgX);
30553 this.previewEl.setTop(bgY);
30555 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30556 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30559 onMouseUp : function(e)
30563 this.dragable = false;
30566 onMouseWheel : function(e)
30570 this.startScale = this.scale;
30572 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30574 if(!this.zoomable()){
30575 this.scale = this.startScale;
30584 zoomable : function()
30586 var minScale = this.thumbEl.getWidth() / this.minWidth;
30588 if(this.minWidth < this.minHeight){
30589 minScale = this.thumbEl.getHeight() / this.minHeight;
30592 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30593 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30597 (this.rotate == 0 || this.rotate == 180) &&
30599 width > this.imageEl.OriginWidth ||
30600 height > this.imageEl.OriginHeight ||
30601 (width < this.minWidth && height < this.minHeight)
30609 (this.rotate == 90 || this.rotate == 270) &&
30611 width > this.imageEl.OriginWidth ||
30612 height > this.imageEl.OriginHeight ||
30613 (width < this.minHeight && height < this.minWidth)
30620 !this.isDocument &&
30621 (this.rotate == 0 || this.rotate == 180) &&
30623 width < this.minWidth ||
30624 width > this.imageEl.OriginWidth ||
30625 height < this.minHeight ||
30626 height > this.imageEl.OriginHeight
30633 !this.isDocument &&
30634 (this.rotate == 90 || this.rotate == 270) &&
30636 width < this.minHeight ||
30637 width > this.imageEl.OriginWidth ||
30638 height < this.minWidth ||
30639 height > this.imageEl.OriginHeight
30649 onRotateLeft : function(e)
30651 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30653 var minScale = this.thumbEl.getWidth() / this.minWidth;
30655 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30656 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30658 this.startScale = this.scale;
30660 while (this.getScaleLevel() < minScale){
30662 this.scale = this.scale + 1;
30664 if(!this.zoomable()){
30669 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30670 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30675 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30682 this.scale = this.startScale;
30684 this.onRotateFail();
30689 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30691 if(this.isDocument){
30692 this.setThumbBoxSize();
30693 this.setThumbBoxPosition();
30694 this.setCanvasPosition();
30699 this.fireEvent('rotate', this, 'left');
30703 onRotateRight : function(e)
30705 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30707 var minScale = this.thumbEl.getWidth() / this.minWidth;
30709 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30710 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30712 this.startScale = this.scale;
30714 while (this.getScaleLevel() < minScale){
30716 this.scale = this.scale + 1;
30718 if(!this.zoomable()){
30723 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30724 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30729 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30736 this.scale = this.startScale;
30738 this.onRotateFail();
30743 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30745 if(this.isDocument){
30746 this.setThumbBoxSize();
30747 this.setThumbBoxPosition();
30748 this.setCanvasPosition();
30753 this.fireEvent('rotate', this, 'right');
30756 onRotateFail : function()
30758 this.errorEl.show(true);
30762 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30767 this.previewEl.dom.innerHTML = '';
30769 var canvasEl = document.createElement("canvas");
30771 var contextEl = canvasEl.getContext("2d");
30773 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30774 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30775 var center = this.imageEl.OriginWidth / 2;
30777 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30778 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30779 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30780 center = this.imageEl.OriginHeight / 2;
30783 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30785 contextEl.translate(center, center);
30786 contextEl.rotate(this.rotate * Math.PI / 180);
30788 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30790 this.canvasEl = document.createElement("canvas");
30792 this.contextEl = this.canvasEl.getContext("2d");
30794 switch (this.rotate) {
30797 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30798 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30800 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30805 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30806 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30808 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30809 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);
30813 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30818 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30819 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30821 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30822 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);
30826 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);
30831 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30832 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30834 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30835 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30839 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);
30846 this.previewEl.appendChild(this.canvasEl);
30848 this.setCanvasPosition();
30853 if(!this.canvasLoaded){
30857 var imageCanvas = document.createElement("canvas");
30859 var imageContext = imageCanvas.getContext("2d");
30861 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30862 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30864 var center = imageCanvas.width / 2;
30866 imageContext.translate(center, center);
30868 imageContext.rotate(this.rotate * Math.PI / 180);
30870 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30872 var canvas = document.createElement("canvas");
30874 var context = canvas.getContext("2d");
30876 canvas.width = this.minWidth;
30877 canvas.height = this.minHeight;
30879 switch (this.rotate) {
30882 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30883 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30885 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30886 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30888 var targetWidth = this.minWidth - 2 * x;
30889 var targetHeight = this.minHeight - 2 * y;
30893 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30894 scale = targetWidth / width;
30897 if(x > 0 && y == 0){
30898 scale = targetHeight / height;
30901 if(x > 0 && y > 0){
30902 scale = targetWidth / width;
30904 if(width < height){
30905 scale = targetHeight / height;
30909 context.scale(scale, scale);
30911 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30912 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30914 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30915 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30917 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30922 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30923 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30925 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30926 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30928 var targetWidth = this.minWidth - 2 * x;
30929 var targetHeight = this.minHeight - 2 * y;
30933 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30934 scale = targetWidth / width;
30937 if(x > 0 && y == 0){
30938 scale = targetHeight / height;
30941 if(x > 0 && y > 0){
30942 scale = targetWidth / width;
30944 if(width < height){
30945 scale = targetHeight / height;
30949 context.scale(scale, scale);
30951 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30952 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30954 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30955 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30957 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30959 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30964 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30965 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30967 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30968 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30970 var targetWidth = this.minWidth - 2 * x;
30971 var targetHeight = this.minHeight - 2 * y;
30975 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30976 scale = targetWidth / width;
30979 if(x > 0 && y == 0){
30980 scale = targetHeight / height;
30983 if(x > 0 && y > 0){
30984 scale = targetWidth / width;
30986 if(width < height){
30987 scale = targetHeight / height;
30991 context.scale(scale, scale);
30993 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30994 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30996 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30997 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30999 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31000 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31002 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31007 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31008 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31010 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31011 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31013 var targetWidth = this.minWidth - 2 * x;
31014 var targetHeight = this.minHeight - 2 * y;
31018 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31019 scale = targetWidth / width;
31022 if(x > 0 && y == 0){
31023 scale = targetHeight / height;
31026 if(x > 0 && y > 0){
31027 scale = targetWidth / width;
31029 if(width < height){
31030 scale = targetHeight / height;
31034 context.scale(scale, scale);
31036 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31037 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31039 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31040 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31042 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31044 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31051 this.cropData = canvas.toDataURL(this.cropType);
31053 if(this.fireEvent('crop', this, this.cropData) !== false){
31054 this.process(this.file, this.cropData);
31061 setThumbBoxSize : function()
31065 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31066 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31067 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31069 this.minWidth = width;
31070 this.minHeight = height;
31072 if(this.rotate == 90 || this.rotate == 270){
31073 this.minWidth = height;
31074 this.minHeight = width;
31079 width = Math.ceil(this.minWidth * height / this.minHeight);
31081 if(this.minWidth > this.minHeight){
31083 height = Math.ceil(this.minHeight * width / this.minWidth);
31086 this.thumbEl.setStyle({
31087 width : width + 'px',
31088 height : height + 'px'
31095 setThumbBoxPosition : function()
31097 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31098 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31100 this.thumbEl.setLeft(x);
31101 this.thumbEl.setTop(y);
31105 baseRotateLevel : function()
31107 this.baseRotate = 1;
31110 typeof(this.exif) != 'undefined' &&
31111 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31112 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31114 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31117 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31121 baseScaleLevel : function()
31125 if(this.isDocument){
31127 if(this.baseRotate == 6 || this.baseRotate == 8){
31129 height = this.thumbEl.getHeight();
31130 this.baseScale = height / this.imageEl.OriginWidth;
31132 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31133 width = this.thumbEl.getWidth();
31134 this.baseScale = width / this.imageEl.OriginHeight;
31140 height = this.thumbEl.getHeight();
31141 this.baseScale = height / this.imageEl.OriginHeight;
31143 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31144 width = this.thumbEl.getWidth();
31145 this.baseScale = width / this.imageEl.OriginWidth;
31151 if(this.baseRotate == 6 || this.baseRotate == 8){
31153 width = this.thumbEl.getHeight();
31154 this.baseScale = width / this.imageEl.OriginHeight;
31156 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31157 height = this.thumbEl.getWidth();
31158 this.baseScale = height / this.imageEl.OriginHeight;
31161 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31162 height = this.thumbEl.getWidth();
31163 this.baseScale = height / this.imageEl.OriginHeight;
31165 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31166 width = this.thumbEl.getHeight();
31167 this.baseScale = width / this.imageEl.OriginWidth;
31174 width = this.thumbEl.getWidth();
31175 this.baseScale = width / this.imageEl.OriginWidth;
31177 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31178 height = this.thumbEl.getHeight();
31179 this.baseScale = height / this.imageEl.OriginHeight;
31182 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31184 height = this.thumbEl.getHeight();
31185 this.baseScale = height / this.imageEl.OriginHeight;
31187 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31188 width = this.thumbEl.getWidth();
31189 this.baseScale = width / this.imageEl.OriginWidth;
31197 getScaleLevel : function()
31199 return this.baseScale * Math.pow(1.1, this.scale);
31202 onTouchStart : function(e)
31204 if(!this.canvasLoaded){
31205 this.beforeSelectFile(e);
31209 var touches = e.browserEvent.touches;
31215 if(touches.length == 1){
31216 this.onMouseDown(e);
31220 if(touches.length != 2){
31226 for(var i = 0, finger; finger = touches[i]; i++){
31227 coords.push(finger.pageX, finger.pageY);
31230 var x = Math.pow(coords[0] - coords[2], 2);
31231 var y = Math.pow(coords[1] - coords[3], 2);
31233 this.startDistance = Math.sqrt(x + y);
31235 this.startScale = this.scale;
31237 this.pinching = true;
31238 this.dragable = false;
31242 onTouchMove : function(e)
31244 if(!this.pinching && !this.dragable){
31248 var touches = e.browserEvent.touches;
31255 this.onMouseMove(e);
31261 for(var i = 0, finger; finger = touches[i]; i++){
31262 coords.push(finger.pageX, finger.pageY);
31265 var x = Math.pow(coords[0] - coords[2], 2);
31266 var y = Math.pow(coords[1] - coords[3], 2);
31268 this.endDistance = Math.sqrt(x + y);
31270 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31272 if(!this.zoomable()){
31273 this.scale = this.startScale;
31281 onTouchEnd : function(e)
31283 this.pinching = false;
31284 this.dragable = false;
31288 process : function(file, crop)
31291 this.maskEl.mask(this.loadingText);
31294 this.xhr = new XMLHttpRequest();
31296 file.xhr = this.xhr;
31298 this.xhr.open(this.method, this.url, true);
31301 "Accept": "application/json",
31302 "Cache-Control": "no-cache",
31303 "X-Requested-With": "XMLHttpRequest"
31306 for (var headerName in headers) {
31307 var headerValue = headers[headerName];
31309 this.xhr.setRequestHeader(headerName, headerValue);
31315 this.xhr.onload = function()
31317 _this.xhrOnLoad(_this.xhr);
31320 this.xhr.onerror = function()
31322 _this.xhrOnError(_this.xhr);
31325 var formData = new FormData();
31327 formData.append('returnHTML', 'NO');
31330 formData.append('crop', crop);
31333 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31334 formData.append(this.paramName, file, file.name);
31337 if(typeof(file.filename) != 'undefined'){
31338 formData.append('filename', file.filename);
31341 if(typeof(file.mimetype) != 'undefined'){
31342 formData.append('mimetype', file.mimetype);
31345 if(this.fireEvent('arrange', this, formData) != false){
31346 this.xhr.send(formData);
31350 xhrOnLoad : function(xhr)
31353 this.maskEl.unmask();
31356 if (xhr.readyState !== 4) {
31357 this.fireEvent('exception', this, xhr);
31361 var response = Roo.decode(xhr.responseText);
31363 if(!response.success){
31364 this.fireEvent('exception', this, xhr);
31368 var response = Roo.decode(xhr.responseText);
31370 this.fireEvent('upload', this, response);
31374 xhrOnError : function()
31377 this.maskEl.unmask();
31380 Roo.log('xhr on error');
31382 var response = Roo.decode(xhr.responseText);
31388 prepare : function(file)
31391 this.maskEl.mask(this.loadingText);
31397 if(typeof(file) === 'string'){
31398 this.loadCanvas(file);
31402 if(!file || !this.urlAPI){
31407 this.cropType = file.type;
31411 if(this.fireEvent('prepare', this, this.file) != false){
31413 var reader = new FileReader();
31415 reader.onload = function (e) {
31416 if (e.target.error) {
31417 Roo.log(e.target.error);
31421 var buffer = e.target.result,
31422 dataView = new DataView(buffer),
31424 maxOffset = dataView.byteLength - 4,
31428 if (dataView.getUint16(0) === 0xffd8) {
31429 while (offset < maxOffset) {
31430 markerBytes = dataView.getUint16(offset);
31432 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31433 markerLength = dataView.getUint16(offset + 2) + 2;
31434 if (offset + markerLength > dataView.byteLength) {
31435 Roo.log('Invalid meta data: Invalid segment size.');
31439 if(markerBytes == 0xffe1){
31440 _this.parseExifData(
31447 offset += markerLength;
31457 var url = _this.urlAPI.createObjectURL(_this.file);
31459 _this.loadCanvas(url);
31464 reader.readAsArrayBuffer(this.file);
31470 parseExifData : function(dataView, offset, length)
31472 var tiffOffset = offset + 10,
31476 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31477 // No Exif data, might be XMP data instead
31481 // Check for the ASCII code for "Exif" (0x45786966):
31482 if (dataView.getUint32(offset + 4) !== 0x45786966) {
31483 // No Exif data, might be XMP data instead
31486 if (tiffOffset + 8 > dataView.byteLength) {
31487 Roo.log('Invalid Exif data: Invalid segment size.');
31490 // Check for the two null bytes:
31491 if (dataView.getUint16(offset + 8) !== 0x0000) {
31492 Roo.log('Invalid Exif data: Missing byte alignment offset.');
31495 // Check the byte alignment:
31496 switch (dataView.getUint16(tiffOffset)) {
31498 littleEndian = true;
31501 littleEndian = false;
31504 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31507 // Check for the TIFF tag marker (0x002A):
31508 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31509 Roo.log('Invalid Exif data: Missing TIFF marker.');
31512 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31513 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31515 this.parseExifTags(
31518 tiffOffset + dirOffset,
31523 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31528 if (dirOffset + 6 > dataView.byteLength) {
31529 Roo.log('Invalid Exif data: Invalid directory offset.');
31532 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31533 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31534 if (dirEndOffset + 4 > dataView.byteLength) {
31535 Roo.log('Invalid Exif data: Invalid directory size.');
31538 for (i = 0; i < tagsNumber; i += 1) {
31542 dirOffset + 2 + 12 * i, // tag offset
31546 // Return the offset to the next directory:
31547 return dataView.getUint32(dirEndOffset, littleEndian);
31550 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
31552 var tag = dataView.getUint16(offset, littleEndian);
31554 this.exif[tag] = this.getExifValue(
31558 dataView.getUint16(offset + 2, littleEndian), // tag type
31559 dataView.getUint32(offset + 4, littleEndian), // tag length
31564 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31566 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31575 Roo.log('Invalid Exif data: Invalid tag type.');
31579 tagSize = tagType.size * length;
31580 // Determine if the value is contained in the dataOffset bytes,
31581 // or if the value at the dataOffset is a pointer to the actual data:
31582 dataOffset = tagSize > 4 ?
31583 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31584 if (dataOffset + tagSize > dataView.byteLength) {
31585 Roo.log('Invalid Exif data: Invalid data offset.');
31588 if (length === 1) {
31589 return tagType.getValue(dataView, dataOffset, littleEndian);
31592 for (i = 0; i < length; i += 1) {
31593 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31596 if (tagType.ascii) {
31598 // Concatenate the chars:
31599 for (i = 0; i < values.length; i += 1) {
31601 // Ignore the terminating NULL byte(s):
31602 if (c === '\u0000') {
31614 Roo.apply(Roo.bootstrap.UploadCropbox, {
31616 'Orientation': 0x0112
31620 1: 0, //'top-left',
31622 3: 180, //'bottom-right',
31623 // 4: 'bottom-left',
31625 6: 90, //'right-top',
31626 // 7: 'right-bottom',
31627 8: 270 //'left-bottom'
31631 // byte, 8-bit unsigned int:
31633 getValue: function (dataView, dataOffset) {
31634 return dataView.getUint8(dataOffset);
31638 // ascii, 8-bit byte:
31640 getValue: function (dataView, dataOffset) {
31641 return String.fromCharCode(dataView.getUint8(dataOffset));
31646 // short, 16 bit int:
31648 getValue: function (dataView, dataOffset, littleEndian) {
31649 return dataView.getUint16(dataOffset, littleEndian);
31653 // long, 32 bit int:
31655 getValue: function (dataView, dataOffset, littleEndian) {
31656 return dataView.getUint32(dataOffset, littleEndian);
31660 // rational = two long values, first is numerator, second is denominator:
31662 getValue: function (dataView, dataOffset, littleEndian) {
31663 return dataView.getUint32(dataOffset, littleEndian) /
31664 dataView.getUint32(dataOffset + 4, littleEndian);
31668 // slong, 32 bit signed int:
31670 getValue: function (dataView, dataOffset, littleEndian) {
31671 return dataView.getInt32(dataOffset, littleEndian);
31675 // srational, two slongs, first is numerator, second is denominator:
31677 getValue: function (dataView, dataOffset, littleEndian) {
31678 return dataView.getInt32(dataOffset, littleEndian) /
31679 dataView.getInt32(dataOffset + 4, littleEndian);
31689 cls : 'btn-group roo-upload-cropbox-rotate-left',
31690 action : 'rotate-left',
31694 cls : 'btn btn-default',
31695 html : '<i class="fa fa-undo"></i>'
31701 cls : 'btn-group roo-upload-cropbox-picture',
31702 action : 'picture',
31706 cls : 'btn btn-default',
31707 html : '<i class="fa fa-picture-o"></i>'
31713 cls : 'btn-group roo-upload-cropbox-rotate-right',
31714 action : 'rotate-right',
31718 cls : 'btn btn-default',
31719 html : '<i class="fa fa-repeat"></i>'
31727 cls : 'btn-group roo-upload-cropbox-rotate-left',
31728 action : 'rotate-left',
31732 cls : 'btn btn-default',
31733 html : '<i class="fa fa-undo"></i>'
31739 cls : 'btn-group roo-upload-cropbox-download',
31740 action : 'download',
31744 cls : 'btn btn-default',
31745 html : '<i class="fa fa-download"></i>'
31751 cls : 'btn-group roo-upload-cropbox-crop',
31756 cls : 'btn btn-default',
31757 html : '<i class="fa fa-crop"></i>'
31763 cls : 'btn-group roo-upload-cropbox-trash',
31768 cls : 'btn btn-default',
31769 html : '<i class="fa fa-trash"></i>'
31775 cls : 'btn-group roo-upload-cropbox-rotate-right',
31776 action : 'rotate-right',
31780 cls : 'btn btn-default',
31781 html : '<i class="fa fa-repeat"></i>'
31789 cls : 'btn-group roo-upload-cropbox-rotate-left',
31790 action : 'rotate-left',
31794 cls : 'btn btn-default',
31795 html : '<i class="fa fa-undo"></i>'
31801 cls : 'btn-group roo-upload-cropbox-rotate-right',
31802 action : 'rotate-right',
31806 cls : 'btn btn-default',
31807 html : '<i class="fa fa-repeat"></i>'
31820 * @class Roo.bootstrap.DocumentManager
31821 * @extends Roo.bootstrap.Component
31822 * Bootstrap DocumentManager class
31823 * @cfg {String} paramName default 'imageUpload'
31824 * @cfg {String} toolTipName default 'filename'
31825 * @cfg {String} method default POST
31826 * @cfg {String} url action url
31827 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31828 * @cfg {Boolean} multiple multiple upload default true
31829 * @cfg {Number} thumbSize default 300
31830 * @cfg {String} fieldLabel
31831 * @cfg {Number} labelWidth default 4
31832 * @cfg {String} labelAlign (left|top) default left
31833 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31834 * @cfg {Number} labellg set the width of label (1-12)
31835 * @cfg {Number} labelmd set the width of label (1-12)
31836 * @cfg {Number} labelsm set the width of label (1-12)
31837 * @cfg {Number} labelxs set the width of label (1-12)
31840 * Create a new DocumentManager
31841 * @param {Object} config The config object
31844 Roo.bootstrap.DocumentManager = function(config){
31845 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31848 this.delegates = [];
31853 * Fire when initial the DocumentManager
31854 * @param {Roo.bootstrap.DocumentManager} this
31859 * inspect selected file
31860 * @param {Roo.bootstrap.DocumentManager} this
31861 * @param {File} file
31866 * Fire when xhr load exception
31867 * @param {Roo.bootstrap.DocumentManager} this
31868 * @param {XMLHttpRequest} xhr
31870 "exception" : true,
31872 * @event afterupload
31873 * Fire when xhr load exception
31874 * @param {Roo.bootstrap.DocumentManager} this
31875 * @param {XMLHttpRequest} xhr
31877 "afterupload" : true,
31880 * prepare the form data
31881 * @param {Roo.bootstrap.DocumentManager} this
31882 * @param {Object} formData
31887 * Fire when remove the file
31888 * @param {Roo.bootstrap.DocumentManager} this
31889 * @param {Object} file
31894 * Fire after refresh the file
31895 * @param {Roo.bootstrap.DocumentManager} this
31900 * Fire after click the image
31901 * @param {Roo.bootstrap.DocumentManager} this
31902 * @param {Object} file
31907 * Fire when upload a image and editable set to true
31908 * @param {Roo.bootstrap.DocumentManager} this
31909 * @param {Object} file
31913 * @event beforeselectfile
31914 * Fire before select file
31915 * @param {Roo.bootstrap.DocumentManager} this
31917 "beforeselectfile" : true,
31920 * Fire before process file
31921 * @param {Roo.bootstrap.DocumentManager} this
31922 * @param {Object} file
31926 * @event previewrendered
31927 * Fire when preview rendered
31928 * @param {Roo.bootstrap.DocumentManager} this
31929 * @param {Object} file
31931 "previewrendered" : true,
31934 "previewResize" : true
31939 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
31948 paramName : 'imageUpload',
31949 toolTipName : 'filename',
31952 labelAlign : 'left',
31962 getAutoCreate : function()
31964 var managerWidget = {
31966 cls : 'roo-document-manager',
31970 cls : 'roo-document-manager-selector',
31975 cls : 'roo-document-manager-uploader',
31979 cls : 'roo-document-manager-upload-btn',
31980 html : '<i class="fa fa-plus"></i>'
31991 cls : 'column col-md-12',
31996 if(this.fieldLabel.length){
32001 cls : 'column col-md-12',
32002 html : this.fieldLabel
32006 cls : 'column col-md-12',
32011 if(this.labelAlign == 'left'){
32016 html : this.fieldLabel
32025 if(this.labelWidth > 12){
32026 content[0].style = "width: " + this.labelWidth + 'px';
32029 if(this.labelWidth < 13 && this.labelmd == 0){
32030 this.labelmd = this.labelWidth;
32033 if(this.labellg > 0){
32034 content[0].cls += ' col-lg-' + this.labellg;
32035 content[1].cls += ' col-lg-' + (12 - this.labellg);
32038 if(this.labelmd > 0){
32039 content[0].cls += ' col-md-' + this.labelmd;
32040 content[1].cls += ' col-md-' + (12 - this.labelmd);
32043 if(this.labelsm > 0){
32044 content[0].cls += ' col-sm-' + this.labelsm;
32045 content[1].cls += ' col-sm-' + (12 - this.labelsm);
32048 if(this.labelxs > 0){
32049 content[0].cls += ' col-xs-' + this.labelxs;
32050 content[1].cls += ' col-xs-' + (12 - this.labelxs);
32058 cls : 'row clearfix',
32066 initEvents : function()
32068 this.managerEl = this.el.select('.roo-document-manager', true).first();
32069 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32071 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32072 this.selectorEl.hide();
32075 this.selectorEl.attr('multiple', 'multiple');
32078 this.selectorEl.on('change', this.onFileSelected, this);
32080 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32081 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32083 this.uploader.on('click', this.onUploaderClick, this);
32085 this.renderProgressDialog();
32089 window.addEventListener("resize", function() { _this.refresh(); } );
32091 this.fireEvent('initial', this);
32094 renderProgressDialog : function()
32098 this.progressDialog = new Roo.bootstrap.Modal({
32099 cls : 'roo-document-manager-progress-dialog',
32100 allow_close : false,
32111 btnclick : function() {
32112 _this.uploadCancel();
32118 this.progressDialog.render(Roo.get(document.body));
32120 this.progress = new Roo.bootstrap.Progress({
32121 cls : 'roo-document-manager-progress',
32126 this.progress.render(this.progressDialog.getChildContainer());
32128 this.progressBar = new Roo.bootstrap.ProgressBar({
32129 cls : 'roo-document-manager-progress-bar',
32132 aria_valuemax : 12,
32136 this.progressBar.render(this.progress.getChildContainer());
32139 onUploaderClick : function(e)
32141 e.preventDefault();
32143 if(this.fireEvent('beforeselectfile', this) != false){
32144 this.selectorEl.dom.click();
32149 onFileSelected : function(e)
32151 e.preventDefault();
32153 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32157 Roo.each(this.selectorEl.dom.files, function(file){
32158 if(this.fireEvent('inspect', this, file) != false){
32159 this.files.push(file);
32169 this.selectorEl.dom.value = '';
32171 if(!this.files || !this.files.length){
32175 if(this.boxes > 0 && this.files.length > this.boxes){
32176 this.files = this.files.slice(0, this.boxes);
32179 this.uploader.show();
32181 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32182 this.uploader.hide();
32191 Roo.each(this.files, function(file){
32193 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32194 var f = this.renderPreview(file);
32199 if(file.type.indexOf('image') != -1){
32200 this.delegates.push(
32202 _this.process(file);
32203 }).createDelegate(this)
32211 _this.process(file);
32212 }).createDelegate(this)
32217 this.files = files;
32219 this.delegates = this.delegates.concat(docs);
32221 if(!this.delegates.length){
32226 this.progressBar.aria_valuemax = this.delegates.length;
32233 arrange : function()
32235 if(!this.delegates.length){
32236 this.progressDialog.hide();
32241 var delegate = this.delegates.shift();
32243 this.progressDialog.show();
32245 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32247 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32252 refresh : function()
32254 this.uploader.show();
32256 if(this.boxes > 0 && this.files.length > this.boxes - 1){
32257 this.uploader.hide();
32260 Roo.isTouch ? this.closable(false) : this.closable(true);
32262 this.fireEvent('refresh', this);
32265 onRemove : function(e, el, o)
32267 e.preventDefault();
32269 this.fireEvent('remove', this, o);
32273 remove : function(o)
32277 Roo.each(this.files, function(file){
32278 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32287 this.files = files;
32294 Roo.each(this.files, function(file){
32299 file.target.remove();
32308 onClick : function(e, el, o)
32310 e.preventDefault();
32312 this.fireEvent('click', this, o);
32316 closable : function(closable)
32318 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32320 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32332 xhrOnLoad : function(xhr)
32334 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32338 if (xhr.readyState !== 4) {
32340 this.fireEvent('exception', this, xhr);
32344 var response = Roo.decode(xhr.responseText);
32346 if(!response.success){
32348 this.fireEvent('exception', this, xhr);
32352 var file = this.renderPreview(response.data);
32354 this.files.push(file);
32358 this.fireEvent('afterupload', this, xhr);
32362 xhrOnError : function(xhr)
32364 Roo.log('xhr on error');
32366 var response = Roo.decode(xhr.responseText);
32373 process : function(file)
32375 if(this.fireEvent('process', this, file) !== false){
32376 if(this.editable && file.type.indexOf('image') != -1){
32377 this.fireEvent('edit', this, file);
32381 this.uploadStart(file, false);
32388 uploadStart : function(file, crop)
32390 this.xhr = new XMLHttpRequest();
32392 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32397 file.xhr = this.xhr;
32399 this.managerEl.createChild({
32401 cls : 'roo-document-manager-loading',
32405 tooltip : file.name,
32406 cls : 'roo-document-manager-thumb',
32407 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32413 this.xhr.open(this.method, this.url, true);
32416 "Accept": "application/json",
32417 "Cache-Control": "no-cache",
32418 "X-Requested-With": "XMLHttpRequest"
32421 for (var headerName in headers) {
32422 var headerValue = headers[headerName];
32424 this.xhr.setRequestHeader(headerName, headerValue);
32430 this.xhr.onload = function()
32432 _this.xhrOnLoad(_this.xhr);
32435 this.xhr.onerror = function()
32437 _this.xhrOnError(_this.xhr);
32440 var formData = new FormData();
32442 formData.append('returnHTML', 'NO');
32445 formData.append('crop', crop);
32448 formData.append(this.paramName, file, file.name);
32455 if(this.fireEvent('prepare', this, formData, options) != false){
32457 if(options.manually){
32461 this.xhr.send(formData);
32465 this.uploadCancel();
32468 uploadCancel : function()
32474 this.delegates = [];
32476 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32483 renderPreview : function(file)
32485 if(typeof(file.target) != 'undefined' && file.target){
32489 var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32491 var previewEl = this.managerEl.createChild({
32493 cls : 'roo-document-manager-preview',
32497 tooltip : file[this.toolTipName],
32498 cls : 'roo-document-manager-thumb',
32499 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32504 html : '<i class="fa fa-times-circle"></i>'
32509 var close = previewEl.select('button.close', true).first();
32511 close.on('click', this.onRemove, this, file);
32513 file.target = previewEl;
32515 var image = previewEl.select('img', true).first();
32519 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32521 image.on('click', this.onClick, this, file);
32523 this.fireEvent('previewrendered', this, file);
32529 onPreviewLoad : function(file, image)
32531 if(typeof(file.target) == 'undefined' || !file.target){
32535 var width = image.dom.naturalWidth || image.dom.width;
32536 var height = image.dom.naturalHeight || image.dom.height;
32538 if(!this.previewResize) {
32542 if(width > height){
32543 file.target.addClass('wide');
32547 file.target.addClass('tall');
32552 uploadFromSource : function(file, crop)
32554 this.xhr = new XMLHttpRequest();
32556 this.managerEl.createChild({
32558 cls : 'roo-document-manager-loading',
32562 tooltip : file.name,
32563 cls : 'roo-document-manager-thumb',
32564 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32570 this.xhr.open(this.method, this.url, true);
32573 "Accept": "application/json",
32574 "Cache-Control": "no-cache",
32575 "X-Requested-With": "XMLHttpRequest"
32578 for (var headerName in headers) {
32579 var headerValue = headers[headerName];
32581 this.xhr.setRequestHeader(headerName, headerValue);
32587 this.xhr.onload = function()
32589 _this.xhrOnLoad(_this.xhr);
32592 this.xhr.onerror = function()
32594 _this.xhrOnError(_this.xhr);
32597 var formData = new FormData();
32599 formData.append('returnHTML', 'NO');
32601 formData.append('crop', crop);
32603 if(typeof(file.filename) != 'undefined'){
32604 formData.append('filename', file.filename);
32607 if(typeof(file.mimetype) != 'undefined'){
32608 formData.append('mimetype', file.mimetype);
32613 if(this.fireEvent('prepare', this, formData) != false){
32614 this.xhr.send(formData);
32624 * @class Roo.bootstrap.DocumentViewer
32625 * @extends Roo.bootstrap.Component
32626 * Bootstrap DocumentViewer class
32627 * @cfg {Boolean} showDownload (true|false) show download button (default true)
32628 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32631 * Create a new DocumentViewer
32632 * @param {Object} config The config object
32635 Roo.bootstrap.DocumentViewer = function(config){
32636 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32641 * Fire after initEvent
32642 * @param {Roo.bootstrap.DocumentViewer} this
32648 * @param {Roo.bootstrap.DocumentViewer} this
32653 * Fire after download button
32654 * @param {Roo.bootstrap.DocumentViewer} this
32659 * Fire after trash button
32660 * @param {Roo.bootstrap.DocumentViewer} this
32667 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
32669 showDownload : true,
32673 getAutoCreate : function()
32677 cls : 'roo-document-viewer',
32681 cls : 'roo-document-viewer-body',
32685 cls : 'roo-document-viewer-thumb',
32689 cls : 'roo-document-viewer-image'
32697 cls : 'roo-document-viewer-footer',
32700 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32704 cls : 'btn-group roo-document-viewer-download',
32708 cls : 'btn btn-default',
32709 html : '<i class="fa fa-download"></i>'
32715 cls : 'btn-group roo-document-viewer-trash',
32719 cls : 'btn btn-default',
32720 html : '<i class="fa fa-trash"></i>'
32733 initEvents : function()
32735 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32736 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32738 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32739 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32741 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32742 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32744 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32745 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32747 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32748 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32750 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32751 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32753 this.bodyEl.on('click', this.onClick, this);
32754 this.downloadBtn.on('click', this.onDownload, this);
32755 this.trashBtn.on('click', this.onTrash, this);
32757 this.downloadBtn.hide();
32758 this.trashBtn.hide();
32760 if(this.showDownload){
32761 this.downloadBtn.show();
32764 if(this.showTrash){
32765 this.trashBtn.show();
32768 if(!this.showDownload && !this.showTrash) {
32769 this.footerEl.hide();
32774 initial : function()
32776 this.fireEvent('initial', this);
32780 onClick : function(e)
32782 e.preventDefault();
32784 this.fireEvent('click', this);
32787 onDownload : function(e)
32789 e.preventDefault();
32791 this.fireEvent('download', this);
32794 onTrash : function(e)
32796 e.preventDefault();
32798 this.fireEvent('trash', this);
32810 * @class Roo.bootstrap.NavProgressBar
32811 * @extends Roo.bootstrap.Component
32812 * Bootstrap NavProgressBar class
32815 * Create a new nav progress bar
32816 * @param {Object} config The config object
32819 Roo.bootstrap.NavProgressBar = function(config){
32820 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32822 this.bullets = this.bullets || [];
32824 // Roo.bootstrap.NavProgressBar.register(this);
32828 * Fires when the active item changes
32829 * @param {Roo.bootstrap.NavProgressBar} this
32830 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32831 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
32838 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
32843 getAutoCreate : function()
32845 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32849 cls : 'roo-navigation-bar-group',
32853 cls : 'roo-navigation-top-bar'
32857 cls : 'roo-navigation-bullets-bar',
32861 cls : 'roo-navigation-bar'
32868 cls : 'roo-navigation-bottom-bar'
32878 initEvents: function()
32883 onRender : function(ct, position)
32885 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32887 if(this.bullets.length){
32888 Roo.each(this.bullets, function(b){
32897 addItem : function(cfg)
32899 var item = new Roo.bootstrap.NavProgressItem(cfg);
32901 item.parentId = this.id;
32902 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32905 var top = new Roo.bootstrap.Element({
32907 cls : 'roo-navigation-bar-text'
32910 var bottom = new Roo.bootstrap.Element({
32912 cls : 'roo-navigation-bar-text'
32915 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32916 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32918 var topText = new Roo.bootstrap.Element({
32920 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32923 var bottomText = new Roo.bootstrap.Element({
32925 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32928 topText.onRender(top.el, null);
32929 bottomText.onRender(bottom.el, null);
32932 item.bottomEl = bottom;
32935 this.barItems.push(item);
32940 getActive : function()
32942 var active = false;
32944 Roo.each(this.barItems, function(v){
32946 if (!v.isActive()) {
32958 setActiveItem : function(item)
32962 Roo.each(this.barItems, function(v){
32963 if (v.rid == item.rid) {
32967 if (v.isActive()) {
32968 v.setActive(false);
32973 item.setActive(true);
32975 this.fireEvent('changed', this, item, prev);
32978 getBarItem: function(rid)
32982 Roo.each(this.barItems, function(e) {
32983 if (e.rid != rid) {
32994 indexOfItem : function(item)
32998 Roo.each(this.barItems, function(v, i){
33000 if (v.rid != item.rid) {
33011 setActiveNext : function()
33013 var i = this.indexOfItem(this.getActive());
33015 if (i > this.barItems.length) {
33019 this.setActiveItem(this.barItems[i+1]);
33022 setActivePrev : function()
33024 var i = this.indexOfItem(this.getActive());
33030 this.setActiveItem(this.barItems[i-1]);
33033 format : function()
33035 if(!this.barItems.length){
33039 var width = 100 / this.barItems.length;
33041 Roo.each(this.barItems, function(i){
33042 i.el.setStyle('width', width + '%');
33043 i.topEl.el.setStyle('width', width + '%');
33044 i.bottomEl.el.setStyle('width', width + '%');
33053 * Nav Progress Item
33058 * @class Roo.bootstrap.NavProgressItem
33059 * @extends Roo.bootstrap.Component
33060 * Bootstrap NavProgressItem class
33061 * @cfg {String} rid the reference id
33062 * @cfg {Boolean} active (true|false) Is item active default false
33063 * @cfg {Boolean} disabled (true|false) Is item active default false
33064 * @cfg {String} html
33065 * @cfg {String} position (top|bottom) text position default bottom
33066 * @cfg {String} icon show icon instead of number
33069 * Create a new NavProgressItem
33070 * @param {Object} config The config object
33072 Roo.bootstrap.NavProgressItem = function(config){
33073 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33078 * The raw click event for the entire grid.
33079 * @param {Roo.bootstrap.NavProgressItem} this
33080 * @param {Roo.EventObject} e
33087 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
33093 position : 'bottom',
33096 getAutoCreate : function()
33098 var iconCls = 'roo-navigation-bar-item-icon';
33100 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33104 cls: 'roo-navigation-bar-item',
33114 cfg.cls += ' active';
33117 cfg.cls += ' disabled';
33123 disable : function()
33125 this.setDisabled(true);
33128 enable : function()
33130 this.setDisabled(false);
33133 initEvents: function()
33135 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33137 this.iconEl.on('click', this.onClick, this);
33140 onClick : function(e)
33142 e.preventDefault();
33148 if(this.fireEvent('click', this, e) === false){
33152 this.parent().setActiveItem(this);
33155 isActive: function ()
33157 return this.active;
33160 setActive : function(state)
33162 if(this.active == state){
33166 this.active = state;
33169 this.el.addClass('active');
33173 this.el.removeClass('active');
33178 setDisabled : function(state)
33180 if(this.disabled == state){
33184 this.disabled = state;
33187 this.el.addClass('disabled');
33191 this.el.removeClass('disabled');
33194 tooltipEl : function()
33196 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33209 * @class Roo.bootstrap.FieldLabel
33210 * @extends Roo.bootstrap.Component
33211 * Bootstrap FieldLabel class
33212 * @cfg {String} html contents of the element
33213 * @cfg {String} tag tag of the element default label
33214 * @cfg {String} cls class of the element
33215 * @cfg {String} target label target
33216 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33217 * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33218 * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33219 * @cfg {String} iconTooltip default "This field is required"
33220 * @cfg {String} indicatorpos (left|right) default left
33223 * Create a new FieldLabel
33224 * @param {Object} config The config object
33227 Roo.bootstrap.FieldLabel = function(config){
33228 Roo.bootstrap.Element.superclass.constructor.call(this, config);
33233 * Fires after the field has been marked as invalid.
33234 * @param {Roo.form.FieldLabel} this
33235 * @param {String} msg The validation message
33240 * Fires after the field has been validated with no errors.
33241 * @param {Roo.form.FieldLabel} this
33247 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
33254 invalidClass : 'has-warning',
33255 validClass : 'has-success',
33256 iconTooltip : 'This field is required',
33257 indicatorpos : 'left',
33259 getAutoCreate : function(){
33262 if (!this.allowBlank) {
33268 cls : 'roo-bootstrap-field-label ' + this.cls,
33273 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33274 tooltip : this.iconTooltip
33283 if(this.indicatorpos == 'right'){
33286 cls : 'roo-bootstrap-field-label ' + this.cls,
33295 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33296 tooltip : this.iconTooltip
33305 initEvents: function()
33307 Roo.bootstrap.Element.superclass.initEvents.call(this);
33309 this.indicator = this.indicatorEl();
33311 if(this.indicator){
33312 this.indicator.removeClass('visible');
33313 this.indicator.addClass('invisible');
33316 Roo.bootstrap.FieldLabel.register(this);
33319 indicatorEl : function()
33321 var indicator = this.el.select('i.roo-required-indicator',true).first();
33332 * Mark this field as valid
33334 markValid : function()
33336 if(this.indicator){
33337 this.indicator.removeClass('visible');
33338 this.indicator.addClass('invisible');
33340 if (Roo.bootstrap.version == 3) {
33341 this.el.removeClass(this.invalidClass);
33342 this.el.addClass(this.validClass);
33344 this.el.removeClass('is-invalid');
33345 this.el.addClass('is-valid');
33349 this.fireEvent('valid', this);
33353 * Mark this field as invalid
33354 * @param {String} msg The validation message
33356 markInvalid : function(msg)
33358 if(this.indicator){
33359 this.indicator.removeClass('invisible');
33360 this.indicator.addClass('visible');
33362 if (Roo.bootstrap.version == 3) {
33363 this.el.removeClass(this.validClass);
33364 this.el.addClass(this.invalidClass);
33366 this.el.removeClass('is-valid');
33367 this.el.addClass('is-invalid');
33371 this.fireEvent('invalid', this, msg);
33377 Roo.apply(Roo.bootstrap.FieldLabel, {
33382 * register a FieldLabel Group
33383 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33385 register : function(label)
33387 if(this.groups.hasOwnProperty(label.target)){
33391 this.groups[label.target] = label;
33395 * fetch a FieldLabel Group based on the target
33396 * @param {string} target
33397 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33399 get: function(target) {
33400 if (typeof(this.groups[target]) == 'undefined') {
33404 return this.groups[target] ;
33413 * page DateSplitField.
33419 * @class Roo.bootstrap.DateSplitField
33420 * @extends Roo.bootstrap.Component
33421 * Bootstrap DateSplitField class
33422 * @cfg {string} fieldLabel - the label associated
33423 * @cfg {Number} labelWidth set the width of label (0-12)
33424 * @cfg {String} labelAlign (top|left)
33425 * @cfg {Boolean} dayAllowBlank (true|false) default false
33426 * @cfg {Boolean} monthAllowBlank (true|false) default false
33427 * @cfg {Boolean} yearAllowBlank (true|false) default false
33428 * @cfg {string} dayPlaceholder
33429 * @cfg {string} monthPlaceholder
33430 * @cfg {string} yearPlaceholder
33431 * @cfg {string} dayFormat default 'd'
33432 * @cfg {string} monthFormat default 'm'
33433 * @cfg {string} yearFormat default 'Y'
33434 * @cfg {Number} labellg set the width of label (1-12)
33435 * @cfg {Number} labelmd set the width of label (1-12)
33436 * @cfg {Number} labelsm set the width of label (1-12)
33437 * @cfg {Number} labelxs set the width of label (1-12)
33441 * Create a new DateSplitField
33442 * @param {Object} config The config object
33445 Roo.bootstrap.DateSplitField = function(config){
33446 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33452 * getting the data of years
33453 * @param {Roo.bootstrap.DateSplitField} this
33454 * @param {Object} years
33459 * getting the data of days
33460 * @param {Roo.bootstrap.DateSplitField} this
33461 * @param {Object} days
33466 * Fires after the field has been marked as invalid.
33467 * @param {Roo.form.Field} this
33468 * @param {String} msg The validation message
33473 * Fires after the field has been validated with no errors.
33474 * @param {Roo.form.Field} this
33480 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
33483 labelAlign : 'top',
33485 dayAllowBlank : false,
33486 monthAllowBlank : false,
33487 yearAllowBlank : false,
33488 dayPlaceholder : '',
33489 monthPlaceholder : '',
33490 yearPlaceholder : '',
33494 isFormField : true,
33500 getAutoCreate : function()
33504 cls : 'row roo-date-split-field-group',
33509 cls : 'form-hidden-field roo-date-split-field-group-value',
33515 var labelCls = 'col-md-12';
33516 var contentCls = 'col-md-4';
33518 if(this.fieldLabel){
33522 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33526 html : this.fieldLabel
33531 if(this.labelAlign == 'left'){
33533 if(this.labelWidth > 12){
33534 label.style = "width: " + this.labelWidth + 'px';
33537 if(this.labelWidth < 13 && this.labelmd == 0){
33538 this.labelmd = this.labelWidth;
33541 if(this.labellg > 0){
33542 labelCls = ' col-lg-' + this.labellg;
33543 contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33546 if(this.labelmd > 0){
33547 labelCls = ' col-md-' + this.labelmd;
33548 contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33551 if(this.labelsm > 0){
33552 labelCls = ' col-sm-' + this.labelsm;
33553 contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33556 if(this.labelxs > 0){
33557 labelCls = ' col-xs-' + this.labelxs;
33558 contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33562 label.cls += ' ' + labelCls;
33564 cfg.cn.push(label);
33567 Roo.each(['day', 'month', 'year'], function(t){
33570 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33577 inputEl: function ()
33579 return this.el.select('.roo-date-split-field-group-value', true).first();
33582 onRender : function(ct, position)
33586 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33588 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33590 this.dayField = new Roo.bootstrap.ComboBox({
33591 allowBlank : this.dayAllowBlank,
33592 alwaysQuery : true,
33593 displayField : 'value',
33596 forceSelection : true,
33598 placeholder : this.dayPlaceholder,
33599 selectOnFocus : true,
33600 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33601 triggerAction : 'all',
33603 valueField : 'value',
33604 store : new Roo.data.SimpleStore({
33605 data : (function() {
33607 _this.fireEvent('days', _this, days);
33610 fields : [ 'value' ]
33613 select : function (_self, record, index)
33615 _this.setValue(_this.getValue());
33620 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33622 this.monthField = new Roo.bootstrap.MonthField({
33623 after : '<i class=\"fa fa-calendar\"></i>',
33624 allowBlank : this.monthAllowBlank,
33625 placeholder : this.monthPlaceholder,
33628 render : function (_self)
33630 this.el.select('span.input-group-addon', true).first().on('click', function(e){
33631 e.preventDefault();
33635 select : function (_self, oldvalue, newvalue)
33637 _this.setValue(_this.getValue());
33642 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33644 this.yearField = new Roo.bootstrap.ComboBox({
33645 allowBlank : this.yearAllowBlank,
33646 alwaysQuery : true,
33647 displayField : 'value',
33650 forceSelection : true,
33652 placeholder : this.yearPlaceholder,
33653 selectOnFocus : true,
33654 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33655 triggerAction : 'all',
33657 valueField : 'value',
33658 store : new Roo.data.SimpleStore({
33659 data : (function() {
33661 _this.fireEvent('years', _this, years);
33664 fields : [ 'value' ]
33667 select : function (_self, record, index)
33669 _this.setValue(_this.getValue());
33674 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33677 setValue : function(v, format)
33679 this.inputEl.dom.value = v;
33681 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33683 var d = Date.parseDate(v, f);
33690 this.setDay(d.format(this.dayFormat));
33691 this.setMonth(d.format(this.monthFormat));
33692 this.setYear(d.format(this.yearFormat));
33699 setDay : function(v)
33701 this.dayField.setValue(v);
33702 this.inputEl.dom.value = this.getValue();
33707 setMonth : function(v)
33709 this.monthField.setValue(v, true);
33710 this.inputEl.dom.value = this.getValue();
33715 setYear : function(v)
33717 this.yearField.setValue(v);
33718 this.inputEl.dom.value = this.getValue();
33723 getDay : function()
33725 return this.dayField.getValue();
33728 getMonth : function()
33730 return this.monthField.getValue();
33733 getYear : function()
33735 return this.yearField.getValue();
33738 getValue : function()
33740 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33742 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33752 this.inputEl.dom.value = '';
33757 validate : function()
33759 var d = this.dayField.validate();
33760 var m = this.monthField.validate();
33761 var y = this.yearField.validate();
33766 (!this.dayAllowBlank && !d) ||
33767 (!this.monthAllowBlank && !m) ||
33768 (!this.yearAllowBlank && !y)
33773 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33782 this.markInvalid();
33787 markValid : function()
33790 var label = this.el.select('label', true).first();
33791 var icon = this.el.select('i.fa-star', true).first();
33797 this.fireEvent('valid', this);
33801 * Mark this field as invalid
33802 * @param {String} msg The validation message
33804 markInvalid : function(msg)
33807 var label = this.el.select('label', true).first();
33808 var icon = this.el.select('i.fa-star', true).first();
33810 if(label && !icon){
33811 this.el.select('.roo-date-split-field-label', true).createChild({
33813 cls : 'text-danger fa fa-lg fa-star',
33814 tooltip : 'This field is required',
33815 style : 'margin-right:5px;'
33819 this.fireEvent('invalid', this, msg);
33822 clearInvalid : function()
33824 var label = this.el.select('label', true).first();
33825 var icon = this.el.select('i.fa-star', true).first();
33831 this.fireEvent('valid', this);
33834 getName: function()
33844 * http://masonry.desandro.com
33846 * The idea is to render all the bricks based on vertical width...
33848 * The original code extends 'outlayer' - we might need to use that....
33854 * @class Roo.bootstrap.LayoutMasonry
33855 * @extends Roo.bootstrap.Component
33856 * Bootstrap Layout Masonry class
33859 * Create a new Element
33860 * @param {Object} config The config object
33863 Roo.bootstrap.LayoutMasonry = function(config){
33865 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33869 Roo.bootstrap.LayoutMasonry.register(this);
33875 * Fire after layout the items
33876 * @param {Roo.bootstrap.LayoutMasonry} this
33877 * @param {Roo.EventObject} e
33884 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
33887 * @cfg {Boolean} isLayoutInstant = no animation?
33889 isLayoutInstant : false, // needed?
33892 * @cfg {Number} boxWidth width of the columns
33897 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
33902 * @cfg {Number} padWidth padding below box..
33907 * @cfg {Number} gutter gutter width..
33912 * @cfg {Number} maxCols maximum number of columns
33918 * @cfg {Boolean} isAutoInitial defalut true
33920 isAutoInitial : true,
33925 * @cfg {Boolean} isHorizontal defalut false
33927 isHorizontal : false,
33929 currentSize : null,
33935 bricks: null, //CompositeElement
33939 _isLayoutInited : false,
33941 // isAlternative : false, // only use for vertical layout...
33944 * @cfg {Number} alternativePadWidth padding below box..
33946 alternativePadWidth : 50,
33948 selectedBrick : [],
33950 getAutoCreate : function(){
33952 var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33956 cls: 'blog-masonary-wrapper ' + this.cls,
33958 cls : 'mas-boxes masonary'
33965 getChildContainer: function( )
33967 if (this.boxesEl) {
33968 return this.boxesEl;
33971 this.boxesEl = this.el.select('.mas-boxes').first();
33973 return this.boxesEl;
33977 initEvents : function()
33981 if(this.isAutoInitial){
33982 Roo.log('hook children rendered');
33983 this.on('childrenrendered', function() {
33984 Roo.log('children rendered');
33990 initial : function()
33992 this.selectedBrick = [];
33994 this.currentSize = this.el.getBox(true);
33996 Roo.EventManager.onWindowResize(this.resize, this);
33998 if(!this.isAutoInitial){
34006 //this.layout.defer(500,this);
34010 resize : function()
34012 var cs = this.el.getBox(true);
34015 this.currentSize.width == cs.width &&
34016 this.currentSize.x == cs.x &&
34017 this.currentSize.height == cs.height &&
34018 this.currentSize.y == cs.y
34020 Roo.log("no change in with or X or Y");
34024 this.currentSize = cs;
34030 layout : function()
34032 this._resetLayout();
34034 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34036 this.layoutItems( isInstant );
34038 this._isLayoutInited = true;
34040 this.fireEvent('layout', this);
34044 _resetLayout : function()
34046 if(this.isHorizontal){
34047 this.horizontalMeasureColumns();
34051 this.verticalMeasureColumns();
34055 verticalMeasureColumns : function()
34057 this.getContainerWidth();
34059 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34060 // this.colWidth = Math.floor(this.containerWidth * 0.8);
34064 var boxWidth = this.boxWidth + this.padWidth;
34066 if(this.containerWidth < this.boxWidth){
34067 boxWidth = this.containerWidth
34070 var containerWidth = this.containerWidth;
34072 var cols = Math.floor(containerWidth / boxWidth);
34074 this.cols = Math.max( cols, 1 );
34076 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34078 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34080 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34082 this.colWidth = boxWidth + avail - this.padWidth;
34084 this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34085 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
34088 horizontalMeasureColumns : function()
34090 this.getContainerWidth();
34092 var boxWidth = this.boxWidth;
34094 if(this.containerWidth < boxWidth){
34095 boxWidth = this.containerWidth;
34098 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34100 this.el.setHeight(boxWidth);
34104 getContainerWidth : function()
34106 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
34109 layoutItems : function( isInstant )
34111 Roo.log(this.bricks);
34113 var items = Roo.apply([], this.bricks);
34115 if(this.isHorizontal){
34116 this._horizontalLayoutItems( items , isInstant );
34120 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34121 // this._verticalAlternativeLayoutItems( items , isInstant );
34125 this._verticalLayoutItems( items , isInstant );
34129 _verticalLayoutItems : function ( items , isInstant)
34131 if ( !items || !items.length ) {
34136 ['xs', 'xs', 'xs', 'tall'],
34137 ['xs', 'xs', 'tall'],
34138 ['xs', 'xs', 'sm'],
34139 ['xs', 'xs', 'xs'],
34145 ['sm', 'xs', 'xs'],
34149 ['tall', 'xs', 'xs', 'xs'],
34150 ['tall', 'xs', 'xs'],
34162 Roo.each(items, function(item, k){
34164 switch (item.size) {
34165 // these layouts take up a full box,
34176 boxes.push([item]);
34199 var filterPattern = function(box, length)
34207 var pattern = box.slice(0, length);
34211 Roo.each(pattern, function(i){
34212 format.push(i.size);
34215 Roo.each(standard, function(s){
34217 if(String(s) != String(format)){
34226 if(!match && length == 1){
34231 filterPattern(box, length - 1);
34235 queue.push(pattern);
34237 box = box.slice(length, box.length);
34239 filterPattern(box, 4);
34245 Roo.each(boxes, function(box, k){
34251 if(box.length == 1){
34256 filterPattern(box, 4);
34260 this._processVerticalLayoutQueue( queue, isInstant );
34264 // _verticalAlternativeLayoutItems : function( items , isInstant )
34266 // if ( !items || !items.length ) {
34270 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
34274 _horizontalLayoutItems : function ( items , isInstant)
34276 if ( !items || !items.length || items.length < 3) {
34282 var eItems = items.slice(0, 3);
34284 items = items.slice(3, items.length);
34287 ['xs', 'xs', 'xs', 'wide'],
34288 ['xs', 'xs', 'wide'],
34289 ['xs', 'xs', 'sm'],
34290 ['xs', 'xs', 'xs'],
34296 ['sm', 'xs', 'xs'],
34300 ['wide', 'xs', 'xs', 'xs'],
34301 ['wide', 'xs', 'xs'],
34314 Roo.each(items, function(item, k){
34316 switch (item.size) {
34327 boxes.push([item]);
34351 var filterPattern = function(box, length)
34359 var pattern = box.slice(0, length);
34363 Roo.each(pattern, function(i){
34364 format.push(i.size);
34367 Roo.each(standard, function(s){
34369 if(String(s) != String(format)){
34378 if(!match && length == 1){
34383 filterPattern(box, length - 1);
34387 queue.push(pattern);
34389 box = box.slice(length, box.length);
34391 filterPattern(box, 4);
34397 Roo.each(boxes, function(box, k){
34403 if(box.length == 1){
34408 filterPattern(box, 4);
34415 var pos = this.el.getBox(true);
34419 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34421 var hit_end = false;
34423 Roo.each(queue, function(box){
34427 Roo.each(box, function(b){
34429 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34439 Roo.each(box, function(b){
34441 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34444 mx = Math.max(mx, b.x);
34448 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34452 Roo.each(box, function(b){
34454 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34468 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34471 /** Sets position of item in DOM
34472 * @param {Element} item
34473 * @param {Number} x - horizontal position
34474 * @param {Number} y - vertical position
34475 * @param {Boolean} isInstant - disables transitions
34477 _processVerticalLayoutQueue : function( queue, isInstant )
34479 var pos = this.el.getBox(true);
34484 for (var i = 0; i < this.cols; i++){
34488 Roo.each(queue, function(box, k){
34490 var col = k % this.cols;
34492 Roo.each(box, function(b,kk){
34494 b.el.position('absolute');
34496 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34497 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34499 if(b.size == 'md-left' || b.size == 'md-right'){
34500 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34501 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34504 b.el.setWidth(width);
34505 b.el.setHeight(height);
34507 b.el.select('iframe',true).setSize(width,height);
34511 for (var i = 0; i < this.cols; i++){
34513 if(maxY[i] < maxY[col]){
34518 col = Math.min(col, i);
34522 x = pos.x + col * (this.colWidth + this.padWidth);
34526 var positions = [];
34528 switch (box.length){
34530 positions = this.getVerticalOneBoxColPositions(x, y, box);
34533 positions = this.getVerticalTwoBoxColPositions(x, y, box);
34536 positions = this.getVerticalThreeBoxColPositions(x, y, box);
34539 positions = this.getVerticalFourBoxColPositions(x, y, box);
34545 Roo.each(box, function(b,kk){
34547 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34549 var sz = b.el.getSize();
34551 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34559 for (var i = 0; i < this.cols; i++){
34560 mY = Math.max(mY, maxY[i]);
34563 this.el.setHeight(mY - pos.y);
34567 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34569 // var pos = this.el.getBox(true);
34572 // var maxX = pos.right;
34574 // var maxHeight = 0;
34576 // Roo.each(items, function(item, k){
34580 // item.el.position('absolute');
34582 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34584 // item.el.setWidth(width);
34586 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34588 // item.el.setHeight(height);
34591 // item.el.setXY([x, y], isInstant ? false : true);
34593 // item.el.setXY([maxX - width, y], isInstant ? false : true);
34596 // y = y + height + this.alternativePadWidth;
34598 // maxHeight = maxHeight + height + this.alternativePadWidth;
34602 // this.el.setHeight(maxHeight);
34606 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34608 var pos = this.el.getBox(true);
34613 var maxX = pos.right;
34615 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34617 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34619 Roo.each(queue, function(box, k){
34621 Roo.each(box, function(b, kk){
34623 b.el.position('absolute');
34625 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34626 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34628 if(b.size == 'md-left' || b.size == 'md-right'){
34629 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34630 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34633 b.el.setWidth(width);
34634 b.el.setHeight(height);
34642 var positions = [];
34644 switch (box.length){
34646 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34649 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34652 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34655 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34661 Roo.each(box, function(b,kk){
34663 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34665 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34673 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34675 Roo.each(eItems, function(b,k){
34677 b.size = (k == 0) ? 'sm' : 'xs';
34678 b.x = (k == 0) ? 2 : 1;
34679 b.y = (k == 0) ? 2 : 1;
34681 b.el.position('absolute');
34683 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34685 b.el.setWidth(width);
34687 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34689 b.el.setHeight(height);
34693 var positions = [];
34696 x : maxX - this.unitWidth * 2 - this.gutter,
34701 x : maxX - this.unitWidth,
34702 y : minY + (this.unitWidth + this.gutter) * 2
34706 x : maxX - this.unitWidth * 3 - this.gutter * 2,
34710 Roo.each(eItems, function(b,k){
34712 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34718 getVerticalOneBoxColPositions : function(x, y, box)
34722 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34724 if(box[0].size == 'md-left'){
34728 if(box[0].size == 'md-right'){
34733 x : x + (this.unitWidth + this.gutter) * rand,
34740 getVerticalTwoBoxColPositions : function(x, y, box)
34744 if(box[0].size == 'xs'){
34748 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34752 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34766 x : x + (this.unitWidth + this.gutter) * 2,
34767 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34774 getVerticalThreeBoxColPositions : function(x, y, box)
34778 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34786 x : x + (this.unitWidth + this.gutter) * 1,
34791 x : x + (this.unitWidth + this.gutter) * 2,
34799 if(box[0].size == 'xs' && box[1].size == 'xs'){
34808 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34812 x : x + (this.unitWidth + this.gutter) * 1,
34826 x : x + (this.unitWidth + this.gutter) * 2,
34831 x : x + (this.unitWidth + this.gutter) * 2,
34832 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34839 getVerticalFourBoxColPositions : function(x, y, box)
34843 if(box[0].size == 'xs'){
34852 y : y + (this.unitHeight + this.gutter) * 1
34857 y : y + (this.unitHeight + this.gutter) * 2
34861 x : x + (this.unitWidth + this.gutter) * 1,
34875 x : x + (this.unitWidth + this.gutter) * 2,
34880 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34881 y : y + (this.unitHeight + this.gutter) * 1
34885 x : x + (this.unitWidth + this.gutter) * 2,
34886 y : y + (this.unitWidth + this.gutter) * 2
34893 getHorizontalOneBoxColPositions : function(maxX, minY, box)
34897 if(box[0].size == 'md-left'){
34899 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34906 if(box[0].size == 'md-right'){
34908 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34909 y : minY + (this.unitWidth + this.gutter) * 1
34915 var rand = Math.floor(Math.random() * (4 - box[0].y));
34918 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34919 y : minY + (this.unitWidth + this.gutter) * rand
34926 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34930 if(box[0].size == 'xs'){
34933 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34938 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34939 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34947 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34952 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34953 y : minY + (this.unitWidth + this.gutter) * 2
34960 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34964 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34967 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34972 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34973 y : minY + (this.unitWidth + this.gutter) * 1
34977 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34978 y : minY + (this.unitWidth + this.gutter) * 2
34985 if(box[0].size == 'xs' && box[1].size == 'xs'){
34988 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34993 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34998 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34999 y : minY + (this.unitWidth + this.gutter) * 1
35007 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35012 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35013 y : minY + (this.unitWidth + this.gutter) * 2
35017 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35018 y : minY + (this.unitWidth + this.gutter) * 2
35025 getHorizontalFourBoxColPositions : function(maxX, minY, box)
35029 if(box[0].size == 'xs'){
35032 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35037 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35042 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),
35047 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35048 y : minY + (this.unitWidth + this.gutter) * 1
35056 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35061 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35062 y : minY + (this.unitWidth + this.gutter) * 2
35066 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35067 y : minY + (this.unitWidth + this.gutter) * 2
35071 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),
35072 y : minY + (this.unitWidth + this.gutter) * 2
35080 * remove a Masonry Brick
35081 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35083 removeBrick : function(brick_id)
35089 for (var i = 0; i<this.bricks.length; i++) {
35090 if (this.bricks[i].id == brick_id) {
35091 this.bricks.splice(i,1);
35092 this.el.dom.removeChild(Roo.get(brick_id).dom);
35099 * adds a Masonry Brick
35100 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35102 addBrick : function(cfg)
35104 var cn = new Roo.bootstrap.MasonryBrick(cfg);
35105 //this.register(cn);
35106 cn.parentId = this.id;
35107 cn.render(this.el);
35112 * register a Masonry Brick
35113 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35116 register : function(brick)
35118 this.bricks.push(brick);
35119 brick.masonryId = this.id;
35123 * clear all the Masonry Brick
35125 clearAll : function()
35128 //this.getChildContainer().dom.innerHTML = "";
35129 this.el.dom.innerHTML = '';
35132 getSelected : function()
35134 if (!this.selectedBrick) {
35138 return this.selectedBrick;
35142 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35146 * register a Masonry Layout
35147 * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35150 register : function(layout)
35152 this.groups[layout.id] = layout;
35155 * fetch a Masonry Layout based on the masonry layout ID
35156 * @param {string} the masonry layout to add
35157 * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35160 get: function(layout_id) {
35161 if (typeof(this.groups[layout_id]) == 'undefined') {
35164 return this.groups[layout_id] ;
35176 * http://masonry.desandro.com
35178 * The idea is to render all the bricks based on vertical width...
35180 * The original code extends 'outlayer' - we might need to use that....
35186 * @class Roo.bootstrap.LayoutMasonryAuto
35187 * @extends Roo.bootstrap.Component
35188 * Bootstrap Layout Masonry class
35191 * Create a new Element
35192 * @param {Object} config The config object
35195 Roo.bootstrap.LayoutMasonryAuto = function(config){
35196 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35199 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
35202 * @cfg {Boolean} isFitWidth - resize the width..
35204 isFitWidth : false, // options..
35206 * @cfg {Boolean} isOriginLeft = left align?
35208 isOriginLeft : true,
35210 * @cfg {Boolean} isOriginTop = top align?
35212 isOriginTop : false,
35214 * @cfg {Boolean} isLayoutInstant = no animation?
35216 isLayoutInstant : false, // needed?
35218 * @cfg {Boolean} isResizingContainer = not sure if this is used..
35220 isResizingContainer : true,
35222 * @cfg {Number} columnWidth width of the columns
35228 * @cfg {Number} maxCols maximum number of columns
35233 * @cfg {Number} padHeight padding below box..
35239 * @cfg {Boolean} isAutoInitial defalut true
35242 isAutoInitial : true,
35248 initialColumnWidth : 0,
35249 currentSize : null,
35251 colYs : null, // array.
35258 bricks: null, //CompositeElement
35259 cols : 0, // array?
35260 // element : null, // wrapped now this.el
35261 _isLayoutInited : null,
35264 getAutoCreate : function(){
35268 cls: 'blog-masonary-wrapper ' + this.cls,
35270 cls : 'mas-boxes masonary'
35277 getChildContainer: function( )
35279 if (this.boxesEl) {
35280 return this.boxesEl;
35283 this.boxesEl = this.el.select('.mas-boxes').first();
35285 return this.boxesEl;
35289 initEvents : function()
35293 if(this.isAutoInitial){
35294 Roo.log('hook children rendered');
35295 this.on('childrenrendered', function() {
35296 Roo.log('children rendered');
35303 initial : function()
35305 this.reloadItems();
35307 this.currentSize = this.el.getBox(true);
35309 /// was window resize... - let's see if this works..
35310 Roo.EventManager.onWindowResize(this.resize, this);
35312 if(!this.isAutoInitial){
35317 this.layout.defer(500,this);
35320 reloadItems: function()
35322 this.bricks = this.el.select('.masonry-brick', true);
35324 this.bricks.each(function(b) {
35325 //Roo.log(b.getSize());
35326 if (!b.attr('originalwidth')) {
35327 b.attr('originalwidth', b.getSize().width);
35332 Roo.log(this.bricks.elements.length);
35335 resize : function()
35338 var cs = this.el.getBox(true);
35340 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35341 Roo.log("no change in with or X");
35344 this.currentSize = cs;
35348 layout : function()
35351 this._resetLayout();
35352 //this._manageStamps();
35354 // don't animate first layout
35355 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35356 this.layoutItems( isInstant );
35358 // flag for initalized
35359 this._isLayoutInited = true;
35362 layoutItems : function( isInstant )
35364 //var items = this._getItemsForLayout( this.items );
35365 // original code supports filtering layout items.. we just ignore it..
35367 this._layoutItems( this.bricks , isInstant );
35369 this._postLayout();
35371 _layoutItems : function ( items , isInstant)
35373 //this.fireEvent( 'layout', this, items );
35376 if ( !items || !items.elements.length ) {
35377 // no items, emit event with empty array
35382 items.each(function(item) {
35383 Roo.log("layout item");
35385 // get x/y object from method
35386 var position = this._getItemLayoutPosition( item );
35388 position.item = item;
35389 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35390 queue.push( position );
35393 this._processLayoutQueue( queue );
35395 /** Sets position of item in DOM
35396 * @param {Element} item
35397 * @param {Number} x - horizontal position
35398 * @param {Number} y - vertical position
35399 * @param {Boolean} isInstant - disables transitions
35401 _processLayoutQueue : function( queue )
35403 for ( var i=0, len = queue.length; i < len; i++ ) {
35404 var obj = queue[i];
35405 obj.item.position('absolute');
35406 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35412 * Any logic you want to do after each layout,
35413 * i.e. size the container
35415 _postLayout : function()
35417 this.resizeContainer();
35420 resizeContainer : function()
35422 if ( !this.isResizingContainer ) {
35425 var size = this._getContainerSize();
35427 this.el.setSize(size.width,size.height);
35428 this.boxesEl.setSize(size.width,size.height);
35434 _resetLayout : function()
35436 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35437 this.colWidth = this.el.getWidth();
35438 //this.gutter = this.el.getWidth();
35440 this.measureColumns();
35446 this.colYs.push( 0 );
35452 measureColumns : function()
35454 this.getContainerWidth();
35455 // if columnWidth is 0, default to outerWidth of first item
35456 if ( !this.columnWidth ) {
35457 var firstItem = this.bricks.first();
35458 Roo.log(firstItem);
35459 this.columnWidth = this.containerWidth;
35460 if (firstItem && firstItem.attr('originalwidth') ) {
35461 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35463 // columnWidth fall back to item of first element
35464 Roo.log("set column width?");
35465 this.initialColumnWidth = this.columnWidth ;
35467 // if first elem has no width, default to size of container
35472 if (this.initialColumnWidth) {
35473 this.columnWidth = this.initialColumnWidth;
35478 // column width is fixed at the top - however if container width get's smaller we should
35481 // this bit calcs how man columns..
35483 var columnWidth = this.columnWidth += this.gutter;
35485 // calculate columns
35486 var containerWidth = this.containerWidth + this.gutter;
35488 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35489 // fix rounding errors, typically with gutters
35490 var excess = columnWidth - containerWidth % columnWidth;
35493 // if overshoot is less than a pixel, round up, otherwise floor it
35494 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35495 cols = Math[ mathMethod ]( cols );
35496 this.cols = Math.max( cols, 1 );
35497 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35499 // padding positioning..
35500 var totalColWidth = this.cols * this.columnWidth;
35501 var padavail = this.containerWidth - totalColWidth;
35502 // so for 2 columns - we need 3 'pads'
35504 var padNeeded = (1+this.cols) * this.padWidth;
35506 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35508 this.columnWidth += padExtra
35509 //this.padWidth = Math.floor(padavail / ( this.cols));
35511 // adjust colum width so that padding is fixed??
35513 // we have 3 columns ... total = width * 3
35514 // we have X left over... that should be used by
35516 //if (this.expandC) {
35524 getContainerWidth : function()
35526 /* // container is parent if fit width
35527 var container = this.isFitWidth ? this.element.parentNode : this.element;
35528 // check that this.size and size are there
35529 // IE8 triggers resize on body size change, so they might not be
35531 var size = getSize( container ); //FIXME
35532 this.containerWidth = size && size.innerWidth; //FIXME
35535 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
35539 _getItemLayoutPosition : function( item ) // what is item?
35541 // we resize the item to our columnWidth..
35543 item.setWidth(this.columnWidth);
35544 item.autoBoxAdjust = false;
35546 var sz = item.getSize();
35548 // how many columns does this brick span
35549 var remainder = this.containerWidth % this.columnWidth;
35551 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35552 // round if off by 1 pixel, otherwise use ceil
35553 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
35554 colSpan = Math.min( colSpan, this.cols );
35556 // normally this should be '1' as we dont' currently allow multi width columns..
35558 var colGroup = this._getColGroup( colSpan );
35559 // get the minimum Y value from the columns
35560 var minimumY = Math.min.apply( Math, colGroup );
35561 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35563 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
35565 // position the brick
35567 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35568 y: this.currentSize.y + minimumY + this.padHeight
35572 // apply setHeight to necessary columns
35573 var setHeight = minimumY + sz.height + this.padHeight;
35574 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
35576 var setSpan = this.cols + 1 - colGroup.length;
35577 for ( var i = 0; i < setSpan; i++ ) {
35578 this.colYs[ shortColIndex + i ] = setHeight ;
35585 * @param {Number} colSpan - number of columns the element spans
35586 * @returns {Array} colGroup
35588 _getColGroup : function( colSpan )
35590 if ( colSpan < 2 ) {
35591 // if brick spans only one column, use all the column Ys
35596 // how many different places could this brick fit horizontally
35597 var groupCount = this.cols + 1 - colSpan;
35598 // for each group potential horizontal position
35599 for ( var i = 0; i < groupCount; i++ ) {
35600 // make an array of colY values for that one group
35601 var groupColYs = this.colYs.slice( i, i + colSpan );
35602 // and get the max value of the array
35603 colGroup[i] = Math.max.apply( Math, groupColYs );
35608 _manageStamp : function( stamp )
35610 var stampSize = stamp.getSize();
35611 var offset = stamp.getBox();
35612 // get the columns that this stamp affects
35613 var firstX = this.isOriginLeft ? offset.x : offset.right;
35614 var lastX = firstX + stampSize.width;
35615 var firstCol = Math.floor( firstX / this.columnWidth );
35616 firstCol = Math.max( 0, firstCol );
35618 var lastCol = Math.floor( lastX / this.columnWidth );
35619 // lastCol should not go over if multiple of columnWidth #425
35620 lastCol -= lastX % this.columnWidth ? 0 : 1;
35621 lastCol = Math.min( this.cols - 1, lastCol );
35623 // set colYs to bottom of the stamp
35624 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35627 for ( var i = firstCol; i <= lastCol; i++ ) {
35628 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35633 _getContainerSize : function()
35635 this.maxY = Math.max.apply( Math, this.colYs );
35640 if ( this.isFitWidth ) {
35641 size.width = this._getContainerFitWidth();
35647 _getContainerFitWidth : function()
35649 var unusedCols = 0;
35650 // count unused columns
35653 if ( this.colYs[i] !== 0 ) {
35658 // fit container to columns that have been used
35659 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35662 needsResizeLayout : function()
35664 var previousWidth = this.containerWidth;
35665 this.getContainerWidth();
35666 return previousWidth !== this.containerWidth;
35681 * @class Roo.bootstrap.MasonryBrick
35682 * @extends Roo.bootstrap.Component
35683 * Bootstrap MasonryBrick class
35686 * Create a new MasonryBrick
35687 * @param {Object} config The config object
35690 Roo.bootstrap.MasonryBrick = function(config){
35692 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35694 Roo.bootstrap.MasonryBrick.register(this);
35700 * When a MasonryBrick is clcik
35701 * @param {Roo.bootstrap.MasonryBrick} this
35702 * @param {Roo.EventObject} e
35708 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
35711 * @cfg {String} title
35715 * @cfg {String} html
35719 * @cfg {String} bgimage
35723 * @cfg {String} videourl
35727 * @cfg {String} cls
35731 * @cfg {String} href
35735 * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35740 * @cfg {String} placetitle (center|bottom)
35745 * @cfg {Boolean} isFitContainer defalut true
35747 isFitContainer : true,
35750 * @cfg {Boolean} preventDefault defalut false
35752 preventDefault : false,
35755 * @cfg {Boolean} inverse defalut false
35757 maskInverse : false,
35759 getAutoCreate : function()
35761 if(!this.isFitContainer){
35762 return this.getSplitAutoCreate();
35765 var cls = 'masonry-brick masonry-brick-full';
35767 if(this.href.length){
35768 cls += ' masonry-brick-link';
35771 if(this.bgimage.length){
35772 cls += ' masonry-brick-image';
35775 if(this.maskInverse){
35776 cls += ' mask-inverse';
35779 if(!this.html.length && !this.maskInverse && !this.videourl.length){
35780 cls += ' enable-mask';
35784 cls += ' masonry-' + this.size + '-brick';
35787 if(this.placetitle.length){
35789 switch (this.placetitle) {
35791 cls += ' masonry-center-title';
35794 cls += ' masonry-bottom-title';
35801 if(!this.html.length && !this.bgimage.length){
35802 cls += ' masonry-center-title';
35805 if(!this.html.length && this.bgimage.length){
35806 cls += ' masonry-bottom-title';
35811 cls += ' ' + this.cls;
35815 tag: (this.href.length) ? 'a' : 'div',
35820 cls: 'masonry-brick-mask'
35824 cls: 'masonry-brick-paragraph',
35830 if(this.href.length){
35831 cfg.href = this.href;
35834 var cn = cfg.cn[1].cn;
35836 if(this.title.length){
35839 cls: 'masonry-brick-title',
35844 if(this.html.length){
35847 cls: 'masonry-brick-text',
35852 if (!this.title.length && !this.html.length) {
35853 cfg.cn[1].cls += ' hide';
35856 if(this.bgimage.length){
35859 cls: 'masonry-brick-image-view',
35864 if(this.videourl.length){
35865 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35866 // youtube support only?
35869 cls: 'masonry-brick-image-view',
35872 allowfullscreen : true
35880 getSplitAutoCreate : function()
35882 var cls = 'masonry-brick masonry-brick-split';
35884 if(this.href.length){
35885 cls += ' masonry-brick-link';
35888 if(this.bgimage.length){
35889 cls += ' masonry-brick-image';
35893 cls += ' masonry-' + this.size + '-brick';
35896 switch (this.placetitle) {
35898 cls += ' masonry-center-title';
35901 cls += ' masonry-bottom-title';
35904 if(!this.bgimage.length){
35905 cls += ' masonry-center-title';
35908 if(this.bgimage.length){
35909 cls += ' masonry-bottom-title';
35915 cls += ' ' + this.cls;
35919 tag: (this.href.length) ? 'a' : 'div',
35924 cls: 'masonry-brick-split-head',
35928 cls: 'masonry-brick-paragraph',
35935 cls: 'masonry-brick-split-body',
35941 if(this.href.length){
35942 cfg.href = this.href;
35945 if(this.title.length){
35946 cfg.cn[0].cn[0].cn.push({
35948 cls: 'masonry-brick-title',
35953 if(this.html.length){
35954 cfg.cn[1].cn.push({
35956 cls: 'masonry-brick-text',
35961 if(this.bgimage.length){
35962 cfg.cn[0].cn.push({
35964 cls: 'masonry-brick-image-view',
35969 if(this.videourl.length){
35970 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35971 // youtube support only?
35972 cfg.cn[0].cn.cn.push({
35974 cls: 'masonry-brick-image-view',
35977 allowfullscreen : true
35984 initEvents: function()
35986 switch (this.size) {
36019 this.el.on('touchstart', this.onTouchStart, this);
36020 this.el.on('touchmove', this.onTouchMove, this);
36021 this.el.on('touchend', this.onTouchEnd, this);
36022 this.el.on('contextmenu', this.onContextMenu, this);
36024 this.el.on('mouseenter' ,this.enter, this);
36025 this.el.on('mouseleave', this.leave, this);
36026 this.el.on('click', this.onClick, this);
36029 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36030 this.parent().bricks.push(this);
36035 onClick: function(e, el)
36037 var time = this.endTimer - this.startTimer;
36038 // Roo.log(e.preventDefault());
36041 e.preventDefault();
36046 if(!this.preventDefault){
36050 e.preventDefault();
36052 if (this.activeClass != '') {
36053 this.selectBrick();
36056 this.fireEvent('click', this, e);
36059 enter: function(e, el)
36061 e.preventDefault();
36063 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36067 if(this.bgimage.length && this.html.length){
36068 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36072 leave: function(e, el)
36074 e.preventDefault();
36076 if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36080 if(this.bgimage.length && this.html.length){
36081 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36085 onTouchStart: function(e, el)
36087 // e.preventDefault();
36089 this.touchmoved = false;
36091 if(!this.isFitContainer){
36095 if(!this.bgimage.length || !this.html.length){
36099 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36101 this.timer = new Date().getTime();
36105 onTouchMove: function(e, el)
36107 this.touchmoved = true;
36110 onContextMenu : function(e,el)
36112 e.preventDefault();
36113 e.stopPropagation();
36117 onTouchEnd: function(e, el)
36119 // e.preventDefault();
36121 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36128 if(!this.bgimage.length || !this.html.length){
36130 if(this.href.length){
36131 window.location.href = this.href;
36137 if(!this.isFitContainer){
36141 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36143 window.location.href = this.href;
36146 //selection on single brick only
36147 selectBrick : function() {
36149 if (!this.parentId) {
36153 var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36154 var index = m.selectedBrick.indexOf(this.id);
36157 m.selectedBrick.splice(index,1);
36158 this.el.removeClass(this.activeClass);
36162 for(var i = 0; i < m.selectedBrick.length; i++) {
36163 var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36164 b.el.removeClass(b.activeClass);
36167 m.selectedBrick = [];
36169 m.selectedBrick.push(this.id);
36170 this.el.addClass(this.activeClass);
36174 isSelected : function(){
36175 return this.el.hasClass(this.activeClass);
36180 Roo.apply(Roo.bootstrap.MasonryBrick, {
36183 groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36185 * register a Masonry Brick
36186 * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36189 register : function(brick)
36191 //this.groups[brick.id] = brick;
36192 this.groups.add(brick.id, brick);
36195 * fetch a masonry brick based on the masonry brick ID
36196 * @param {string} the masonry brick to add
36197 * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36200 get: function(brick_id)
36202 // if (typeof(this.groups[brick_id]) == 'undefined') {
36205 // return this.groups[brick_id] ;
36207 if(this.groups.key(brick_id)) {
36208 return this.groups.key(brick_id);
36226 * @class Roo.bootstrap.Brick
36227 * @extends Roo.bootstrap.Component
36228 * Bootstrap Brick class
36231 * Create a new Brick
36232 * @param {Object} config The config object
36235 Roo.bootstrap.Brick = function(config){
36236 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36242 * When a Brick is click
36243 * @param {Roo.bootstrap.Brick} this
36244 * @param {Roo.EventObject} e
36250 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
36253 * @cfg {String} title
36257 * @cfg {String} html
36261 * @cfg {String} bgimage
36265 * @cfg {String} cls
36269 * @cfg {String} href
36273 * @cfg {String} video
36277 * @cfg {Boolean} square
36281 getAutoCreate : function()
36283 var cls = 'roo-brick';
36285 if(this.href.length){
36286 cls += ' roo-brick-link';
36289 if(this.bgimage.length){
36290 cls += ' roo-brick-image';
36293 if(!this.html.length && !this.bgimage.length){
36294 cls += ' roo-brick-center-title';
36297 if(!this.html.length && this.bgimage.length){
36298 cls += ' roo-brick-bottom-title';
36302 cls += ' ' + this.cls;
36306 tag: (this.href.length) ? 'a' : 'div',
36311 cls: 'roo-brick-paragraph',
36317 if(this.href.length){
36318 cfg.href = this.href;
36321 var cn = cfg.cn[0].cn;
36323 if(this.title.length){
36326 cls: 'roo-brick-title',
36331 if(this.html.length){
36334 cls: 'roo-brick-text',
36341 if(this.bgimage.length){
36344 cls: 'roo-brick-image-view',
36352 initEvents: function()
36354 if(this.title.length || this.html.length){
36355 this.el.on('mouseenter' ,this.enter, this);
36356 this.el.on('mouseleave', this.leave, this);
36359 Roo.EventManager.onWindowResize(this.resize, this);
36361 if(this.bgimage.length){
36362 this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36363 this.imageEl.on('load', this.onImageLoad, this);
36370 onImageLoad : function()
36375 resize : function()
36377 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36379 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36381 if(this.bgimage.length){
36382 var image = this.el.select('.roo-brick-image-view', true).first();
36384 image.setWidth(paragraph.getWidth());
36387 image.setHeight(paragraph.getWidth());
36390 this.el.setHeight(image.getHeight());
36391 paragraph.setHeight(image.getHeight());
36397 enter: function(e, el)
36399 e.preventDefault();
36401 if(this.bgimage.length){
36402 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36403 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36407 leave: function(e, el)
36409 e.preventDefault();
36411 if(this.bgimage.length){
36412 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36413 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36428 * @class Roo.bootstrap.NumberField
36429 * @extends Roo.bootstrap.Input
36430 * Bootstrap NumberField class
36436 * Create a new NumberField
36437 * @param {Object} config The config object
36440 Roo.bootstrap.NumberField = function(config){
36441 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36444 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36447 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36449 allowDecimals : true,
36451 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36453 decimalSeparator : ".",
36455 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36457 decimalPrecision : 2,
36459 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36461 allowNegative : true,
36464 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36468 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36470 minValue : Number.NEGATIVE_INFINITY,
36472 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36474 maxValue : Number.MAX_VALUE,
36476 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36478 minText : "The minimum value for this field is {0}",
36480 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36482 maxText : "The maximum value for this field is {0}",
36484 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
36485 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36487 nanText : "{0} is not a valid number",
36489 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36491 thousandsDelimiter : false,
36493 * @cfg {String} valueAlign alignment of value
36495 valueAlign : "left",
36497 getAutoCreate : function()
36499 var hiddenInput = {
36503 cls: 'hidden-number-input'
36507 hiddenInput.name = this.name;
36512 var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36514 this.name = hiddenInput.name;
36516 if(cfg.cn.length > 0) {
36517 cfg.cn.push(hiddenInput);
36524 initEvents : function()
36526 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36528 var allowed = "0123456789";
36530 if(this.allowDecimals){
36531 allowed += this.decimalSeparator;
36534 if(this.allowNegative){
36538 if(this.thousandsDelimiter) {
36542 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36544 var keyPress = function(e){
36546 var k = e.getKey();
36548 var c = e.getCharCode();
36551 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36552 allowed.indexOf(String.fromCharCode(c)) === -1
36558 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36562 if(allowed.indexOf(String.fromCharCode(c)) === -1){
36567 this.el.on("keypress", keyPress, this);
36570 validateValue : function(value)
36573 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36577 var num = this.parseValue(value);
36580 this.markInvalid(String.format(this.nanText, value));
36584 if(num < this.minValue){
36585 this.markInvalid(String.format(this.minText, this.minValue));
36589 if(num > this.maxValue){
36590 this.markInvalid(String.format(this.maxText, this.maxValue));
36597 getValue : function()
36599 var v = this.hiddenEl().getValue();
36601 return this.fixPrecision(this.parseValue(v));
36604 parseValue : function(value)
36606 if(this.thousandsDelimiter) {
36608 r = new RegExp(",", "g");
36609 value = value.replace(r, "");
36612 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36613 return isNaN(value) ? '' : value;
36616 fixPrecision : function(value)
36618 if(this.thousandsDelimiter) {
36620 r = new RegExp(",", "g");
36621 value = value.replace(r, "");
36624 var nan = isNaN(value);
36626 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36627 return nan ? '' : value;
36629 return parseFloat(value).toFixed(this.decimalPrecision);
36632 setValue : function(v)
36634 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36640 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36642 this.inputEl().dom.value = (v == '') ? '' :
36643 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36645 if(!this.allowZero && v === '0') {
36646 this.hiddenEl().dom.value = '';
36647 this.inputEl().dom.value = '';
36654 decimalPrecisionFcn : function(v)
36656 return Math.floor(v);
36659 beforeBlur : function()
36661 var v = this.parseValue(this.getRawValue());
36663 if(v || v === 0 || v === ''){
36668 hiddenEl : function()
36670 return this.el.select('input.hidden-number-input',true).first();
36682 * @class Roo.bootstrap.DocumentSlider
36683 * @extends Roo.bootstrap.Component
36684 * Bootstrap DocumentSlider class
36687 * Create a new DocumentViewer
36688 * @param {Object} config The config object
36691 Roo.bootstrap.DocumentSlider = function(config){
36692 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36699 * Fire after initEvent
36700 * @param {Roo.bootstrap.DocumentSlider} this
36705 * Fire after update
36706 * @param {Roo.bootstrap.DocumentSlider} this
36712 * @param {Roo.bootstrap.DocumentSlider} this
36718 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
36724 getAutoCreate : function()
36728 cls : 'roo-document-slider',
36732 cls : 'roo-document-slider-header',
36736 cls : 'roo-document-slider-header-title'
36742 cls : 'roo-document-slider-body',
36746 cls : 'roo-document-slider-prev',
36750 cls : 'fa fa-chevron-left'
36756 cls : 'roo-document-slider-thumb',
36760 cls : 'roo-document-slider-image'
36766 cls : 'roo-document-slider-next',
36770 cls : 'fa fa-chevron-right'
36782 initEvents : function()
36784 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36785 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36787 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36788 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36790 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36791 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36793 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36794 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36796 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36797 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36799 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36800 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36802 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36803 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36805 this.thumbEl.on('click', this.onClick, this);
36807 this.prevIndicator.on('click', this.prev, this);
36809 this.nextIndicator.on('click', this.next, this);
36813 initial : function()
36815 if(this.files.length){
36816 this.indicator = 1;
36820 this.fireEvent('initial', this);
36823 update : function()
36825 this.imageEl.attr('src', this.files[this.indicator - 1]);
36827 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36829 this.prevIndicator.show();
36831 if(this.indicator == 1){
36832 this.prevIndicator.hide();
36835 this.nextIndicator.show();
36837 if(this.indicator == this.files.length){
36838 this.nextIndicator.hide();
36841 this.thumbEl.scrollTo('top');
36843 this.fireEvent('update', this);
36846 onClick : function(e)
36848 e.preventDefault();
36850 this.fireEvent('click', this);
36855 e.preventDefault();
36857 this.indicator = Math.max(1, this.indicator - 1);
36864 e.preventDefault();
36866 this.indicator = Math.min(this.files.length, this.indicator + 1);
36880 * @class Roo.bootstrap.RadioSet
36881 * @extends Roo.bootstrap.Input
36882 * Bootstrap RadioSet class
36883 * @cfg {String} indicatorpos (left|right) default left
36884 * @cfg {Boolean} inline (true|false) inline the element (default true)
36885 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36887 * Create a new RadioSet
36888 * @param {Object} config The config object
36891 Roo.bootstrap.RadioSet = function(config){
36893 Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36897 Roo.bootstrap.RadioSet.register(this);
36902 * Fires when the element is checked or unchecked.
36903 * @param {Roo.bootstrap.RadioSet} this This radio
36904 * @param {Roo.bootstrap.Radio} item The checked item
36909 * Fires when the element is click.
36910 * @param {Roo.bootstrap.RadioSet} this This radio set
36911 * @param {Roo.bootstrap.Radio} item The checked item
36912 * @param {Roo.EventObject} e The event object
36919 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input, {
36927 indicatorpos : 'left',
36929 getAutoCreate : function()
36933 cls : 'roo-radio-set-label',
36937 html : this.fieldLabel
36941 if (Roo.bootstrap.version == 3) {
36944 if(this.indicatorpos == 'left'){
36947 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36948 tooltip : 'This field is required'
36953 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36954 tooltip : 'This field is required'
36960 cls : 'roo-radio-set-items'
36963 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36965 if (align === 'left' && this.fieldLabel.length) {
36968 cls : "roo-radio-set-right",
36974 if(this.labelWidth > 12){
36975 label.style = "width: " + this.labelWidth + 'px';
36978 if(this.labelWidth < 13 && this.labelmd == 0){
36979 this.labelmd = this.labelWidth;
36982 if(this.labellg > 0){
36983 label.cls += ' col-lg-' + this.labellg;
36984 items.cls += ' col-lg-' + (12 - this.labellg);
36987 if(this.labelmd > 0){
36988 label.cls += ' col-md-' + this.labelmd;
36989 items.cls += ' col-md-' + (12 - this.labelmd);
36992 if(this.labelsm > 0){
36993 label.cls += ' col-sm-' + this.labelsm;
36994 items.cls += ' col-sm-' + (12 - this.labelsm);
36997 if(this.labelxs > 0){
36998 label.cls += ' col-xs-' + this.labelxs;
36999 items.cls += ' col-xs-' + (12 - this.labelxs);
37005 cls : 'roo-radio-set',
37009 cls : 'roo-radio-set-input',
37012 value : this.value ? this.value : ''
37019 if(this.weight.length){
37020 cfg.cls += ' roo-radio-' + this.weight;
37024 cfg.cls += ' roo-radio-set-inline';
37028 ['xs','sm','md','lg'].map(function(size){
37029 if (settings[size]) {
37030 cfg.cls += ' col-' + size + '-' + settings[size];
37038 initEvents : function()
37040 this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37041 this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37043 if(!this.fieldLabel.length){
37044 this.labelEl.hide();
37047 this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37048 this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37050 this.indicator = this.indicatorEl();
37052 if(this.indicator){
37053 this.indicator.addClass('invisible');
37056 this.originalValue = this.getValue();
37060 inputEl: function ()
37062 return this.el.select('.roo-radio-set-input', true).first();
37065 getChildContainer : function()
37067 return this.itemsEl;
37070 register : function(item)
37072 this.radioes.push(item);
37076 validate : function()
37078 if(this.getVisibilityEl().hasClass('hidden')){
37084 Roo.each(this.radioes, function(i){
37093 if(this.allowBlank) {
37097 if(this.disabled || valid){
37102 this.markInvalid();
37107 markValid : function()
37109 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37110 this.indicatorEl().removeClass('visible');
37111 this.indicatorEl().addClass('invisible');
37115 if (Roo.bootstrap.version == 3) {
37116 this.el.removeClass([this.invalidClass, this.validClass]);
37117 this.el.addClass(this.validClass);
37119 this.el.removeClass(['is-invalid','is-valid']);
37120 this.el.addClass(['is-valid']);
37122 this.fireEvent('valid', this);
37125 markInvalid : function(msg)
37127 if(this.allowBlank || this.disabled){
37131 if(this.labelEl.isVisible(true) && this.indicatorEl()){
37132 this.indicatorEl().removeClass('invisible');
37133 this.indicatorEl().addClass('visible');
37135 if (Roo.bootstrap.version == 3) {
37136 this.el.removeClass([this.invalidClass, this.validClass]);
37137 this.el.addClass(this.invalidClass);
37139 this.el.removeClass(['is-invalid','is-valid']);
37140 this.el.addClass(['is-invalid']);
37143 this.fireEvent('invalid', this, msg);
37147 setValue : function(v, suppressEvent)
37149 if(this.value === v){
37156 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37159 Roo.each(this.radioes, function(i){
37161 i.el.removeClass('checked');
37164 Roo.each(this.radioes, function(i){
37166 if(i.value === v || i.value.toString() === v.toString()){
37168 i.el.addClass('checked');
37170 if(suppressEvent !== true){
37171 this.fireEvent('check', this, i);
37182 clearInvalid : function(){
37184 if(!this.el || this.preventMark){
37188 this.el.removeClass([this.invalidClass]);
37190 this.fireEvent('valid', this);
37195 Roo.apply(Roo.bootstrap.RadioSet, {
37199 register : function(set)
37201 this.groups[set.name] = set;
37204 get: function(name)
37206 if (typeof(this.groups[name]) == 'undefined') {
37210 return this.groups[name] ;
37216 * Ext JS Library 1.1.1
37217 * Copyright(c) 2006-2007, Ext JS, LLC.
37219 * Originally Released Under LGPL - original licence link has changed is not relivant.
37222 * <script type="text/javascript">
37227 * @class Roo.bootstrap.SplitBar
37228 * @extends Roo.util.Observable
37229 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37233 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37234 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37235 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37236 split.minSize = 100;
37237 split.maxSize = 600;
37238 split.animate = true;
37239 split.on('moved', splitterMoved);
37242 * Create a new SplitBar
37243 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
37244 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
37245 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37246 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
37247 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37248 position of the SplitBar).
37250 Roo.bootstrap.SplitBar = function(cfg){
37255 // dragElement : elm
37256 // resizingElement: el,
37258 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37259 // placement : Roo.bootstrap.SplitBar.LEFT ,
37260 // existingProxy ???
37263 this.el = Roo.get(cfg.dragElement, true);
37264 this.el.dom.unselectable = "on";
37266 this.resizingEl = Roo.get(cfg.resizingElement, true);
37270 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37271 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37274 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37277 * The minimum size of the resizing element. (Defaults to 0)
37283 * The maximum size of the resizing element. (Defaults to 2000)
37286 this.maxSize = 2000;
37289 * Whether to animate the transition to the new size
37292 this.animate = false;
37295 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37298 this.useShim = false;
37303 if(!cfg.existingProxy){
37305 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37307 this.proxy = Roo.get(cfg.existingProxy).dom;
37310 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37313 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37316 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37319 this.dragSpecs = {};
37322 * @private The adapter to use to positon and resize elements
37324 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37325 this.adapter.init(this);
37327 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37329 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37330 this.el.addClass("roo-splitbar-h");
37333 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37334 this.el.addClass("roo-splitbar-v");
37340 * Fires when the splitter is moved (alias for {@link #event-moved})
37341 * @param {Roo.bootstrap.SplitBar} this
37342 * @param {Number} newSize the new width or height
37347 * Fires when the splitter is moved
37348 * @param {Roo.bootstrap.SplitBar} this
37349 * @param {Number} newSize the new width or height
37353 * @event beforeresize
37354 * Fires before the splitter is dragged
37355 * @param {Roo.bootstrap.SplitBar} this
37357 "beforeresize" : true,
37359 "beforeapply" : true
37362 Roo.util.Observable.call(this);
37365 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37366 onStartProxyDrag : function(x, y){
37367 this.fireEvent("beforeresize", this);
37369 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
37371 o.enableDisplayMode("block");
37372 // all splitbars share the same overlay
37373 Roo.bootstrap.SplitBar.prototype.overlay = o;
37375 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37376 this.overlay.show();
37377 Roo.get(this.proxy).setDisplayed("block");
37378 var size = this.adapter.getElementSize(this);
37379 this.activeMinSize = this.getMinimumSize();;
37380 this.activeMaxSize = this.getMaximumSize();;
37381 var c1 = size - this.activeMinSize;
37382 var c2 = Math.max(this.activeMaxSize - size, 0);
37383 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37384 this.dd.resetConstraints();
37385 this.dd.setXConstraint(
37386 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
37387 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37389 this.dd.setYConstraint(0, 0);
37391 this.dd.resetConstraints();
37392 this.dd.setXConstraint(0, 0);
37393 this.dd.setYConstraint(
37394 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
37395 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37398 this.dragSpecs.startSize = size;
37399 this.dragSpecs.startPoint = [x, y];
37400 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37404 * @private Called after the drag operation by the DDProxy
37406 onEndProxyDrag : function(e){
37407 Roo.get(this.proxy).setDisplayed(false);
37408 var endPoint = Roo.lib.Event.getXY(e);
37410 this.overlay.hide();
37413 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37414 newSize = this.dragSpecs.startSize +
37415 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37416 endPoint[0] - this.dragSpecs.startPoint[0] :
37417 this.dragSpecs.startPoint[0] - endPoint[0]
37420 newSize = this.dragSpecs.startSize +
37421 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37422 endPoint[1] - this.dragSpecs.startPoint[1] :
37423 this.dragSpecs.startPoint[1] - endPoint[1]
37426 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37427 if(newSize != this.dragSpecs.startSize){
37428 if(this.fireEvent('beforeapply', this, newSize) !== false){
37429 this.adapter.setElementSize(this, newSize);
37430 this.fireEvent("moved", this, newSize);
37431 this.fireEvent("resize", this, newSize);
37437 * Get the adapter this SplitBar uses
37438 * @return The adapter object
37440 getAdapter : function(){
37441 return this.adapter;
37445 * Set the adapter this SplitBar uses
37446 * @param {Object} adapter A SplitBar adapter object
37448 setAdapter : function(adapter){
37449 this.adapter = adapter;
37450 this.adapter.init(this);
37454 * Gets the minimum size for the resizing element
37455 * @return {Number} The minimum size
37457 getMinimumSize : function(){
37458 return this.minSize;
37462 * Sets the minimum size for the resizing element
37463 * @param {Number} minSize The minimum size
37465 setMinimumSize : function(minSize){
37466 this.minSize = minSize;
37470 * Gets the maximum size for the resizing element
37471 * @return {Number} The maximum size
37473 getMaximumSize : function(){
37474 return this.maxSize;
37478 * Sets the maximum size for the resizing element
37479 * @param {Number} maxSize The maximum size
37481 setMaximumSize : function(maxSize){
37482 this.maxSize = maxSize;
37486 * Sets the initialize size for the resizing element
37487 * @param {Number} size The initial size
37489 setCurrentSize : function(size){
37490 var oldAnimate = this.animate;
37491 this.animate = false;
37492 this.adapter.setElementSize(this, size);
37493 this.animate = oldAnimate;
37497 * Destroy this splitbar.
37498 * @param {Boolean} removeEl True to remove the element
37500 destroy : function(removeEl){
37502 this.shim.remove();
37505 this.proxy.parentNode.removeChild(this.proxy);
37513 * @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.
37515 Roo.bootstrap.SplitBar.createProxy = function(dir){
37516 var proxy = new Roo.Element(document.createElement("div"));
37517 proxy.unselectable();
37518 var cls = 'roo-splitbar-proxy';
37519 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37520 document.body.appendChild(proxy.dom);
37525 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37526 * Default Adapter. It assumes the splitter and resizing element are not positioned
37527 * elements and only gets/sets the width of the element. Generally used for table based layouts.
37529 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37532 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37533 // do nothing for now
37534 init : function(s){
37538 * Called before drag operations to get the current size of the resizing element.
37539 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37541 getElementSize : function(s){
37542 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37543 return s.resizingEl.getWidth();
37545 return s.resizingEl.getHeight();
37550 * Called after drag operations to set the size of the resizing element.
37551 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37552 * @param {Number} newSize The new size to set
37553 * @param {Function} onComplete A function to be invoked when resizing is complete
37555 setElementSize : function(s, newSize, onComplete){
37556 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37558 s.resizingEl.setWidth(newSize);
37560 onComplete(s, newSize);
37563 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37568 s.resizingEl.setHeight(newSize);
37570 onComplete(s, newSize);
37573 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37580 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37581 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37582 * Adapter that moves the splitter element to align with the resized sizing element.
37583 * Used with an absolute positioned SplitBar.
37584 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37585 * document.body, make sure you assign an id to the body element.
37587 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37588 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37589 this.container = Roo.get(container);
37592 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37593 init : function(s){
37594 this.basic.init(s);
37597 getElementSize : function(s){
37598 return this.basic.getElementSize(s);
37601 setElementSize : function(s, newSize, onComplete){
37602 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37605 moveSplitter : function(s){
37606 var yes = Roo.bootstrap.SplitBar;
37607 switch(s.placement){
37609 s.el.setX(s.resizingEl.getRight());
37612 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37615 s.el.setY(s.resizingEl.getBottom());
37618 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37625 * Orientation constant - Create a vertical SplitBar
37629 Roo.bootstrap.SplitBar.VERTICAL = 1;
37632 * Orientation constant - Create a horizontal SplitBar
37636 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37639 * Placement constant - The resizing element is to the left of the splitter element
37643 Roo.bootstrap.SplitBar.LEFT = 1;
37646 * Placement constant - The resizing element is to the right of the splitter element
37650 Roo.bootstrap.SplitBar.RIGHT = 2;
37653 * Placement constant - The resizing element is positioned above the splitter element
37657 Roo.bootstrap.SplitBar.TOP = 3;
37660 * Placement constant - The resizing element is positioned under splitter element
37664 Roo.bootstrap.SplitBar.BOTTOM = 4;
37665 Roo.namespace("Roo.bootstrap.layout");/*
37667 * Ext JS Library 1.1.1
37668 * Copyright(c) 2006-2007, Ext JS, LLC.
37670 * Originally Released Under LGPL - original licence link has changed is not relivant.
37673 * <script type="text/javascript">
37677 * @class Roo.bootstrap.layout.Manager
37678 * @extends Roo.bootstrap.Component
37679 * Base class for layout managers.
37681 Roo.bootstrap.layout.Manager = function(config)
37683 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37689 /** false to disable window resize monitoring @type Boolean */
37690 this.monitorWindowResize = true;
37695 * Fires when a layout is performed.
37696 * @param {Roo.LayoutManager} this
37700 * @event regionresized
37701 * Fires when the user resizes a region.
37702 * @param {Roo.LayoutRegion} region The resized region
37703 * @param {Number} newSize The new size (width for east/west, height for north/south)
37705 "regionresized" : true,
37707 * @event regioncollapsed
37708 * Fires when a region is collapsed.
37709 * @param {Roo.LayoutRegion} region The collapsed region
37711 "regioncollapsed" : true,
37713 * @event regionexpanded
37714 * Fires when a region is expanded.
37715 * @param {Roo.LayoutRegion} region The expanded region
37717 "regionexpanded" : true
37719 this.updating = false;
37722 this.el = Roo.get(config.el);
37728 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37733 monitorWindowResize : true,
37739 onRender : function(ct, position)
37742 this.el = Roo.get(ct);
37745 //this.fireEvent('render',this);
37749 initEvents: function()
37753 // ie scrollbar fix
37754 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37755 document.body.scroll = "no";
37756 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37757 this.el.position('relative');
37759 this.id = this.el.id;
37760 this.el.addClass("roo-layout-container");
37761 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37762 if(this.el.dom != document.body ) {
37763 this.el.on('resize', this.layout,this);
37764 this.el.on('show', this.layout,this);
37770 * Returns true if this layout is currently being updated
37771 * @return {Boolean}
37773 isUpdating : function(){
37774 return this.updating;
37778 * Suspend the LayoutManager from doing auto-layouts while
37779 * making multiple add or remove calls
37781 beginUpdate : function(){
37782 this.updating = true;
37786 * Restore auto-layouts and optionally disable the manager from performing a layout
37787 * @param {Boolean} noLayout true to disable a layout update
37789 endUpdate : function(noLayout){
37790 this.updating = false;
37796 layout: function(){
37800 onRegionResized : function(region, newSize){
37801 this.fireEvent("regionresized", region, newSize);
37805 onRegionCollapsed : function(region){
37806 this.fireEvent("regioncollapsed", region);
37809 onRegionExpanded : function(region){
37810 this.fireEvent("regionexpanded", region);
37814 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37815 * performs box-model adjustments.
37816 * @return {Object} The size as an object {width: (the width), height: (the height)}
37818 getViewSize : function()
37821 if(this.el.dom != document.body){
37822 size = this.el.getSize();
37824 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37826 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37827 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37832 * Returns the Element this layout is bound to.
37833 * @return {Roo.Element}
37835 getEl : function(){
37840 * Returns the specified region.
37841 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37842 * @return {Roo.LayoutRegion}
37844 getRegion : function(target){
37845 return this.regions[target.toLowerCase()];
37848 onWindowResize : function(){
37849 if(this.monitorWindowResize){
37856 * Ext JS Library 1.1.1
37857 * Copyright(c) 2006-2007, Ext JS, LLC.
37859 * Originally Released Under LGPL - original licence link has changed is not relivant.
37862 * <script type="text/javascript">
37865 * @class Roo.bootstrap.layout.Border
37866 * @extends Roo.bootstrap.layout.Manager
37867 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37868 * please see: examples/bootstrap/nested.html<br><br>
37870 <b>The container the layout is rendered into can be either the body element or any other element.
37871 If it is not the body element, the container needs to either be an absolute positioned element,
37872 or you will need to add "position:relative" to the css of the container. You will also need to specify
37873 the container size if it is not the body element.</b>
37876 * Create a new Border
37877 * @param {Object} config Configuration options
37879 Roo.bootstrap.layout.Border = function(config){
37880 config = config || {};
37881 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37885 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37886 if(config[region]){
37887 config[region].region = region;
37888 this.addRegion(config[region]);
37894 Roo.bootstrap.layout.Border.regions = ["center", "north","south","east","west"];
37896 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37898 parent : false, // this might point to a 'nest' or a ???
37901 * Creates and adds a new region if it doesn't already exist.
37902 * @param {String} target The target region key (north, south, east, west or center).
37903 * @param {Object} config The regions config object
37904 * @return {BorderLayoutRegion} The new region
37906 addRegion : function(config)
37908 if(!this.regions[config.region]){
37909 var r = this.factory(config);
37910 this.bindRegion(r);
37912 return this.regions[config.region];
37916 bindRegion : function(r){
37917 this.regions[r.config.region] = r;
37919 r.on("visibilitychange", this.layout, this);
37920 r.on("paneladded", this.layout, this);
37921 r.on("panelremoved", this.layout, this);
37922 r.on("invalidated", this.layout, this);
37923 r.on("resized", this.onRegionResized, this);
37924 r.on("collapsed", this.onRegionCollapsed, this);
37925 r.on("expanded", this.onRegionExpanded, this);
37929 * Performs a layout update.
37931 layout : function()
37933 if(this.updating) {
37937 // render all the rebions if they have not been done alreayd?
37938 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37939 if(this.regions[region] && !this.regions[region].bodyEl){
37940 this.regions[region].onRender(this.el)
37944 var size = this.getViewSize();
37945 var w = size.width;
37946 var h = size.height;
37951 //var x = 0, y = 0;
37953 var rs = this.regions;
37954 var north = rs["north"];
37955 var south = rs["south"];
37956 var west = rs["west"];
37957 var east = rs["east"];
37958 var center = rs["center"];
37959 //if(this.hideOnLayout){ // not supported anymore
37960 //c.el.setStyle("display", "none");
37962 if(north && north.isVisible()){
37963 var b = north.getBox();
37964 var m = north.getMargins();
37965 b.width = w - (m.left+m.right);
37968 centerY = b.height + b.y + m.bottom;
37969 centerH -= centerY;
37970 north.updateBox(this.safeBox(b));
37972 if(south && south.isVisible()){
37973 var b = south.getBox();
37974 var m = south.getMargins();
37975 b.width = w - (m.left+m.right);
37977 var totalHeight = (b.height + m.top + m.bottom);
37978 b.y = h - totalHeight + m.top;
37979 centerH -= totalHeight;
37980 south.updateBox(this.safeBox(b));
37982 if(west && west.isVisible()){
37983 var b = west.getBox();
37984 var m = west.getMargins();
37985 b.height = centerH - (m.top+m.bottom);
37987 b.y = centerY + m.top;
37988 var totalWidth = (b.width + m.left + m.right);
37989 centerX += totalWidth;
37990 centerW -= totalWidth;
37991 west.updateBox(this.safeBox(b));
37993 if(east && east.isVisible()){
37994 var b = east.getBox();
37995 var m = east.getMargins();
37996 b.height = centerH - (m.top+m.bottom);
37997 var totalWidth = (b.width + m.left + m.right);
37998 b.x = w - totalWidth + m.left;
37999 b.y = centerY + m.top;
38000 centerW -= totalWidth;
38001 east.updateBox(this.safeBox(b));
38004 var m = center.getMargins();
38006 x: centerX + m.left,
38007 y: centerY + m.top,
38008 width: centerW - (m.left+m.right),
38009 height: centerH - (m.top+m.bottom)
38011 //if(this.hideOnLayout){
38012 //center.el.setStyle("display", "block");
38014 center.updateBox(this.safeBox(centerBox));
38017 this.fireEvent("layout", this);
38021 safeBox : function(box){
38022 box.width = Math.max(0, box.width);
38023 box.height = Math.max(0, box.height);
38028 * Adds a ContentPanel (or subclass) to this layout.
38029 * @param {String} target The target region key (north, south, east, west or center).
38030 * @param {Roo.ContentPanel} panel The panel to add
38031 * @return {Roo.ContentPanel} The added panel
38033 add : function(target, panel){
38035 target = target.toLowerCase();
38036 return this.regions[target].add(panel);
38040 * Remove a ContentPanel (or subclass) to this layout.
38041 * @param {String} target The target region key (north, south, east, west or center).
38042 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38043 * @return {Roo.ContentPanel} The removed panel
38045 remove : function(target, panel){
38046 target = target.toLowerCase();
38047 return this.regions[target].remove(panel);
38051 * Searches all regions for a panel with the specified id
38052 * @param {String} panelId
38053 * @return {Roo.ContentPanel} The panel or null if it wasn't found
38055 findPanel : function(panelId){
38056 var rs = this.regions;
38057 for(var target in rs){
38058 if(typeof rs[target] != "function"){
38059 var p = rs[target].getPanel(panelId);
38069 * Searches all regions for a panel with the specified id and activates (shows) it.
38070 * @param {String/ContentPanel} panelId The panels id or the panel itself
38071 * @return {Roo.ContentPanel} The shown panel or null
38073 showPanel : function(panelId) {
38074 var rs = this.regions;
38075 for(var target in rs){
38076 var r = rs[target];
38077 if(typeof r != "function"){
38078 if(r.hasPanel(panelId)){
38079 return r.showPanel(panelId);
38087 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38088 * @param {Roo.state.Provider} provider (optional) An alternate state provider
38091 restoreState : function(provider){
38093 provider = Roo.state.Manager;
38095 var sm = new Roo.LayoutStateManager();
38096 sm.init(this, provider);
38102 * Adds a xtype elements to the layout.
38106 xtype : 'ContentPanel',
38113 xtype : 'NestedLayoutPanel',
38119 items : [ ... list of content panels or nested layout panels.. ]
38123 * @param {Object} cfg Xtype definition of item to add.
38125 addxtype : function(cfg)
38127 // basically accepts a pannel...
38128 // can accept a layout region..!?!?
38129 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38132 // theory? children can only be panels??
38134 //if (!cfg.xtype.match(/Panel$/)) {
38139 if (typeof(cfg.region) == 'undefined') {
38140 Roo.log("Failed to add Panel, region was not set");
38144 var region = cfg.region;
38150 xitems = cfg.items;
38155 if ( region == 'center') {
38156 Roo.log("Center: " + cfg.title);
38162 case 'Content': // ContentPanel (el, cfg)
38163 case 'Scroll': // ContentPanel (el, cfg)
38165 cfg.autoCreate = cfg.autoCreate || true;
38166 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38168 // var el = this.el.createChild();
38169 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38172 this.add(region, ret);
38176 case 'TreePanel': // our new panel!
38177 cfg.el = this.el.createChild();
38178 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38179 this.add(region, ret);
38184 // create a new Layout (which is a Border Layout...
38186 var clayout = cfg.layout;
38187 clayout.el = this.el.createChild();
38188 clayout.items = clayout.items || [];
38192 // replace this exitems with the clayout ones..
38193 xitems = clayout.items;
38195 // force background off if it's in center...
38196 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38197 cfg.background = false;
38199 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
38202 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38203 //console.log('adding nested layout panel ' + cfg.toSource());
38204 this.add(region, ret);
38205 nb = {}; /// find first...
38210 // needs grid and region
38212 //var el = this.getRegion(region).el.createChild();
38214 *var el = this.el.createChild();
38215 // create the grid first...
38216 cfg.grid.container = el;
38217 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38220 if (region == 'center' && this.active ) {
38221 cfg.background = false;
38224 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38226 this.add(region, ret);
38228 if (cfg.background) {
38229 // render grid on panel activation (if panel background)
38230 ret.on('activate', function(gp) {
38231 if (!gp.grid.rendered) {
38232 // gp.grid.render(el);
38236 // cfg.grid.render(el);
38242 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38243 // it was the old xcomponent building that caused this before.
38244 // espeically if border is the top element in the tree.
38254 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38256 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38257 this.add(region, ret);
38261 throw "Can not add '" + cfg.xtype + "' to Border";
38267 this.beginUpdate();
38271 Roo.each(xitems, function(i) {
38272 region = nb && i.region ? i.region : false;
38274 var add = ret.addxtype(i);
38277 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38278 if (!i.background) {
38279 abn[region] = nb[region] ;
38286 // make the last non-background panel active..
38287 //if (nb) { Roo.log(abn); }
38290 for(var r in abn) {
38291 region = this.getRegion(r);
38293 // tried using nb[r], but it does not work..
38295 region.showPanel(abn[r]);
38306 factory : function(cfg)
38309 var validRegions = Roo.bootstrap.layout.Border.regions;
38311 var target = cfg.region;
38314 var r = Roo.bootstrap.layout;
38318 return new r.North(cfg);
38320 return new r.South(cfg);
38322 return new r.East(cfg);
38324 return new r.West(cfg);
38326 return new r.Center(cfg);
38328 throw 'Layout region "'+target+'" not supported.';
38335 * Ext JS Library 1.1.1
38336 * Copyright(c) 2006-2007, Ext JS, LLC.
38338 * Originally Released Under LGPL - original licence link has changed is not relivant.
38341 * <script type="text/javascript">
38345 * @class Roo.bootstrap.layout.Basic
38346 * @extends Roo.util.Observable
38347 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38348 * and does not have a titlebar, tabs or any other features. All it does is size and position
38349 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38350 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38351 * @cfg {string} region the region that it inhabits..
38352 * @cfg {bool} skipConfig skip config?
38356 Roo.bootstrap.layout.Basic = function(config){
38358 this.mgr = config.mgr;
38360 this.position = config.region;
38362 var skipConfig = config.skipConfig;
38366 * @scope Roo.BasicLayoutRegion
38370 * @event beforeremove
38371 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38372 * @param {Roo.LayoutRegion} this
38373 * @param {Roo.ContentPanel} panel The panel
38374 * @param {Object} e The cancel event object
38376 "beforeremove" : true,
38378 * @event invalidated
38379 * Fires when the layout for this region is changed.
38380 * @param {Roo.LayoutRegion} this
38382 "invalidated" : true,
38384 * @event visibilitychange
38385 * Fires when this region is shown or hidden
38386 * @param {Roo.LayoutRegion} this
38387 * @param {Boolean} visibility true or false
38389 "visibilitychange" : true,
38391 * @event paneladded
38392 * Fires when a panel is added.
38393 * @param {Roo.LayoutRegion} this
38394 * @param {Roo.ContentPanel} panel The panel
38396 "paneladded" : true,
38398 * @event panelremoved
38399 * Fires when a panel is removed.
38400 * @param {Roo.LayoutRegion} this
38401 * @param {Roo.ContentPanel} panel The panel
38403 "panelremoved" : true,
38405 * @event beforecollapse
38406 * Fires when this region before collapse.
38407 * @param {Roo.LayoutRegion} this
38409 "beforecollapse" : true,
38412 * Fires when this region is collapsed.
38413 * @param {Roo.LayoutRegion} this
38415 "collapsed" : true,
38418 * Fires when this region is expanded.
38419 * @param {Roo.LayoutRegion} this
38424 * Fires when this region is slid into view.
38425 * @param {Roo.LayoutRegion} this
38427 "slideshow" : true,
38430 * Fires when this region slides out of view.
38431 * @param {Roo.LayoutRegion} this
38433 "slidehide" : true,
38435 * @event panelactivated
38436 * Fires when a panel is activated.
38437 * @param {Roo.LayoutRegion} this
38438 * @param {Roo.ContentPanel} panel The activated panel
38440 "panelactivated" : true,
38443 * Fires when the user resizes this region.
38444 * @param {Roo.LayoutRegion} this
38445 * @param {Number} newSize The new size (width for east/west, height for north/south)
38449 /** A collection of panels in this region. @type Roo.util.MixedCollection */
38450 this.panels = new Roo.util.MixedCollection();
38451 this.panels.getKey = this.getPanelId.createDelegate(this);
38453 this.activePanel = null;
38454 // ensure listeners are added...
38456 if (config.listeners || config.events) {
38457 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38458 listeners : config.listeners || {},
38459 events : config.events || {}
38463 if(skipConfig !== true){
38464 this.applyConfig(config);
38468 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38470 getPanelId : function(p){
38474 applyConfig : function(config){
38475 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38476 this.config = config;
38481 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
38482 * the width, for horizontal (north, south) the height.
38483 * @param {Number} newSize The new width or height
38485 resizeTo : function(newSize){
38486 var el = this.el ? this.el :
38487 (this.activePanel ? this.activePanel.getEl() : null);
38489 switch(this.position){
38492 el.setWidth(newSize);
38493 this.fireEvent("resized", this, newSize);
38497 el.setHeight(newSize);
38498 this.fireEvent("resized", this, newSize);
38504 getBox : function(){
38505 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38508 getMargins : function(){
38509 return this.margins;
38512 updateBox : function(box){
38514 var el = this.activePanel.getEl();
38515 el.dom.style.left = box.x + "px";
38516 el.dom.style.top = box.y + "px";
38517 this.activePanel.setSize(box.width, box.height);
38521 * Returns the container element for this region.
38522 * @return {Roo.Element}
38524 getEl : function(){
38525 return this.activePanel;
38529 * Returns true if this region is currently visible.
38530 * @return {Boolean}
38532 isVisible : function(){
38533 return this.activePanel ? true : false;
38536 setActivePanel : function(panel){
38537 panel = this.getPanel(panel);
38538 if(this.activePanel && this.activePanel != panel){
38539 this.activePanel.setActiveState(false);
38540 this.activePanel.getEl().setLeftTop(-10000,-10000);
38542 this.activePanel = panel;
38543 panel.setActiveState(true);
38545 panel.setSize(this.box.width, this.box.height);
38547 this.fireEvent("panelactivated", this, panel);
38548 this.fireEvent("invalidated");
38552 * Show the specified panel.
38553 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38554 * @return {Roo.ContentPanel} The shown panel or null
38556 showPanel : function(panel){
38557 panel = this.getPanel(panel);
38559 this.setActivePanel(panel);
38565 * Get the active panel for this region.
38566 * @return {Roo.ContentPanel} The active panel or null
38568 getActivePanel : function(){
38569 return this.activePanel;
38573 * Add the passed ContentPanel(s)
38574 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38575 * @return {Roo.ContentPanel} The panel added (if only one was added)
38577 add : function(panel){
38578 if(arguments.length > 1){
38579 for(var i = 0, len = arguments.length; i < len; i++) {
38580 this.add(arguments[i]);
38584 if(this.hasPanel(panel)){
38585 this.showPanel(panel);
38588 var el = panel.getEl();
38589 if(el.dom.parentNode != this.mgr.el.dom){
38590 this.mgr.el.dom.appendChild(el.dom);
38592 if(panel.setRegion){
38593 panel.setRegion(this);
38595 this.panels.add(panel);
38596 el.setStyle("position", "absolute");
38597 if(!panel.background){
38598 this.setActivePanel(panel);
38599 if(this.config.initialSize && this.panels.getCount()==1){
38600 this.resizeTo(this.config.initialSize);
38603 this.fireEvent("paneladded", this, panel);
38608 * Returns true if the panel is in this region.
38609 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38610 * @return {Boolean}
38612 hasPanel : function(panel){
38613 if(typeof panel == "object"){ // must be panel obj
38614 panel = panel.getId();
38616 return this.getPanel(panel) ? true : false;
38620 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38621 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38622 * @param {Boolean} preservePanel Overrides the config preservePanel option
38623 * @return {Roo.ContentPanel} The panel that was removed
38625 remove : function(panel, preservePanel){
38626 panel = this.getPanel(panel);
38631 this.fireEvent("beforeremove", this, panel, e);
38632 if(e.cancel === true){
38635 var panelId = panel.getId();
38636 this.panels.removeKey(panelId);
38641 * Returns the panel specified or null if it's not in this region.
38642 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38643 * @return {Roo.ContentPanel}
38645 getPanel : function(id){
38646 if(typeof id == "object"){ // must be panel obj
38649 return this.panels.get(id);
38653 * Returns this regions position (north/south/east/west/center).
38656 getPosition: function(){
38657 return this.position;
38661 * Ext JS Library 1.1.1
38662 * Copyright(c) 2006-2007, Ext JS, LLC.
38664 * Originally Released Under LGPL - original licence link has changed is not relivant.
38667 * <script type="text/javascript">
38671 * @class Roo.bootstrap.layout.Region
38672 * @extends Roo.bootstrap.layout.Basic
38673 * This class represents a region in a layout manager.
38675 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38676 * @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})
38677 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
38678 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
38679 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
38680 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
38681 * @cfg {String} title The title for the region (overrides panel titles)
38682 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
38683 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38684 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
38685 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38686 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
38687 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38688 * the space available, similar to FireFox 1.5 tabs (defaults to false)
38689 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
38690 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
38691 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
38693 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
38694 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
38695 * @cfg {Boolean} disableTabTips True to disable tab tooltips
38696 * @cfg {Number} width For East/West panels
38697 * @cfg {Number} height For North/South panels
38698 * @cfg {Boolean} split To show the splitter
38699 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
38701 * @cfg {string} cls Extra CSS classes to add to region
38703 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
38704 * @cfg {string} region the region that it inhabits..
38707 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
38708 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
38710 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
38711 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
38712 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
38714 Roo.bootstrap.layout.Region = function(config)
38716 this.applyConfig(config);
38718 var mgr = config.mgr;
38719 var pos = config.region;
38720 config.skipConfig = true;
38721 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38724 this.onRender(mgr.el);
38727 this.visible = true;
38728 this.collapsed = false;
38729 this.unrendered_panels = [];
38732 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38734 position: '', // set by wrapper (eg. north/south etc..)
38735 unrendered_panels : null, // unrendered panels.
38737 tabPosition : false,
38739 mgr: false, // points to 'Border'
38742 createBody : function(){
38743 /** This region's body element
38744 * @type Roo.Element */
38745 this.bodyEl = this.el.createChild({
38747 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38751 onRender: function(ctr, pos)
38753 var dh = Roo.DomHelper;
38754 /** This region's container element
38755 * @type Roo.Element */
38756 this.el = dh.append(ctr.dom, {
38758 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38760 /** This region's title element
38761 * @type Roo.Element */
38763 this.titleEl = dh.append(this.el.dom, {
38765 unselectable: "on",
38766 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38768 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
38769 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38773 this.titleEl.enableDisplayMode();
38774 /** This region's title text element
38775 * @type HTMLElement */
38776 this.titleTextEl = this.titleEl.dom.firstChild;
38777 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38779 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38780 this.closeBtn.enableDisplayMode();
38781 this.closeBtn.on("click", this.closeClicked, this);
38782 this.closeBtn.hide();
38784 this.createBody(this.config);
38785 if(this.config.hideWhenEmpty){
38787 this.on("paneladded", this.validateVisibility, this);
38788 this.on("panelremoved", this.validateVisibility, this);
38790 if(this.autoScroll){
38791 this.bodyEl.setStyle("overflow", "auto");
38793 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38795 //if(c.titlebar !== false){
38796 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38797 this.titleEl.hide();
38799 this.titleEl.show();
38800 if(this.config.title){
38801 this.titleTextEl.innerHTML = this.config.title;
38805 if(this.config.collapsed){
38806 this.collapse(true);
38808 if(this.config.hidden){
38812 if (this.unrendered_panels && this.unrendered_panels.length) {
38813 for (var i =0;i< this.unrendered_panels.length; i++) {
38814 this.add(this.unrendered_panels[i]);
38816 this.unrendered_panels = null;
38822 applyConfig : function(c)
38825 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38826 var dh = Roo.DomHelper;
38827 if(c.titlebar !== false){
38828 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38829 this.collapseBtn.on("click", this.collapse, this);
38830 this.collapseBtn.enableDisplayMode();
38832 if(c.showPin === true || this.showPin){
38833 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38834 this.stickBtn.enableDisplayMode();
38835 this.stickBtn.on("click", this.expand, this);
38836 this.stickBtn.hide();
38841 /** This region's collapsed element
38842 * @type Roo.Element */
38845 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38846 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38849 if(c.floatable !== false){
38850 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38851 this.collapsedEl.on("click", this.collapseClick, this);
38854 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38855 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38856 id: "message", unselectable: "on", style:{"float":"left"}});
38857 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38859 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38860 this.expandBtn.on("click", this.expand, this);
38864 if(this.collapseBtn){
38865 this.collapseBtn.setVisible(c.collapsible == true);
38868 this.cmargins = c.cmargins || this.cmargins ||
38869 (this.position == "west" || this.position == "east" ?
38870 {top: 0, left: 2, right:2, bottom: 0} :
38871 {top: 2, left: 0, right:0, bottom: 2});
38873 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38876 this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38878 this.autoScroll = c.autoScroll || false;
38883 this.duration = c.duration || .30;
38884 this.slideDuration = c.slideDuration || .45;
38889 * Returns true if this region is currently visible.
38890 * @return {Boolean}
38892 isVisible : function(){
38893 return this.visible;
38897 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38898 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
38900 //setCollapsedTitle : function(title){
38901 // title = title || " ";
38902 // if(this.collapsedTitleTextEl){
38903 // this.collapsedTitleTextEl.innerHTML = title;
38907 getBox : function(){
38909 // if(!this.collapsed){
38910 b = this.el.getBox(false, true);
38912 // b = this.collapsedEl.getBox(false, true);
38917 getMargins : function(){
38918 return this.margins;
38919 //return this.collapsed ? this.cmargins : this.margins;
38922 highlight : function(){
38923 this.el.addClass("x-layout-panel-dragover");
38926 unhighlight : function(){
38927 this.el.removeClass("x-layout-panel-dragover");
38930 updateBox : function(box)
38932 if (!this.bodyEl) {
38933 return; // not rendered yet..
38937 if(!this.collapsed){
38938 this.el.dom.style.left = box.x + "px";
38939 this.el.dom.style.top = box.y + "px";
38940 this.updateBody(box.width, box.height);
38942 this.collapsedEl.dom.style.left = box.x + "px";
38943 this.collapsedEl.dom.style.top = box.y + "px";
38944 this.collapsedEl.setSize(box.width, box.height);
38947 this.tabs.autoSizeTabs();
38951 updateBody : function(w, h)
38954 this.el.setWidth(w);
38955 w -= this.el.getBorderWidth("rl");
38956 if(this.config.adjustments){
38957 w += this.config.adjustments[0];
38960 if(h !== null && h > 0){
38961 this.el.setHeight(h);
38962 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38963 h -= this.el.getBorderWidth("tb");
38964 if(this.config.adjustments){
38965 h += this.config.adjustments[1];
38967 this.bodyEl.setHeight(h);
38969 h = this.tabs.syncHeight(h);
38972 if(this.panelSize){
38973 w = w !== null ? w : this.panelSize.width;
38974 h = h !== null ? h : this.panelSize.height;
38976 if(this.activePanel){
38977 var el = this.activePanel.getEl();
38978 w = w !== null ? w : el.getWidth();
38979 h = h !== null ? h : el.getHeight();
38980 this.panelSize = {width: w, height: h};
38981 this.activePanel.setSize(w, h);
38983 if(Roo.isIE && this.tabs){
38984 this.tabs.el.repaint();
38989 * Returns the container element for this region.
38990 * @return {Roo.Element}
38992 getEl : function(){
38997 * Hides this region.
39000 //if(!this.collapsed){
39001 this.el.dom.style.left = "-2000px";
39004 // this.collapsedEl.dom.style.left = "-2000px";
39005 // this.collapsedEl.hide();
39007 this.visible = false;
39008 this.fireEvent("visibilitychange", this, false);
39012 * Shows this region if it was previously hidden.
39015 //if(!this.collapsed){
39018 // this.collapsedEl.show();
39020 this.visible = true;
39021 this.fireEvent("visibilitychange", this, true);
39024 closeClicked : function(){
39025 if(this.activePanel){
39026 this.remove(this.activePanel);
39030 collapseClick : function(e){
39032 e.stopPropagation();
39035 e.stopPropagation();
39041 * Collapses this region.
39042 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39045 collapse : function(skipAnim, skipCheck = false){
39046 if(this.collapsed) {
39050 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39052 this.collapsed = true;
39054 this.split.el.hide();
39056 if(this.config.animate && skipAnim !== true){
39057 this.fireEvent("invalidated", this);
39058 this.animateCollapse();
39060 this.el.setLocation(-20000,-20000);
39062 this.collapsedEl.show();
39063 this.fireEvent("collapsed", this);
39064 this.fireEvent("invalidated", this);
39070 animateCollapse : function(){
39075 * Expands this region if it was previously collapsed.
39076 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39077 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39080 expand : function(e, skipAnim){
39082 e.stopPropagation();
39084 if(!this.collapsed || this.el.hasActiveFx()) {
39088 this.afterSlideIn();
39091 this.collapsed = false;
39092 if(this.config.animate && skipAnim !== true){
39093 this.animateExpand();
39097 this.split.el.show();
39099 this.collapsedEl.setLocation(-2000,-2000);
39100 this.collapsedEl.hide();
39101 this.fireEvent("invalidated", this);
39102 this.fireEvent("expanded", this);
39106 animateExpand : function(){
39110 initTabs : function()
39112 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39114 var ts = new Roo.bootstrap.panel.Tabs({
39115 el: this.bodyEl.dom,
39117 tabPosition: this.tabPosition ? this.tabPosition : 'top',
39118 disableTooltips: this.config.disableTabTips,
39119 toolbar : this.config.toolbar
39122 if(this.config.hideTabs){
39123 ts.stripWrap.setDisplayed(false);
39126 ts.resizeTabs = this.config.resizeTabs === true;
39127 ts.minTabWidth = this.config.minTabWidth || 40;
39128 ts.maxTabWidth = this.config.maxTabWidth || 250;
39129 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39130 ts.monitorResize = false;
39131 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39132 ts.bodyEl.addClass('roo-layout-tabs-body');
39133 this.panels.each(this.initPanelAsTab, this);
39136 initPanelAsTab : function(panel){
39137 var ti = this.tabs.addTab(
39141 this.config.closeOnTab && panel.isClosable(),
39144 if(panel.tabTip !== undefined){
39145 ti.setTooltip(panel.tabTip);
39147 ti.on("activate", function(){
39148 this.setActivePanel(panel);
39151 if(this.config.closeOnTab){
39152 ti.on("beforeclose", function(t, e){
39154 this.remove(panel);
39158 panel.tabItem = ti;
39163 updatePanelTitle : function(panel, title)
39165 if(this.activePanel == panel){
39166 this.updateTitle(title);
39169 var ti = this.tabs.getTab(panel.getEl().id);
39171 if(panel.tabTip !== undefined){
39172 ti.setTooltip(panel.tabTip);
39177 updateTitle : function(title){
39178 if(this.titleTextEl && !this.config.title){
39179 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
39183 setActivePanel : function(panel)
39185 panel = this.getPanel(panel);
39186 if(this.activePanel && this.activePanel != panel){
39187 if(this.activePanel.setActiveState(false) === false){
39191 this.activePanel = panel;
39192 panel.setActiveState(true);
39193 if(this.panelSize){
39194 panel.setSize(this.panelSize.width, this.panelSize.height);
39197 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39199 this.updateTitle(panel.getTitle());
39201 this.fireEvent("invalidated", this);
39203 this.fireEvent("panelactivated", this, panel);
39207 * Shows the specified panel.
39208 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39209 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39211 showPanel : function(panel)
39213 panel = this.getPanel(panel);
39216 var tab = this.tabs.getTab(panel.getEl().id);
39217 if(tab.isHidden()){
39218 this.tabs.unhideTab(tab.id);
39222 this.setActivePanel(panel);
39229 * Get the active panel for this region.
39230 * @return {Roo.ContentPanel} The active panel or null
39232 getActivePanel : function(){
39233 return this.activePanel;
39236 validateVisibility : function(){
39237 if(this.panels.getCount() < 1){
39238 this.updateTitle(" ");
39239 this.closeBtn.hide();
39242 if(!this.isVisible()){
39249 * Adds the passed ContentPanel(s) to this region.
39250 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39251 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39253 add : function(panel)
39255 if(arguments.length > 1){
39256 for(var i = 0, len = arguments.length; i < len; i++) {
39257 this.add(arguments[i]);
39262 // if we have not been rendered yet, then we can not really do much of this..
39263 if (!this.bodyEl) {
39264 this.unrendered_panels.push(panel);
39271 if(this.hasPanel(panel)){
39272 this.showPanel(panel);
39275 panel.setRegion(this);
39276 this.panels.add(panel);
39277 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39278 // sinle panel - no tab...?? would it not be better to render it with the tabs,
39279 // and hide them... ???
39280 this.bodyEl.dom.appendChild(panel.getEl().dom);
39281 if(panel.background !== true){
39282 this.setActivePanel(panel);
39284 this.fireEvent("paneladded", this, panel);
39291 this.initPanelAsTab(panel);
39295 if(panel.background !== true){
39296 this.tabs.activate(panel.getEl().id);
39298 this.fireEvent("paneladded", this, panel);
39303 * Hides the tab for the specified panel.
39304 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39306 hidePanel : function(panel){
39307 if(this.tabs && (panel = this.getPanel(panel))){
39308 this.tabs.hideTab(panel.getEl().id);
39313 * Unhides the tab for a previously hidden panel.
39314 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39316 unhidePanel : function(panel){
39317 if(this.tabs && (panel = this.getPanel(panel))){
39318 this.tabs.unhideTab(panel.getEl().id);
39322 clearPanels : function(){
39323 while(this.panels.getCount() > 0){
39324 this.remove(this.panels.first());
39329 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39330 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39331 * @param {Boolean} preservePanel Overrides the config preservePanel option
39332 * @return {Roo.ContentPanel} The panel that was removed
39334 remove : function(panel, preservePanel)
39336 panel = this.getPanel(panel);
39341 this.fireEvent("beforeremove", this, panel, e);
39342 if(e.cancel === true){
39345 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39346 var panelId = panel.getId();
39347 this.panels.removeKey(panelId);
39349 document.body.appendChild(panel.getEl().dom);
39352 this.tabs.removeTab(panel.getEl().id);
39353 }else if (!preservePanel){
39354 this.bodyEl.dom.removeChild(panel.getEl().dom);
39356 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39357 var p = this.panels.first();
39358 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39359 tempEl.appendChild(p.getEl().dom);
39360 this.bodyEl.update("");
39361 this.bodyEl.dom.appendChild(p.getEl().dom);
39363 this.updateTitle(p.getTitle());
39365 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39366 this.setActivePanel(p);
39368 panel.setRegion(null);
39369 if(this.activePanel == panel){
39370 this.activePanel = null;
39372 if(this.config.autoDestroy !== false && preservePanel !== true){
39373 try{panel.destroy();}catch(e){}
39375 this.fireEvent("panelremoved", this, panel);
39380 * Returns the TabPanel component used by this region
39381 * @return {Roo.TabPanel}
39383 getTabs : function(){
39387 createTool : function(parentEl, className){
39388 var btn = Roo.DomHelper.append(parentEl, {
39390 cls: "x-layout-tools-button",
39393 cls: "roo-layout-tools-button-inner " + className,
39397 btn.addClassOnOver("roo-layout-tools-button-over");
39402 * Ext JS Library 1.1.1
39403 * Copyright(c) 2006-2007, Ext JS, LLC.
39405 * Originally Released Under LGPL - original licence link has changed is not relivant.
39408 * <script type="text/javascript">
39414 * @class Roo.SplitLayoutRegion
39415 * @extends Roo.LayoutRegion
39416 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39418 Roo.bootstrap.layout.Split = function(config){
39419 this.cursor = config.cursor;
39420 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39423 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39425 splitTip : "Drag to resize.",
39426 collapsibleSplitTip : "Drag to resize. Double click to hide.",
39427 useSplitTips : false,
39429 applyConfig : function(config){
39430 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39433 onRender : function(ctr,pos) {
39435 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39436 if(!this.config.split){
39441 var splitEl = Roo.DomHelper.append(ctr.dom, {
39443 id: this.el.id + "-split",
39444 cls: "roo-layout-split roo-layout-split-"+this.position,
39447 /** The SplitBar for this region
39448 * @type Roo.SplitBar */
39449 // does not exist yet...
39450 Roo.log([this.position, this.orientation]);
39452 this.split = new Roo.bootstrap.SplitBar({
39453 dragElement : splitEl,
39454 resizingElement: this.el,
39455 orientation : this.orientation
39458 this.split.on("moved", this.onSplitMove, this);
39459 this.split.useShim = this.config.useShim === true;
39460 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39461 if(this.useSplitTips){
39462 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39464 //if(config.collapsible){
39465 // this.split.el.on("dblclick", this.collapse, this);
39468 if(typeof this.config.minSize != "undefined"){
39469 this.split.minSize = this.config.minSize;
39471 if(typeof this.config.maxSize != "undefined"){
39472 this.split.maxSize = this.config.maxSize;
39474 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39475 this.hideSplitter();
39480 getHMaxSize : function(){
39481 var cmax = this.config.maxSize || 10000;
39482 var center = this.mgr.getRegion("center");
39483 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39486 getVMaxSize : function(){
39487 var cmax = this.config.maxSize || 10000;
39488 var center = this.mgr.getRegion("center");
39489 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39492 onSplitMove : function(split, newSize){
39493 this.fireEvent("resized", this, newSize);
39497 * Returns the {@link Roo.SplitBar} for this region.
39498 * @return {Roo.SplitBar}
39500 getSplitBar : function(){
39505 this.hideSplitter();
39506 Roo.bootstrap.layout.Split.superclass.hide.call(this);
39509 hideSplitter : function(){
39511 this.split.el.setLocation(-2000,-2000);
39512 this.split.el.hide();
39518 this.split.el.show();
39520 Roo.bootstrap.layout.Split.superclass.show.call(this);
39523 beforeSlide: function(){
39524 if(Roo.isGecko){// firefox overflow auto bug workaround
39525 this.bodyEl.clip();
39527 this.tabs.bodyEl.clip();
39529 if(this.activePanel){
39530 this.activePanel.getEl().clip();
39532 if(this.activePanel.beforeSlide){
39533 this.activePanel.beforeSlide();
39539 afterSlide : function(){
39540 if(Roo.isGecko){// firefox overflow auto bug workaround
39541 this.bodyEl.unclip();
39543 this.tabs.bodyEl.unclip();
39545 if(this.activePanel){
39546 this.activePanel.getEl().unclip();
39547 if(this.activePanel.afterSlide){
39548 this.activePanel.afterSlide();
39554 initAutoHide : function(){
39555 if(this.autoHide !== false){
39556 if(!this.autoHideHd){
39557 var st = new Roo.util.DelayedTask(this.slideIn, this);
39558 this.autoHideHd = {
39559 "mouseout": function(e){
39560 if(!e.within(this.el, true)){
39564 "mouseover" : function(e){
39570 this.el.on(this.autoHideHd);
39574 clearAutoHide : function(){
39575 if(this.autoHide !== false){
39576 this.el.un("mouseout", this.autoHideHd.mouseout);
39577 this.el.un("mouseover", this.autoHideHd.mouseover);
39581 clearMonitor : function(){
39582 Roo.get(document).un("click", this.slideInIf, this);
39585 // these names are backwards but not changed for compat
39586 slideOut : function(){
39587 if(this.isSlid || this.el.hasActiveFx()){
39590 this.isSlid = true;
39591 if(this.collapseBtn){
39592 this.collapseBtn.hide();
39594 this.closeBtnState = this.closeBtn.getStyle('display');
39595 this.closeBtn.hide();
39597 this.stickBtn.show();
39600 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39601 this.beforeSlide();
39602 this.el.setStyle("z-index", 10001);
39603 this.el.slideIn(this.getSlideAnchor(), {
39604 callback: function(){
39606 this.initAutoHide();
39607 Roo.get(document).on("click", this.slideInIf, this);
39608 this.fireEvent("slideshow", this);
39615 afterSlideIn : function(){
39616 this.clearAutoHide();
39617 this.isSlid = false;
39618 this.clearMonitor();
39619 this.el.setStyle("z-index", "");
39620 if(this.collapseBtn){
39621 this.collapseBtn.show();
39623 this.closeBtn.setStyle('display', this.closeBtnState);
39625 this.stickBtn.hide();
39627 this.fireEvent("slidehide", this);
39630 slideIn : function(cb){
39631 if(!this.isSlid || this.el.hasActiveFx()){
39635 this.isSlid = false;
39636 this.beforeSlide();
39637 this.el.slideOut(this.getSlideAnchor(), {
39638 callback: function(){
39639 this.el.setLeftTop(-10000, -10000);
39641 this.afterSlideIn();
39649 slideInIf : function(e){
39650 if(!e.within(this.el)){
39655 animateCollapse : function(){
39656 this.beforeSlide();
39657 this.el.setStyle("z-index", 20000);
39658 var anchor = this.getSlideAnchor();
39659 this.el.slideOut(anchor, {
39660 callback : function(){
39661 this.el.setStyle("z-index", "");
39662 this.collapsedEl.slideIn(anchor, {duration:.3});
39664 this.el.setLocation(-10000,-10000);
39666 this.fireEvent("collapsed", this);
39673 animateExpand : function(){
39674 this.beforeSlide();
39675 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39676 this.el.setStyle("z-index", 20000);
39677 this.collapsedEl.hide({
39680 this.el.slideIn(this.getSlideAnchor(), {
39681 callback : function(){
39682 this.el.setStyle("z-index", "");
39685 this.split.el.show();
39687 this.fireEvent("invalidated", this);
39688 this.fireEvent("expanded", this);
39716 getAnchor : function(){
39717 return this.anchors[this.position];
39720 getCollapseAnchor : function(){
39721 return this.canchors[this.position];
39724 getSlideAnchor : function(){
39725 return this.sanchors[this.position];
39728 getAlignAdj : function(){
39729 var cm = this.cmargins;
39730 switch(this.position){
39746 getExpandAdj : function(){
39747 var c = this.collapsedEl, cm = this.cmargins;
39748 switch(this.position){
39750 return [-(cm.right+c.getWidth()+cm.left), 0];
39753 return [cm.right+c.getWidth()+cm.left, 0];
39756 return [0, -(cm.top+cm.bottom+c.getHeight())];
39759 return [0, cm.top+cm.bottom+c.getHeight()];
39765 * Ext JS Library 1.1.1
39766 * Copyright(c) 2006-2007, Ext JS, LLC.
39768 * Originally Released Under LGPL - original licence link has changed is not relivant.
39771 * <script type="text/javascript">
39774 * These classes are private internal classes
39776 Roo.bootstrap.layout.Center = function(config){
39777 config.region = "center";
39778 Roo.bootstrap.layout.Region.call(this, config);
39779 this.visible = true;
39780 this.minWidth = config.minWidth || 20;
39781 this.minHeight = config.minHeight || 20;
39784 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39786 // center panel can't be hidden
39790 // center panel can't be hidden
39793 getMinWidth: function(){
39794 return this.minWidth;
39797 getMinHeight: function(){
39798 return this.minHeight;
39812 Roo.bootstrap.layout.North = function(config)
39814 config.region = 'north';
39815 config.cursor = 'n-resize';
39817 Roo.bootstrap.layout.Split.call(this, config);
39821 this.split.placement = Roo.bootstrap.SplitBar.TOP;
39822 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39823 this.split.el.addClass("roo-layout-split-v");
39825 //var size = config.initialSize || config.height;
39826 //if(this.el && typeof size != "undefined"){
39827 // this.el.setHeight(size);
39830 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39832 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39835 onRender : function(ctr, pos)
39837 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39838 var size = this.config.initialSize || this.config.height;
39839 if(this.el && typeof size != "undefined"){
39840 this.el.setHeight(size);
39845 getBox : function(){
39846 if(this.collapsed){
39847 return this.collapsedEl.getBox();
39849 var box = this.el.getBox();
39851 box.height += this.split.el.getHeight();
39856 updateBox : function(box){
39857 if(this.split && !this.collapsed){
39858 box.height -= this.split.el.getHeight();
39859 this.split.el.setLeft(box.x);
39860 this.split.el.setTop(box.y+box.height);
39861 this.split.el.setWidth(box.width);
39863 if(this.collapsed){
39864 this.updateBody(box.width, null);
39866 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39874 Roo.bootstrap.layout.South = function(config){
39875 config.region = 'south';
39876 config.cursor = 's-resize';
39877 Roo.bootstrap.layout.Split.call(this, config);
39879 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39880 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39881 this.split.el.addClass("roo-layout-split-v");
39886 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39887 orientation: Roo.bootstrap.SplitBar.VERTICAL,
39889 onRender : function(ctr, pos)
39891 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39892 var size = this.config.initialSize || this.config.height;
39893 if(this.el && typeof size != "undefined"){
39894 this.el.setHeight(size);
39899 getBox : function(){
39900 if(this.collapsed){
39901 return this.collapsedEl.getBox();
39903 var box = this.el.getBox();
39905 var sh = this.split.el.getHeight();
39912 updateBox : function(box){
39913 if(this.split && !this.collapsed){
39914 var sh = this.split.el.getHeight();
39917 this.split.el.setLeft(box.x);
39918 this.split.el.setTop(box.y-sh);
39919 this.split.el.setWidth(box.width);
39921 if(this.collapsed){
39922 this.updateBody(box.width, null);
39924 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39928 Roo.bootstrap.layout.East = function(config){
39929 config.region = "east";
39930 config.cursor = "e-resize";
39931 Roo.bootstrap.layout.Split.call(this, config);
39933 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39934 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39935 this.split.el.addClass("roo-layout-split-h");
39939 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39940 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39942 onRender : function(ctr, pos)
39944 Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39945 var size = this.config.initialSize || this.config.width;
39946 if(this.el && typeof size != "undefined"){
39947 this.el.setWidth(size);
39952 getBox : function(){
39953 if(this.collapsed){
39954 return this.collapsedEl.getBox();
39956 var box = this.el.getBox();
39958 var sw = this.split.el.getWidth();
39965 updateBox : function(box){
39966 if(this.split && !this.collapsed){
39967 var sw = this.split.el.getWidth();
39969 this.split.el.setLeft(box.x);
39970 this.split.el.setTop(box.y);
39971 this.split.el.setHeight(box.height);
39974 if(this.collapsed){
39975 this.updateBody(null, box.height);
39977 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39981 Roo.bootstrap.layout.West = function(config){
39982 config.region = "west";
39983 config.cursor = "w-resize";
39985 Roo.bootstrap.layout.Split.call(this, config);
39987 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39988 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39989 this.split.el.addClass("roo-layout-split-h");
39993 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39994 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39996 onRender: function(ctr, pos)
39998 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39999 var size = this.config.initialSize || this.config.width;
40000 if(typeof size != "undefined"){
40001 this.el.setWidth(size);
40005 getBox : function(){
40006 if(this.collapsed){
40007 return this.collapsedEl.getBox();
40009 var box = this.el.getBox();
40010 if (box.width == 0) {
40011 box.width = this.config.width; // kludge?
40014 box.width += this.split.el.getWidth();
40019 updateBox : function(box){
40020 if(this.split && !this.collapsed){
40021 var sw = this.split.el.getWidth();
40023 this.split.el.setLeft(box.x+box.width);
40024 this.split.el.setTop(box.y);
40025 this.split.el.setHeight(box.height);
40027 if(this.collapsed){
40028 this.updateBody(null, box.height);
40030 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40032 });Roo.namespace("Roo.bootstrap.panel");/*
40034 * Ext JS Library 1.1.1
40035 * Copyright(c) 2006-2007, Ext JS, LLC.
40037 * Originally Released Under LGPL - original licence link has changed is not relivant.
40040 * <script type="text/javascript">
40043 * @class Roo.ContentPanel
40044 * @extends Roo.util.Observable
40045 * A basic ContentPanel element.
40046 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
40047 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
40048 * @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
40049 * @cfg {Boolean} closable True if the panel can be closed/removed
40050 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
40051 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40052 * @cfg {Toolbar} toolbar A toolbar for this panel
40053 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
40054 * @cfg {String} title The title for this panel
40055 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40056 * @cfg {String} url Calls {@link #setUrl} with this value
40057 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40058 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
40059 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
40060 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
40061 * @cfg {Boolean} iframe contents are an iframe - makes showing remote sources/CSS feasible..
40062 * @cfg {Boolean} badges render the badges
40063 * @cfg {String} cls extra classes to use
40064 * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40067 * Create a new ContentPanel.
40068 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40069 * @param {String/Object} config A string to set only the title or a config object
40070 * @param {String} content (optional) Set the HTML content for this panel
40071 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40073 Roo.bootstrap.panel.Content = function( config){
40075 this.tpl = config.tpl || false;
40077 var el = config.el;
40078 var content = config.content;
40080 if(config.autoCreate){ // xtype is available if this is called from factory
40083 this.el = Roo.get(el);
40084 if(!this.el && config && config.autoCreate){
40085 if(typeof config.autoCreate == "object"){
40086 if(!config.autoCreate.id){
40087 config.autoCreate.id = config.id||el;
40089 this.el = Roo.DomHelper.append(document.body,
40090 config.autoCreate, true);
40094 cls: (config.cls || '') +
40095 (config.background ? ' bg-' + config.background : '') +
40096 " roo-layout-inactive-content",
40099 if (config.iframe) {
40103 style : 'border: 0px',
40104 src : 'about:blank'
40110 elcfg.html = config.html;
40114 this.el = Roo.DomHelper.append(document.body, elcfg , true);
40115 if (config.iframe) {
40116 this.iframeEl = this.el.select('iframe',true).first();
40121 this.closable = false;
40122 this.loaded = false;
40123 this.active = false;
40126 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40128 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40130 this.wrapEl = this.el; //this.el.wrap();
40132 if (config.toolbar.items) {
40133 ti = config.toolbar.items ;
40134 delete config.toolbar.items ;
40138 this.toolbar.render(this.wrapEl, 'before');
40139 for(var i =0;i < ti.length;i++) {
40140 // Roo.log(['add child', items[i]]);
40141 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40143 this.toolbar.items = nitems;
40144 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40145 delete config.toolbar;
40149 // xtype created footer. - not sure if will work as we normally have to render first..
40150 if (this.footer && !this.footer.el && this.footer.xtype) {
40151 if (!this.wrapEl) {
40152 this.wrapEl = this.el.wrap();
40155 this.footer.container = this.wrapEl.createChild();
40157 this.footer = Roo.factory(this.footer, Roo);
40162 if(typeof config == "string"){
40163 this.title = config;
40165 Roo.apply(this, config);
40169 this.resizeEl = Roo.get(this.resizeEl, true);
40171 this.resizeEl = this.el;
40173 // handle view.xtype
40181 * Fires when this panel is activated.
40182 * @param {Roo.ContentPanel} this
40186 * @event deactivate
40187 * Fires when this panel is activated.
40188 * @param {Roo.ContentPanel} this
40190 "deactivate" : true,
40194 * Fires when this panel is resized if fitToFrame is true.
40195 * @param {Roo.ContentPanel} this
40196 * @param {Number} width The width after any component adjustments
40197 * @param {Number} height The height after any component adjustments
40203 * Fires when this tab is created
40204 * @param {Roo.ContentPanel} this
40210 * Fires when this content is scrolled
40211 * @param {Roo.ContentPanel} this
40212 * @param {Event} scrollEvent
40223 if(this.autoScroll && !this.iframe){
40224 this.resizeEl.setStyle("overflow", "auto");
40225 this.resizeEl.on('scroll', this.onScroll, this);
40227 // fix randome scrolling
40228 //this.el.on('scroll', function() {
40229 // Roo.log('fix random scolling');
40230 // this.scrollTo('top',0);
40233 content = content || this.content;
40235 this.setContent(content);
40237 if(config && config.url){
40238 this.setUrl(this.url, this.params, this.loadOnce);
40243 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40245 if (this.view && typeof(this.view.xtype) != 'undefined') {
40246 this.view.el = this.el.appendChild(document.createElement("div"));
40247 this.view = Roo.factory(this.view);
40248 this.view.render && this.view.render(false, '');
40252 this.fireEvent('render', this);
40255 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40265 /* Resize Element - use this to work out scroll etc. */
40268 setRegion : function(region){
40269 this.region = region;
40270 this.setActiveClass(region && !this.background);
40274 setActiveClass: function(state)
40277 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40278 this.el.setStyle('position','relative');
40280 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40281 this.el.setStyle('position', 'absolute');
40286 * Returns the toolbar for this Panel if one was configured.
40287 * @return {Roo.Toolbar}
40289 getToolbar : function(){
40290 return this.toolbar;
40293 setActiveState : function(active)
40295 this.active = active;
40296 this.setActiveClass(active);
40298 if(this.fireEvent("deactivate", this) === false){
40303 this.fireEvent("activate", this);
40307 * Updates this panel's element (not for iframe)
40308 * @param {String} content The new content
40309 * @param {Boolean} loadScripts (optional) true to look for and process scripts
40311 setContent : function(content, loadScripts){
40316 this.el.update(content, loadScripts);
40319 ignoreResize : function(w, h){
40320 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40323 this.lastSize = {width: w, height: h};
40328 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40329 * @return {Roo.UpdateManager} The UpdateManager
40331 getUpdateManager : function(){
40335 return this.el.getUpdateManager();
40338 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40339 * Does not work with IFRAME contents
40340 * @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:
40343 url: "your-url.php",
40344 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40345 callback: yourFunction,
40346 scope: yourObject, //(optional scope)
40349 text: "Loading...",
40355 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40356 * 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.
40357 * @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}
40358 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40359 * @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.
40360 * @return {Roo.ContentPanel} this
40368 var um = this.el.getUpdateManager();
40369 um.update.apply(um, arguments);
40375 * 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.
40376 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40377 * @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)
40378 * @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)
40379 * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40381 setUrl : function(url, params, loadOnce){
40383 this.iframeEl.dom.src = url;
40387 if(this.refreshDelegate){
40388 this.removeListener("activate", this.refreshDelegate);
40390 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40391 this.on("activate", this.refreshDelegate);
40392 return this.el.getUpdateManager();
40395 _handleRefresh : function(url, params, loadOnce){
40396 if(!loadOnce || !this.loaded){
40397 var updater = this.el.getUpdateManager();
40398 updater.update(url, params, this._setLoaded.createDelegate(this));
40402 _setLoaded : function(){
40403 this.loaded = true;
40407 * Returns this panel's id
40410 getId : function(){
40415 * Returns this panel's element - used by regiosn to add.
40416 * @return {Roo.Element}
40418 getEl : function(){
40419 return this.wrapEl || this.el;
40424 adjustForComponents : function(width, height)
40426 //Roo.log('adjustForComponents ');
40427 if(this.resizeEl != this.el){
40428 width -= this.el.getFrameWidth('lr');
40429 height -= this.el.getFrameWidth('tb');
40432 var te = this.toolbar.getEl();
40433 te.setWidth(width);
40434 height -= te.getHeight();
40437 var te = this.footer.getEl();
40438 te.setWidth(width);
40439 height -= te.getHeight();
40443 if(this.adjustments){
40444 width += this.adjustments[0];
40445 height += this.adjustments[1];
40447 return {"width": width, "height": height};
40450 setSize : function(width, height){
40451 if(this.fitToFrame && !this.ignoreResize(width, height)){
40452 if(this.fitContainer && this.resizeEl != this.el){
40453 this.el.setSize(width, height);
40455 var size = this.adjustForComponents(width, height);
40457 this.iframeEl.setSize(width,height);
40460 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40461 this.fireEvent('resize', this, size.width, size.height);
40468 * Returns this panel's title
40471 getTitle : function(){
40473 if (typeof(this.title) != 'object') {
40478 for (var k in this.title) {
40479 if (!this.title.hasOwnProperty(k)) {
40483 if (k.indexOf('-') >= 0) {
40484 var s = k.split('-');
40485 for (var i = 0; i<s.length; i++) {
40486 t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40489 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40496 * Set this panel's title
40497 * @param {String} title
40499 setTitle : function(title){
40500 this.title = title;
40502 this.region.updatePanelTitle(this, title);
40507 * Returns true is this panel was configured to be closable
40508 * @return {Boolean}
40510 isClosable : function(){
40511 return this.closable;
40514 beforeSlide : function(){
40516 this.resizeEl.clip();
40519 afterSlide : function(){
40521 this.resizeEl.unclip();
40525 * Force a content refresh from the URL specified in the {@link #setUrl} method.
40526 * Will fail silently if the {@link #setUrl} method has not been called.
40527 * This does not activate the panel, just updates its content.
40529 refresh : function(){
40530 if(this.refreshDelegate){
40531 this.loaded = false;
40532 this.refreshDelegate();
40537 * Destroys this panel
40539 destroy : function(){
40540 this.el.removeAllListeners();
40541 var tempEl = document.createElement("span");
40542 tempEl.appendChild(this.el.dom);
40543 tempEl.innerHTML = "";
40549 * form - if the content panel contains a form - this is a reference to it.
40550 * @type {Roo.form.Form}
40554 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40555 * This contains a reference to it.
40561 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40571 * @param {Object} cfg Xtype definition of item to add.
40575 getChildContainer: function () {
40576 return this.getEl();
40580 onScroll : function(e)
40582 this.fireEvent('scroll', this, e);
40587 var ret = new Roo.factory(cfg);
40592 if (cfg.xtype.match(/^Form$/)) {
40595 //if (this.footer) {
40596 // el = this.footer.container.insertSibling(false, 'before');
40598 el = this.el.createChild();
40601 this.form = new Roo.form.Form(cfg);
40604 if ( this.form.allItems.length) {
40605 this.form.render(el.dom);
40609 // should only have one of theses..
40610 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40611 // views.. should not be just added - used named prop 'view''
40613 cfg.el = this.el.appendChild(document.createElement("div"));
40616 var ret = new Roo.factory(cfg);
40618 ret.render && ret.render(false, ''); // render blank..
40628 * @class Roo.bootstrap.panel.Grid
40629 * @extends Roo.bootstrap.panel.Content
40631 * Create a new GridPanel.
40632 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40633 * @param {Object} config A the config object
40639 Roo.bootstrap.panel.Grid = function(config)
40643 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40644 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40646 config.el = this.wrapper;
40647 //this.el = this.wrapper;
40649 if (config.container) {
40650 // ctor'ed from a Border/panel.grid
40653 this.wrapper.setStyle("overflow", "hidden");
40654 this.wrapper.addClass('roo-grid-container');
40659 if(config.toolbar){
40660 var tool_el = this.wrapper.createChild();
40661 this.toolbar = Roo.factory(config.toolbar);
40663 if (config.toolbar.items) {
40664 ti = config.toolbar.items ;
40665 delete config.toolbar.items ;
40669 this.toolbar.render(tool_el);
40670 for(var i =0;i < ti.length;i++) {
40671 // Roo.log(['add child', items[i]]);
40672 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40674 this.toolbar.items = nitems;
40676 delete config.toolbar;
40679 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40680 config.grid.scrollBody = true;;
40681 config.grid.monitorWindowResize = false; // turn off autosizing
40682 config.grid.autoHeight = false;
40683 config.grid.autoWidth = false;
40685 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40687 if (config.background) {
40688 // render grid on panel activation (if panel background)
40689 this.on('activate', function(gp) {
40690 if (!gp.grid.rendered) {
40691 gp.grid.render(this.wrapper);
40692 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40697 this.grid.render(this.wrapper);
40698 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
40701 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40702 // ??? needed ??? config.el = this.wrapper;
40707 // xtype created footer. - not sure if will work as we normally have to render first..
40708 if (this.footer && !this.footer.el && this.footer.xtype) {
40710 var ctr = this.grid.getView().getFooterPanel(true);
40711 this.footer.dataSource = this.grid.dataSource;
40712 this.footer = Roo.factory(this.footer, Roo);
40713 this.footer.render(ctr);
40723 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40724 getId : function(){
40725 return this.grid.id;
40729 * Returns the grid for this panel
40730 * @return {Roo.bootstrap.Table}
40732 getGrid : function(){
40736 setSize : function(width, height){
40737 if(!this.ignoreResize(width, height)){
40738 var grid = this.grid;
40739 var size = this.adjustForComponents(width, height);
40740 // tfoot is not a footer?
40743 var gridel = grid.getGridEl();
40744 gridel.setSize(size.width, size.height);
40746 var tbd = grid.getGridEl().select('tbody', true).first();
40747 var thd = grid.getGridEl().select('thead',true).first();
40748 var tbf= grid.getGridEl().select('tfoot', true).first();
40751 size.height -= tbf.getHeight();
40754 size.height -= thd.getHeight();
40757 tbd.setSize(size.width, size.height );
40758 // this is for the account management tab -seems to work there.
40759 var thd = grid.getGridEl().select('thead',true).first();
40761 // tbd.setSize(size.width, size.height - thd.getHeight());
40770 beforeSlide : function(){
40771 this.grid.getView().scroller.clip();
40774 afterSlide : function(){
40775 this.grid.getView().scroller.unclip();
40778 destroy : function(){
40779 this.grid.destroy();
40781 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
40786 * @class Roo.bootstrap.panel.Nest
40787 * @extends Roo.bootstrap.panel.Content
40789 * Create a new Panel, that can contain a layout.Border.
40792 * @param {Roo.BorderLayout} layout The layout for this panel
40793 * @param {String/Object} config A string to set only the title or a config object
40795 Roo.bootstrap.panel.Nest = function(config)
40797 // construct with only one argument..
40798 /* FIXME - implement nicer consturctors
40799 if (layout.layout) {
40801 layout = config.layout;
40802 delete config.layout;
40804 if (layout.xtype && !layout.getEl) {
40805 // then layout needs constructing..
40806 layout = Roo.factory(layout, Roo);
40810 config.el = config.layout.getEl();
40812 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40814 config.layout.monitorWindowResize = false; // turn off autosizing
40815 this.layout = config.layout;
40816 this.layout.getEl().addClass("roo-layout-nested-layout");
40817 this.layout.parent = this;
40824 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40826 setSize : function(width, height){
40827 if(!this.ignoreResize(width, height)){
40828 var size = this.adjustForComponents(width, height);
40829 var el = this.layout.getEl();
40830 if (size.height < 1) {
40831 el.setWidth(size.width);
40833 el.setSize(size.width, size.height);
40835 var touch = el.dom.offsetWidth;
40836 this.layout.layout();
40837 // ie requires a double layout on the first pass
40838 if(Roo.isIE && !this.initialized){
40839 this.initialized = true;
40840 this.layout.layout();
40845 // activate all subpanels if not currently active..
40847 setActiveState : function(active){
40848 this.active = active;
40849 this.setActiveClass(active);
40852 this.fireEvent("deactivate", this);
40856 this.fireEvent("activate", this);
40857 // not sure if this should happen before or after..
40858 if (!this.layout) {
40859 return; // should not happen..
40862 for (var r in this.layout.regions) {
40863 reg = this.layout.getRegion(r);
40864 if (reg.getActivePanel()) {
40865 //reg.showPanel(reg.getActivePanel()); // force it to activate..
40866 reg.setActivePanel(reg.getActivePanel());
40869 if (!reg.panels.length) {
40872 reg.showPanel(reg.getPanel(0));
40881 * Returns the nested BorderLayout for this panel
40882 * @return {Roo.BorderLayout}
40884 getLayout : function(){
40885 return this.layout;
40889 * Adds a xtype elements to the layout of the nested panel
40893 xtype : 'ContentPanel',
40900 xtype : 'NestedLayoutPanel',
40906 items : [ ... list of content panels or nested layout panels.. ]
40910 * @param {Object} cfg Xtype definition of item to add.
40912 addxtype : function(cfg) {
40913 return this.layout.addxtype(cfg);
40918 * Ext JS Library 1.1.1
40919 * Copyright(c) 2006-2007, Ext JS, LLC.
40921 * Originally Released Under LGPL - original licence link has changed is not relivant.
40924 * <script type="text/javascript">
40927 * @class Roo.TabPanel
40928 * @extends Roo.util.Observable
40929 * A lightweight tab container.
40933 // basic tabs 1, built from existing content
40934 var tabs = new Roo.TabPanel("tabs1");
40935 tabs.addTab("script", "View Script");
40936 tabs.addTab("markup", "View Markup");
40937 tabs.activate("script");
40939 // more advanced tabs, built from javascript
40940 var jtabs = new Roo.TabPanel("jtabs");
40941 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40943 // set up the UpdateManager
40944 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40945 var updater = tab2.getUpdateManager();
40946 updater.setDefaultUrl("ajax1.htm");
40947 tab2.on('activate', updater.refresh, updater, true);
40949 // Use setUrl for Ajax loading
40950 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40951 tab3.setUrl("ajax2.htm", null, true);
40954 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40957 jtabs.activate("jtabs-1");
40960 * Create a new TabPanel.
40961 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40962 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40964 Roo.bootstrap.panel.Tabs = function(config){
40966 * The container element for this TabPanel.
40967 * @type Roo.Element
40969 this.el = Roo.get(config.el);
40972 if(typeof config == "boolean"){
40973 this.tabPosition = config ? "bottom" : "top";
40975 Roo.apply(this, config);
40979 if(this.tabPosition == "bottom"){
40980 // if tabs are at the bottom = create the body first.
40981 this.bodyEl = Roo.get(this.createBody(this.el.dom));
40982 this.el.addClass("roo-tabs-bottom");
40984 // next create the tabs holders
40986 if (this.tabPosition == "west"){
40988 var reg = this.region; // fake it..
40990 if (!reg.mgr.parent) {
40993 reg = reg.mgr.parent.region;
40995 Roo.log("got nest?");
40997 if (reg.mgr.getRegion('west')) {
40998 var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40999 this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41000 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41001 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41002 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41010 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41011 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41012 this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41013 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41018 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41021 // finally - if tabs are at the top, then create the body last..
41022 if(this.tabPosition != "bottom"){
41023 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41024 * @type Roo.Element
41026 this.bodyEl = Roo.get(this.createBody(this.el.dom));
41027 this.el.addClass("roo-tabs-top");
41031 this.bodyEl.setStyle("position", "relative");
41033 this.active = null;
41034 this.activateDelegate = this.activate.createDelegate(this);
41039 * Fires when the active tab changes
41040 * @param {Roo.TabPanel} this
41041 * @param {Roo.TabPanelItem} activePanel The new active tab
41045 * @event beforetabchange
41046 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41047 * @param {Roo.TabPanel} this
41048 * @param {Object} e Set cancel to true on this object to cancel the tab change
41049 * @param {Roo.TabPanelItem} tab The tab being changed to
41051 "beforetabchange" : true
41054 Roo.EventManager.onWindowResize(this.onResize, this);
41055 this.cpad = this.el.getPadding("lr");
41056 this.hiddenCount = 0;
41059 // toolbar on the tabbar support...
41060 if (this.toolbar) {
41061 alert("no toolbar support yet");
41062 this.toolbar = false;
41064 var tcfg = this.toolbar;
41065 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
41066 this.toolbar = new Roo.Toolbar(tcfg);
41067 if (Roo.isSafari) {
41068 var tbl = tcfg.container.child('table', true);
41069 tbl.setAttribute('width', '100%');
41077 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41080 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41082 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41084 tabPosition : "top",
41086 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41088 currentTabWidth : 0,
41090 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41094 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41098 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41100 preferredTabWidth : 175,
41102 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41104 resizeTabs : false,
41106 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41108 monitorResize : true,
41110 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
41112 toolbar : false, // set by caller..
41114 region : false, /// set by caller
41116 disableTooltips : true, // not used yet...
41119 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41120 * @param {String} id The id of the div to use <b>or create</b>
41121 * @param {String} text The text for the tab
41122 * @param {String} content (optional) Content to put in the TabPanelItem body
41123 * @param {Boolean} closable (optional) True to create a close icon on the tab
41124 * @return {Roo.TabPanelItem} The created TabPanelItem
41126 addTab : function(id, text, content, closable, tpl)
41128 var item = new Roo.bootstrap.panel.TabItem({
41132 closable : closable,
41135 this.addTabItem(item);
41137 item.setContent(content);
41143 * Returns the {@link Roo.TabPanelItem} with the specified id/index
41144 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41145 * @return {Roo.TabPanelItem}
41147 getTab : function(id){
41148 return this.items[id];
41152 * Hides the {@link Roo.TabPanelItem} with the specified id/index
41153 * @param {String/Number} id The id or index of the TabPanelItem to hide.
41155 hideTab : function(id){
41156 var t = this.items[id];
41159 this.hiddenCount++;
41160 this.autoSizeTabs();
41165 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41166 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41168 unhideTab : function(id){
41169 var t = this.items[id];
41171 t.setHidden(false);
41172 this.hiddenCount--;
41173 this.autoSizeTabs();
41178 * Adds an existing {@link Roo.TabPanelItem}.
41179 * @param {Roo.TabPanelItem} item The TabPanelItem to add
41181 addTabItem : function(item)
41183 this.items[item.id] = item;
41184 this.items.push(item);
41185 this.autoSizeTabs();
41186 // if(this.resizeTabs){
41187 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41188 // this.autoSizeTabs();
41190 // item.autoSize();
41195 * Removes a {@link Roo.TabPanelItem}.
41196 * @param {String/Number} id The id or index of the TabPanelItem to remove.
41198 removeTab : function(id){
41199 var items = this.items;
41200 var tab = items[id];
41201 if(!tab) { return; }
41202 var index = items.indexOf(tab);
41203 if(this.active == tab && items.length > 1){
41204 var newTab = this.getNextAvailable(index);
41209 this.stripEl.dom.removeChild(tab.pnode.dom);
41210 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41211 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41213 items.splice(index, 1);
41214 delete this.items[tab.id];
41215 tab.fireEvent("close", tab);
41216 tab.purgeListeners();
41217 this.autoSizeTabs();
41220 getNextAvailable : function(start){
41221 var items = this.items;
41223 // look for a next tab that will slide over to
41224 // replace the one being removed
41225 while(index < items.length){
41226 var item = items[++index];
41227 if(item && !item.isHidden()){
41231 // if one isn't found select the previous tab (on the left)
41234 var item = items[--index];
41235 if(item && !item.isHidden()){
41243 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41244 * @param {String/Number} id The id or index of the TabPanelItem to disable.
41246 disableTab : function(id){
41247 var tab = this.items[id];
41248 if(tab && this.active != tab){
41254 * Enables a {@link Roo.TabPanelItem} that is disabled.
41255 * @param {String/Number} id The id or index of the TabPanelItem to enable.
41257 enableTab : function(id){
41258 var tab = this.items[id];
41263 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41264 * @param {String/Number} id The id or index of the TabPanelItem to activate.
41265 * @return {Roo.TabPanelItem} The TabPanelItem.
41267 activate : function(id)
41269 //Roo.log('activite:' + id);
41271 var tab = this.items[id];
41275 if(tab == this.active || tab.disabled){
41279 this.fireEvent("beforetabchange", this, e, tab);
41280 if(e.cancel !== true && !tab.disabled){
41282 this.active.hide();
41284 this.active = this.items[id];
41285 this.active.show();
41286 this.fireEvent("tabchange", this, this.active);
41292 * Gets the active {@link Roo.TabPanelItem}.
41293 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41295 getActiveTab : function(){
41296 return this.active;
41300 * Updates the tab body element to fit the height of the container element
41301 * for overflow scrolling
41302 * @param {Number} targetHeight (optional) Override the starting height from the elements height
41304 syncHeight : function(targetHeight){
41305 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41306 var bm = this.bodyEl.getMargins();
41307 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41308 this.bodyEl.setHeight(newHeight);
41312 onResize : function(){
41313 if(this.monitorResize){
41314 this.autoSizeTabs();
41319 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41321 beginUpdate : function(){
41322 this.updating = true;
41326 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41328 endUpdate : function(){
41329 this.updating = false;
41330 this.autoSizeTabs();
41334 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41336 autoSizeTabs : function()
41338 var count = this.items.length;
41339 var vcount = count - this.hiddenCount;
41342 this.stripEl.hide();
41344 this.stripEl.show();
41347 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41352 var w = Math.max(this.el.getWidth() - this.cpad, 10);
41353 var availWidth = Math.floor(w / vcount);
41354 var b = this.stripBody;
41355 if(b.getWidth() > w){
41356 var tabs = this.items;
41357 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41358 if(availWidth < this.minTabWidth){
41359 /*if(!this.sleft){ // incomplete scrolling code
41360 this.createScrollButtons();
41363 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41366 if(this.currentTabWidth < this.preferredTabWidth){
41367 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41373 * Returns the number of tabs in this TabPanel.
41376 getCount : function(){
41377 return this.items.length;
41381 * Resizes all the tabs to the passed width
41382 * @param {Number} The new width
41384 setTabWidth : function(width){
41385 this.currentTabWidth = width;
41386 for(var i = 0, len = this.items.length; i < len; i++) {
41387 if(!this.items[i].isHidden()) {
41388 this.items[i].setWidth(width);
41394 * Destroys this TabPanel
41395 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41397 destroy : function(removeEl){
41398 Roo.EventManager.removeResizeListener(this.onResize, this);
41399 for(var i = 0, len = this.items.length; i < len; i++){
41400 this.items[i].purgeListeners();
41402 if(removeEl === true){
41403 this.el.update("");
41408 createStrip : function(container)
41410 var strip = document.createElement("nav");
41411 strip.className = Roo.bootstrap.version == 4 ?
41412 "navbar-light bg-light" :
41413 "navbar navbar-default"; //"x-tabs-wrap";
41414 container.appendChild(strip);
41418 createStripList : function(strip)
41420 // div wrapper for retard IE
41421 // returns the "tr" element.
41422 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41423 //'<div class="x-tabs-strip-wrap">'+
41424 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41425 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41426 return strip.firstChild; //.firstChild.firstChild.firstChild;
41428 createBody : function(container)
41430 var body = document.createElement("div");
41431 Roo.id(body, "tab-body");
41432 //Roo.fly(body).addClass("x-tabs-body");
41433 Roo.fly(body).addClass("tab-content");
41434 container.appendChild(body);
41437 createItemBody :function(bodyEl, id){
41438 var body = Roo.getDom(id);
41440 body = document.createElement("div");
41443 //Roo.fly(body).addClass("x-tabs-item-body");
41444 Roo.fly(body).addClass("tab-pane");
41445 bodyEl.insertBefore(body, bodyEl.firstChild);
41449 createStripElements : function(stripEl, text, closable, tpl)
41451 var td = document.createElement("li"); // was td..
41452 td.className = 'nav-item';
41454 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41457 stripEl.appendChild(td);
41459 td.className = "x-tabs-closable";
41460 if(!this.closeTpl){
41461 this.closeTpl = new Roo.Template(
41462 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41463 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41464 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
41467 var el = this.closeTpl.overwrite(td, {"text": text});
41468 var close = el.getElementsByTagName("div")[0];
41469 var inner = el.getElementsByTagName("em")[0];
41470 return {"el": el, "close": close, "inner": inner};
41473 // not sure what this is..
41474 // if(!this.tabTpl){
41475 //this.tabTpl = new Roo.Template(
41476 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41477 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41479 // this.tabTpl = new Roo.Template(
41480 // '<a href="#">' +
41481 // '<span unselectable="on"' +
41482 // (this.disableTooltips ? '' : ' title="{text}"') +
41483 // ' >{text}</span></a>'
41489 var template = tpl || this.tabTpl || false;
41492 template = new Roo.Template(
41493 Roo.bootstrap.version == 4 ?
41495 '<a class="nav-link" href="#" unselectable="on"' +
41496 (this.disableTooltips ? '' : ' title="{text}"') +
41499 '<a class="nav-link" href="#">' +
41500 '<span unselectable="on"' +
41501 (this.disableTooltips ? '' : ' title="{text}"') +
41502 ' >{text}</span></a>'
41507 switch (typeof(template)) {
41511 template = new Roo.Template(template);
41517 var el = template.overwrite(td, {"text": text});
41519 var inner = el.getElementsByTagName("span")[0];
41521 return {"el": el, "inner": inner};
41529 * @class Roo.TabPanelItem
41530 * @extends Roo.util.Observable
41531 * Represents an individual item (tab plus body) in a TabPanel.
41532 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41533 * @param {String} id The id of this TabPanelItem
41534 * @param {String} text The text for the tab of this TabPanelItem
41535 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41537 Roo.bootstrap.panel.TabItem = function(config){
41539 * The {@link Roo.TabPanel} this TabPanelItem belongs to
41540 * @type Roo.TabPanel
41542 this.tabPanel = config.panel;
41544 * The id for this TabPanelItem
41547 this.id = config.id;
41549 this.disabled = false;
41551 this.text = config.text;
41553 this.loaded = false;
41554 this.closable = config.closable;
41557 * The body element for this TabPanelItem.
41558 * @type Roo.Element
41560 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41561 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41562 this.bodyEl.setStyle("display", "block");
41563 this.bodyEl.setStyle("zoom", "1");
41564 //this.hideAction();
41566 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41568 this.el = Roo.get(els.el);
41569 this.inner = Roo.get(els.inner, true);
41570 this.textEl = Roo.bootstrap.version == 4 ?
41571 this.el : Roo.get(this.el.dom.firstChild, true);
41573 this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41574 this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41577 // this.el.on("mousedown", this.onTabMouseDown, this);
41578 this.el.on("click", this.onTabClick, this);
41580 if(config.closable){
41581 var c = Roo.get(els.close, true);
41582 c.dom.title = this.closeText;
41583 c.addClassOnOver("close-over");
41584 c.on("click", this.closeClick, this);
41590 * Fires when this tab becomes the active tab.
41591 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41592 * @param {Roo.TabPanelItem} this
41596 * @event beforeclose
41597 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41598 * @param {Roo.TabPanelItem} this
41599 * @param {Object} e Set cancel to true on this object to cancel the close.
41601 "beforeclose": true,
41604 * Fires when this tab is closed.
41605 * @param {Roo.TabPanelItem} this
41609 * @event deactivate
41610 * Fires when this tab is no longer the active tab.
41611 * @param {Roo.TabPanel} tabPanel The parent TabPanel
41612 * @param {Roo.TabPanelItem} this
41614 "deactivate" : true
41616 this.hidden = false;
41618 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41621 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41623 purgeListeners : function(){
41624 Roo.util.Observable.prototype.purgeListeners.call(this);
41625 this.el.removeAllListeners();
41628 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41631 this.status_node.addClass("active");
41634 this.tabPanel.stripWrap.repaint();
41636 this.fireEvent("activate", this.tabPanel, this);
41640 * Returns true if this tab is the active tab.
41641 * @return {Boolean}
41643 isActive : function(){
41644 return this.tabPanel.getActiveTab() == this;
41648 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41651 this.status_node.removeClass("active");
41653 this.fireEvent("deactivate", this.tabPanel, this);
41656 hideAction : function(){
41657 this.bodyEl.hide();
41658 this.bodyEl.setStyle("position", "absolute");
41659 this.bodyEl.setLeft("-20000px");
41660 this.bodyEl.setTop("-20000px");
41663 showAction : function(){
41664 this.bodyEl.setStyle("position", "relative");
41665 this.bodyEl.setTop("");
41666 this.bodyEl.setLeft("");
41667 this.bodyEl.show();
41671 * Set the tooltip for the tab.
41672 * @param {String} tooltip The tab's tooltip
41674 setTooltip : function(text){
41675 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41676 this.textEl.dom.qtip = text;
41677 this.textEl.dom.removeAttribute('title');
41679 this.textEl.dom.title = text;
41683 onTabClick : function(e){
41684 e.preventDefault();
41685 this.tabPanel.activate(this.id);
41688 onTabMouseDown : function(e){
41689 e.preventDefault();
41690 this.tabPanel.activate(this.id);
41693 getWidth : function(){
41694 return this.inner.getWidth();
41697 setWidth : function(width){
41698 var iwidth = width - this.linode.getPadding("lr");
41699 this.inner.setWidth(iwidth);
41700 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41701 this.linode.setWidth(width);
41705 * Show or hide the tab
41706 * @param {Boolean} hidden True to hide or false to show.
41708 setHidden : function(hidden){
41709 this.hidden = hidden;
41710 this.linode.setStyle("display", hidden ? "none" : "");
41714 * Returns true if this tab is "hidden"
41715 * @return {Boolean}
41717 isHidden : function(){
41718 return this.hidden;
41722 * Returns the text for this tab
41725 getText : function(){
41729 autoSize : function(){
41730 //this.el.beginMeasure();
41731 this.textEl.setWidth(1);
41733 * #2804 [new] Tabs in Roojs
41734 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41736 //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41737 //this.el.endMeasure();
41741 * Sets the text for the tab (Note: this also sets the tooltip text)
41742 * @param {String} text The tab's text and tooltip
41744 setText : function(text){
41746 this.textEl.update(text);
41747 this.setTooltip(text);
41748 //if(!this.tabPanel.resizeTabs){
41749 // this.autoSize();
41753 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41755 activate : function(){
41756 this.tabPanel.activate(this.id);
41760 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41762 disable : function(){
41763 if(this.tabPanel.active != this){
41764 this.disabled = true;
41765 this.status_node.addClass("disabled");
41770 * Enables this TabPanelItem if it was previously disabled.
41772 enable : function(){
41773 this.disabled = false;
41774 this.status_node.removeClass("disabled");
41778 * Sets the content for this TabPanelItem.
41779 * @param {String} content The content
41780 * @param {Boolean} loadScripts true to look for and load scripts
41782 setContent : function(content, loadScripts){
41783 this.bodyEl.update(content, loadScripts);
41787 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41788 * @return {Roo.UpdateManager} The UpdateManager
41790 getUpdateManager : function(){
41791 return this.bodyEl.getUpdateManager();
41795 * Set a URL to be used to load the content for this TabPanelItem.
41796 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41797 * @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)
41798 * @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)
41799 * @return {Roo.UpdateManager} The UpdateManager
41801 setUrl : function(url, params, loadOnce){
41802 if(this.refreshDelegate){
41803 this.un('activate', this.refreshDelegate);
41805 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41806 this.on("activate", this.refreshDelegate);
41807 return this.bodyEl.getUpdateManager();
41811 _handleRefresh : function(url, params, loadOnce){
41812 if(!loadOnce || !this.loaded){
41813 var updater = this.bodyEl.getUpdateManager();
41814 updater.update(url, params, this._setLoaded.createDelegate(this));
41819 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
41820 * Will fail silently if the setUrl method has not been called.
41821 * This does not activate the panel, just updates its content.
41823 refresh : function(){
41824 if(this.refreshDelegate){
41825 this.loaded = false;
41826 this.refreshDelegate();
41831 _setLoaded : function(){
41832 this.loaded = true;
41836 closeClick : function(e){
41839 this.fireEvent("beforeclose", this, o);
41840 if(o.cancel !== true){
41841 this.tabPanel.removeTab(this.id);
41845 * The text displayed in the tooltip for the close icon.
41848 closeText : "Close this tab"
41851 * This script refer to:
41852 * Title: International Telephone Input
41853 * Author: Jack O'Connor
41854 * Code version: v12.1.12
41855 * Availability: https://github.com/jackocnr/intl-tel-input.git
41858 Roo.bootstrap.PhoneInputData = function() {
41861 "Afghanistan (افغانستان)",
41866 "Albania (Shqipëri)",
41871 "Algeria (الجزائر)",
41896 "Antigua and Barbuda",
41906 "Armenia (Հայաստան)",
41922 "Austria (Österreich)",
41927 "Azerbaijan (Azərbaycan)",
41937 "Bahrain (البحرين)",
41942 "Bangladesh (বাংলাদেশ)",
41952 "Belarus (Беларусь)",
41957 "Belgium (België)",
41987 "Bosnia and Herzegovina (Босна и Херцеговина)",
42002 "British Indian Ocean Territory",
42007 "British Virgin Islands",
42017 "Bulgaria (България)",
42027 "Burundi (Uburundi)",
42032 "Cambodia (កម្ពុជា)",
42037 "Cameroon (Cameroun)",
42046 ["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"]
42049 "Cape Verde (Kabu Verdi)",
42054 "Caribbean Netherlands",
42065 "Central African Republic (République centrafricaine)",
42085 "Christmas Island",
42091 "Cocos (Keeling) Islands",
42102 "Comoros (جزر القمر)",
42107 "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42112 "Congo (Republic) (Congo-Brazzaville)",
42132 "Croatia (Hrvatska)",
42153 "Czech Republic (Česká republika)",
42158 "Denmark (Danmark)",
42173 "Dominican Republic (República Dominicana)",
42177 ["809", "829", "849"]
42195 "Equatorial Guinea (Guinea Ecuatorial)",
42215 "Falkland Islands (Islas Malvinas)",
42220 "Faroe Islands (Føroyar)",
42241 "French Guiana (Guyane française)",
42246 "French Polynesia (Polynésie française)",
42261 "Georgia (საქართველო)",
42266 "Germany (Deutschland)",
42286 "Greenland (Kalaallit Nunaat)",
42323 "Guinea-Bissau (Guiné Bissau)",
42348 "Hungary (Magyarország)",
42353 "Iceland (Ísland)",
42373 "Iraq (العراق)",
42389 "Israel (ישראל)",
42416 "Jordan (الأردن)",
42421 "Kazakhstan (Казахстан)",
42442 "Kuwait (الكويت)",
42447 "Kyrgyzstan (Кыргызстан)",
42457 "Latvia (Latvija)",
42462 "Lebanon (لبنان)",
42477 "Libya (ليبيا)",
42487 "Lithuania (Lietuva)",
42502 "Macedonia (FYROM) (Македонија)",
42507 "Madagascar (Madagasikara)",
42537 "Marshall Islands",
42547 "Mauritania (موريتانيا)",
42552 "Mauritius (Moris)",
42573 "Moldova (Republica Moldova)",
42583 "Mongolia (Монгол)",
42588 "Montenegro (Crna Gora)",
42598 "Morocco (المغرب)",
42604 "Mozambique (Moçambique)",
42609 "Myanmar (Burma) (မြန်မာ)",
42614 "Namibia (Namibië)",
42629 "Netherlands (Nederland)",
42634 "New Caledonia (Nouvelle-Calédonie)",
42669 "North Korea (조선 민주주의 인민 공화국)",
42674 "Northern Mariana Islands",
42690 "Pakistan (پاکستان)",
42700 "Palestine (فلسطين)",
42710 "Papua New Guinea",
42752 "Réunion (La Réunion)",
42758 "Romania (România)",
42774 "Saint Barthélemy",
42785 "Saint Kitts and Nevis",
42795 "Saint Martin (Saint-Martin (partie française))",
42801 "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42806 "Saint Vincent and the Grenadines",
42821 "São Tomé and Príncipe (São Tomé e Príncipe)",
42826 "Saudi Arabia (المملكة العربية السعودية)",
42831 "Senegal (Sénégal)",
42861 "Slovakia (Slovensko)",
42866 "Slovenia (Slovenija)",
42876 "Somalia (Soomaaliya)",
42886 "South Korea (대한민국)",
42891 "South Sudan (جنوب السودان)",
42901 "Sri Lanka (ශ්රී ලංකාව)",
42906 "Sudan (السودان)",
42916 "Svalbard and Jan Mayen",
42927 "Sweden (Sverige)",
42932 "Switzerland (Schweiz)",
42937 "Syria (سوريا)",
42982 "Trinidad and Tobago",
42987 "Tunisia (تونس)",
42992 "Turkey (Türkiye)",
43002 "Turks and Caicos Islands",
43012 "U.S. Virgin Islands",
43022 "Ukraine (Україна)",
43027 "United Arab Emirates (الإمارات العربية المتحدة)",
43049 "Uzbekistan (Oʻzbekiston)",
43059 "Vatican City (Città del Vaticano)",
43070 "Vietnam (Việt Nam)",
43075 "Wallis and Futuna (Wallis-et-Futuna)",
43080 "Western Sahara (الصحراء الغربية)",
43086 "Yemen (اليمن)",
43110 * This script refer to:
43111 * Title: International Telephone Input
43112 * Author: Jack O'Connor
43113 * Code version: v12.1.12
43114 * Availability: https://github.com/jackocnr/intl-tel-input.git
43118 * @class Roo.bootstrap.PhoneInput
43119 * @extends Roo.bootstrap.TriggerField
43120 * An input with International dial-code selection
43122 * @cfg {String} defaultDialCode default '+852'
43123 * @cfg {Array} preferedCountries default []
43126 * Create a new PhoneInput.
43127 * @param {Object} config Configuration options
43130 Roo.bootstrap.PhoneInput = function(config) {
43131 Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43134 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43136 listWidth: undefined,
43138 selectedClass: 'active',
43140 invalidClass : "has-warning",
43142 validClass: 'has-success',
43144 allowed: '0123456789',
43149 * @cfg {String} defaultDialCode The default dial code when initializing the input
43151 defaultDialCode: '+852',
43154 * @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
43156 preferedCountries: false,
43158 getAutoCreate : function()
43160 var data = Roo.bootstrap.PhoneInputData();
43161 var align = this.labelAlign || this.parentLabelAlign();
43164 this.allCountries = [];
43165 this.dialCodeMapping = [];
43167 for (var i = 0; i < data.length; i++) {
43169 this.allCountries[i] = {
43173 priority: c[3] || 0,
43174 areaCodes: c[4] || null
43176 this.dialCodeMapping[c[2]] = {
43179 priority: c[3] || 0,
43180 areaCodes: c[4] || null
43192 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43193 maxlength: this.max_length,
43194 cls : 'form-control tel-input',
43195 autocomplete: 'new-password'
43198 var hiddenInput = {
43201 cls: 'hidden-tel-input'
43205 hiddenInput.name = this.name;
43208 if (this.disabled) {
43209 input.disabled = true;
43212 var flag_container = {
43229 cls: this.hasFeedback ? 'has-feedback' : '',
43235 cls: 'dial-code-holder',
43242 cls: 'roo-select2-container input-group',
43249 if (this.fieldLabel.length) {
43252 tooltip: 'This field is required'
43258 cls: 'control-label',
43264 html: this.fieldLabel
43267 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43273 if(this.indicatorpos == 'right') {
43274 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43281 if(align == 'left') {
43289 if(this.labelWidth > 12){
43290 label.style = "width: " + this.labelWidth + 'px';
43292 if(this.labelWidth < 13 && this.labelmd == 0){
43293 this.labelmd = this.labelWidth;
43295 if(this.labellg > 0){
43296 label.cls += ' col-lg-' + this.labellg;
43297 input.cls += ' col-lg-' + (12 - this.labellg);
43299 if(this.labelmd > 0){
43300 label.cls += ' col-md-' + this.labelmd;
43301 container.cls += ' col-md-' + (12 - this.labelmd);
43303 if(this.labelsm > 0){
43304 label.cls += ' col-sm-' + this.labelsm;
43305 container.cls += ' col-sm-' + (12 - this.labelsm);
43307 if(this.labelxs > 0){
43308 label.cls += ' col-xs-' + this.labelxs;
43309 container.cls += ' col-xs-' + (12 - this.labelxs);
43319 var settings = this;
43321 ['xs','sm','md','lg'].map(function(size){
43322 if (settings[size]) {
43323 cfg.cls += ' col-' + size + '-' + settings[size];
43327 this.store = new Roo.data.Store({
43328 proxy : new Roo.data.MemoryProxy({}),
43329 reader : new Roo.data.JsonReader({
43340 'name' : 'dialCode',
43344 'name' : 'priority',
43348 'name' : 'areaCodes',
43355 if(!this.preferedCountries) {
43356 this.preferedCountries = [
43363 var p = this.preferedCountries.reverse();
43366 for (var i = 0; i < p.length; i++) {
43367 for (var j = 0; j < this.allCountries.length; j++) {
43368 if(this.allCountries[j].iso2 == p[i]) {
43369 var t = this.allCountries[j];
43370 this.allCountries.splice(j,1);
43371 this.allCountries.unshift(t);
43377 this.store.proxy.data = {
43379 data: this.allCountries
43385 initEvents : function()
43388 Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43390 this.indicator = this.indicatorEl();
43391 this.flag = this.flagEl();
43392 this.dialCodeHolder = this.dialCodeHolderEl();
43394 this.trigger = this.el.select('div.flag-box',true).first();
43395 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43400 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43401 _this.list.setWidth(lw);
43404 this.list.on('mouseover', this.onViewOver, this);
43405 this.list.on('mousemove', this.onViewMove, this);
43406 this.inputEl().on("keyup", this.onKeyUp, this);
43407 this.inputEl().on("keypress", this.onKeyPress, this);
43409 this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43411 this.view = new Roo.View(this.list, this.tpl, {
43412 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43415 this.view.on('click', this.onViewClick, this);
43416 this.setValue(this.defaultDialCode);
43419 onTriggerClick : function(e)
43421 Roo.log('trigger click');
43426 if(this.isExpanded()){
43428 this.hasFocus = false;
43430 this.store.load({});
43431 this.hasFocus = true;
43436 isExpanded : function()
43438 return this.list.isVisible();
43441 collapse : function()
43443 if(!this.isExpanded()){
43447 Roo.get(document).un('mousedown', this.collapseIf, this);
43448 Roo.get(document).un('mousewheel', this.collapseIf, this);
43449 this.fireEvent('collapse', this);
43453 expand : function()
43457 if(this.isExpanded() || !this.hasFocus){
43461 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43462 this.list.setWidth(lw);
43465 this.restrictHeight();
43467 Roo.get(document).on('mousedown', this.collapseIf, this);
43468 Roo.get(document).on('mousewheel', this.collapseIf, this);
43470 this.fireEvent('expand', this);
43473 restrictHeight : function()
43475 this.list.alignTo(this.inputEl(), this.listAlign);
43476 this.list.alignTo(this.inputEl(), this.listAlign);
43479 onViewOver : function(e, t)
43481 if(this.inKeyMode){
43484 var item = this.view.findItemFromChild(t);
43487 var index = this.view.indexOf(item);
43488 this.select(index, false);
43493 onViewClick : function(view, doFocus, el, e)
43495 var index = this.view.getSelectedIndexes()[0];
43497 var r = this.store.getAt(index);
43500 this.onSelect(r, index);
43502 if(doFocus !== false && !this.blockFocus){
43503 this.inputEl().focus();
43507 onViewMove : function(e, t)
43509 this.inKeyMode = false;
43512 select : function(index, scrollIntoView)
43514 this.selectedIndex = index;
43515 this.view.select(index);
43516 if(scrollIntoView !== false){
43517 var el = this.view.getNode(index);
43519 this.list.scrollChildIntoView(el, false);
43524 createList : function()
43526 this.list = Roo.get(document.body).createChild({
43528 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43529 style: 'display:none'
43532 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43535 collapseIf : function(e)
43537 var in_combo = e.within(this.el);
43538 var in_list = e.within(this.list);
43539 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43541 if (in_combo || in_list || is_list) {
43547 onSelect : function(record, index)
43549 if(this.fireEvent('beforeselect', this, record, index) !== false){
43551 this.setFlagClass(record.data.iso2);
43552 this.setDialCode(record.data.dialCode);
43553 this.hasFocus = false;
43555 this.fireEvent('select', this, record, index);
43559 flagEl : function()
43561 var flag = this.el.select('div.flag',true).first();
43568 dialCodeHolderEl : function()
43570 var d = this.el.select('input.dial-code-holder',true).first();
43577 setDialCode : function(v)
43579 this.dialCodeHolder.dom.value = '+'+v;
43582 setFlagClass : function(n)
43584 this.flag.dom.className = 'flag '+n;
43587 getValue : function()
43589 var v = this.inputEl().getValue();
43590 if(this.dialCodeHolder) {
43591 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43596 setValue : function(v)
43598 var d = this.getDialCode(v);
43600 //invalid dial code
43601 if(v.length == 0 || !d || d.length == 0) {
43603 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43604 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43610 this.setFlagClass(this.dialCodeMapping[d].iso2);
43611 this.setDialCode(d);
43612 this.inputEl().dom.value = v.replace('+'+d,'');
43613 this.hiddenEl().dom.value = this.getValue();
43618 getDialCode : function(v)
43622 if (v.length == 0) {
43623 return this.dialCodeHolder.dom.value;
43627 if (v.charAt(0) != "+") {
43630 var numericChars = "";
43631 for (var i = 1; i < v.length; i++) {
43632 var c = v.charAt(i);
43635 if (this.dialCodeMapping[numericChars]) {
43636 dialCode = v.substr(1, i);
43638 if (numericChars.length == 4) {
43648 this.setValue(this.defaultDialCode);
43652 hiddenEl : function()
43654 return this.el.select('input.hidden-tel-input',true).first();
43657 // after setting val
43658 onKeyUp : function(e){
43659 this.setValue(this.getValue());
43662 onKeyPress : function(e){
43663 if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43670 * @class Roo.bootstrap.MoneyField
43671 * @extends Roo.bootstrap.ComboBox
43672 * Bootstrap MoneyField class
43675 * Create a new MoneyField.
43676 * @param {Object} config Configuration options
43679 Roo.bootstrap.MoneyField = function(config) {
43681 Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43685 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43688 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43690 allowDecimals : true,
43692 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43694 decimalSeparator : ".",
43696 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43698 decimalPrecision : 0,
43700 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43702 allowNegative : true,
43704 * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43708 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43710 minValue : Number.NEGATIVE_INFINITY,
43712 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43714 maxValue : Number.MAX_VALUE,
43716 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43718 minText : "The minimum value for this field is {0}",
43720 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43722 maxText : "The maximum value for this field is {0}",
43724 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
43725 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43727 nanText : "{0} is not a valid number",
43729 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43733 * @cfg {String} defaults currency of the MoneyField
43734 * value should be in lkey
43736 defaultCurrency : false,
43738 * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43740 thousandsDelimiter : false,
43742 * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43753 getAutoCreate : function()
43755 var align = this.labelAlign || this.parentLabelAlign();
43767 cls : 'form-control roo-money-amount-input',
43768 autocomplete: 'new-password'
43771 var hiddenInput = {
43775 cls: 'hidden-number-input'
43778 if(this.max_length) {
43779 input.maxlength = this.max_length;
43783 hiddenInput.name = this.name;
43786 if (this.disabled) {
43787 input.disabled = true;
43790 var clg = 12 - this.inputlg;
43791 var cmd = 12 - this.inputmd;
43792 var csm = 12 - this.inputsm;
43793 var cxs = 12 - this.inputxs;
43797 cls : 'row roo-money-field',
43801 cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43805 cls: 'roo-select2-container input-group',
43809 cls : 'form-control roo-money-currency-input',
43810 autocomplete: 'new-password',
43812 name : this.currencyName
43816 cls : 'input-group-addon',
43830 cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43834 cls: this.hasFeedback ? 'has-feedback' : '',
43845 if (this.fieldLabel.length) {
43848 tooltip: 'This field is required'
43854 cls: 'control-label',
43860 html: this.fieldLabel
43863 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43869 if(this.indicatorpos == 'right') {
43870 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43877 if(align == 'left') {
43885 if(this.labelWidth > 12){
43886 label.style = "width: " + this.labelWidth + 'px';
43888 if(this.labelWidth < 13 && this.labelmd == 0){
43889 this.labelmd = this.labelWidth;
43891 if(this.labellg > 0){
43892 label.cls += ' col-lg-' + this.labellg;
43893 input.cls += ' col-lg-' + (12 - this.labellg);
43895 if(this.labelmd > 0){
43896 label.cls += ' col-md-' + this.labelmd;
43897 container.cls += ' col-md-' + (12 - this.labelmd);
43899 if(this.labelsm > 0){
43900 label.cls += ' col-sm-' + this.labelsm;
43901 container.cls += ' col-sm-' + (12 - this.labelsm);
43903 if(this.labelxs > 0){
43904 label.cls += ' col-xs-' + this.labelxs;
43905 container.cls += ' col-xs-' + (12 - this.labelxs);
43916 var settings = this;
43918 ['xs','sm','md','lg'].map(function(size){
43919 if (settings[size]) {
43920 cfg.cls += ' col-' + size + '-' + settings[size];
43927 initEvents : function()
43929 this.indicator = this.indicatorEl();
43931 this.initCurrencyEvent();
43933 this.initNumberEvent();
43936 initCurrencyEvent : function()
43939 throw "can not find store for combo";
43942 this.store = Roo.factory(this.store, Roo.data);
43943 this.store.parent = this;
43947 this.triggerEl = this.el.select('.input-group-addon', true).first();
43949 this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43954 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43955 _this.list.setWidth(lw);
43958 this.list.on('mouseover', this.onViewOver, this);
43959 this.list.on('mousemove', this.onViewMove, this);
43960 this.list.on('scroll', this.onViewScroll, this);
43963 this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43966 this.view = new Roo.View(this.list, this.tpl, {
43967 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43970 this.view.on('click', this.onViewClick, this);
43972 this.store.on('beforeload', this.onBeforeLoad, this);
43973 this.store.on('load', this.onLoad, this);
43974 this.store.on('loadexception', this.onLoadException, this);
43976 this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43977 "up" : function(e){
43978 this.inKeyMode = true;
43982 "down" : function(e){
43983 if(!this.isExpanded()){
43984 this.onTriggerClick();
43986 this.inKeyMode = true;
43991 "enter" : function(e){
43994 if(this.fireEvent("specialkey", this, e)){
43995 this.onViewClick(false);
44001 "esc" : function(e){
44005 "tab" : function(e){
44008 if(this.fireEvent("specialkey", this, e)){
44009 this.onViewClick(false);
44017 doRelay : function(foo, bar, hname){
44018 if(hname == 'down' || this.scope.isExpanded()){
44019 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44027 this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44031 initNumberEvent : function(e)
44033 this.inputEl().on("keydown" , this.fireKey, this);
44034 this.inputEl().on("focus", this.onFocus, this);
44035 this.inputEl().on("blur", this.onBlur, this);
44037 this.inputEl().relayEvent('keyup', this);
44039 if(this.indicator){
44040 this.indicator.addClass('invisible');
44043 this.originalValue = this.getValue();
44045 if(this.validationEvent == 'keyup'){
44046 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44047 this.inputEl().on('keyup', this.filterValidation, this);
44049 else if(this.validationEvent !== false){
44050 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44053 if(this.selectOnFocus){
44054 this.on("focus", this.preFocus, this);
44057 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44058 this.inputEl().on("keypress", this.filterKeys, this);
44060 this.inputEl().relayEvent('keypress', this);
44063 var allowed = "0123456789";
44065 if(this.allowDecimals){
44066 allowed += this.decimalSeparator;
44069 if(this.allowNegative){
44073 if(this.thousandsDelimiter) {
44077 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44079 var keyPress = function(e){
44081 var k = e.getKey();
44083 var c = e.getCharCode();
44086 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44087 allowed.indexOf(String.fromCharCode(c)) === -1
44093 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44097 if(allowed.indexOf(String.fromCharCode(c)) === -1){
44102 this.inputEl().on("keypress", keyPress, this);
44106 onTriggerClick : function(e)
44113 this.loadNext = false;
44115 if(this.isExpanded()){
44120 this.hasFocus = true;
44122 if(this.triggerAction == 'all') {
44123 this.doQuery(this.allQuery, true);
44127 this.doQuery(this.getRawValue());
44130 getCurrency : function()
44132 var v = this.currencyEl().getValue();
44137 restrictHeight : function()
44139 this.list.alignTo(this.currencyEl(), this.listAlign);
44140 this.list.alignTo(this.currencyEl(), this.listAlign);
44143 onViewClick : function(view, doFocus, el, e)
44145 var index = this.view.getSelectedIndexes()[0];
44147 var r = this.store.getAt(index);
44150 this.onSelect(r, index);
44154 onSelect : function(record, index){
44156 if(this.fireEvent('beforeselect', this, record, index) !== false){
44158 this.setFromCurrencyData(index > -1 ? record.data : false);
44162 this.fireEvent('select', this, record, index);
44166 setFromCurrencyData : function(o)
44170 this.lastCurrency = o;
44172 if (this.currencyField) {
44173 currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44175 Roo.log('no currencyField value set for '+ (this.name ? this.name : this.id));
44178 this.lastSelectionText = currency;
44180 //setting default currency
44181 if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44182 this.setCurrency(this.defaultCurrency);
44186 this.setCurrency(currency);
44189 setFromData : function(o)
44193 c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44195 this.setFromCurrencyData(c);
44200 value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44202 Roo.log('no value set for '+ (this.name ? this.name : this.id));
44205 this.setValue(value);
44209 setCurrency : function(v)
44211 this.currencyValue = v;
44214 this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44219 setValue : function(v)
44221 v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44227 this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44229 this.inputEl().dom.value = (v == '') ? '' :
44230 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44232 if(!this.allowZero && v === '0') {
44233 this.hiddenEl().dom.value = '';
44234 this.inputEl().dom.value = '';
44241 getRawValue : function()
44243 var v = this.inputEl().getValue();
44248 getValue : function()
44250 return this.fixPrecision(this.parseValue(this.getRawValue()));
44253 parseValue : function(value)
44255 if(this.thousandsDelimiter) {
44257 r = new RegExp(",", "g");
44258 value = value.replace(r, "");
44261 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44262 return isNaN(value) ? '' : value;
44266 fixPrecision : function(value)
44268 if(this.thousandsDelimiter) {
44270 r = new RegExp(",", "g");
44271 value = value.replace(r, "");
44274 var nan = isNaN(value);
44276 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44277 return nan ? '' : value;
44279 return parseFloat(value).toFixed(this.decimalPrecision);
44282 decimalPrecisionFcn : function(v)
44284 return Math.floor(v);
44287 validateValue : function(value)
44289 if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44293 var num = this.parseValue(value);
44296 this.markInvalid(String.format(this.nanText, value));
44300 if(num < this.minValue){
44301 this.markInvalid(String.format(this.minText, this.minValue));
44305 if(num > this.maxValue){
44306 this.markInvalid(String.format(this.maxText, this.maxValue));
44313 validate : function()
44315 if(this.disabled || this.allowBlank){
44320 var currency = this.getCurrency();
44322 if(this.validateValue(this.getRawValue()) && currency.length){
44327 this.markInvalid();
44331 getName: function()
44336 beforeBlur : function()
44342 var v = this.parseValue(this.getRawValue());
44349 onBlur : function()
44353 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44354 //this.el.removeClass(this.focusClass);
44357 this.hasFocus = false;
44359 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44363 var v = this.getValue();
44365 if(String(v) !== String(this.startValue)){
44366 this.fireEvent('change', this, v, this.startValue);
44369 this.fireEvent("blur", this);
44372 inputEl : function()
44374 return this.el.select('.roo-money-amount-input', true).first();
44377 currencyEl : function()
44379 return this.el.select('.roo-money-currency-input', true).first();
44382 hiddenEl : function()
44384 return this.el.select('input.hidden-number-input',true).first();
44388 * @class Roo.bootstrap.BezierSignature
44389 * @extends Roo.bootstrap.Component
44390 * Bootstrap BezierSignature class
44391 * This script refer to:
44392 * Title: Signature Pad
44394 * Availability: https://github.com/szimek/signature_pad
44397 * Create a new BezierSignature
44398 * @param {Object} config The config object
44401 Roo.bootstrap.BezierSignature = function(config){
44402 Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44408 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44415 mouse_btn_down: true,
44418 * @cfg {int} canvas height
44420 canvas_height: '200px',
44423 * @cfg {float|function} Radius of a single dot.
44428 * @cfg {float} Minimum width of a line. Defaults to 0.5.
44433 * @cfg {float} Maximum width of a line. Defaults to 2.5.
44438 * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44443 * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44448 * @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.
44450 bg_color: 'rgba(0, 0, 0, 0)',
44453 * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44455 dot_color: 'black',
44458 * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44460 velocity_filter_weight: 0.7,
44463 * @cfg {function} Callback when stroke begin.
44468 * @cfg {function} Callback when stroke end.
44472 getAutoCreate : function()
44474 var cls = 'roo-signature column';
44477 cls += ' ' + this.cls;
44487 for(var i = 0; i < col_sizes.length; i++) {
44488 if(this[col_sizes[i]]) {
44489 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44499 cls: 'roo-signature-body',
44503 cls: 'roo-signature-body-canvas',
44504 height: this.canvas_height,
44505 width: this.canvas_width
44512 style: 'display: none'
44520 initEvents: function()
44522 Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44524 var canvas = this.canvasEl();
44526 // mouse && touch event swapping...
44527 canvas.dom.style.touchAction = 'none';
44528 canvas.dom.style.msTouchAction = 'none';
44530 this.mouse_btn_down = false;
44531 canvas.on('mousedown', this._handleMouseDown, this);
44532 canvas.on('mousemove', this._handleMouseMove, this);
44533 Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44535 if (window.PointerEvent) {
44536 canvas.on('pointerdown', this._handleMouseDown, this);
44537 canvas.on('pointermove', this._handleMouseMove, this);
44538 Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44541 if ('ontouchstart' in window) {
44542 canvas.on('touchstart', this._handleTouchStart, this);
44543 canvas.on('touchmove', this._handleTouchMove, this);
44544 canvas.on('touchend', this._handleTouchEnd, this);
44547 Roo.EventManager.onWindowResize(this.resize, this, true);
44549 // file input event
44550 this.fileEl().on('change', this.uploadImage, this);
44557 resize: function(){
44559 var canvas = this.canvasEl().dom;
44560 var ctx = this.canvasElCtx();
44561 var img_data = false;
44563 if(canvas.width > 0) {
44564 var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44566 // setting canvas width will clean img data
44569 var style = window.getComputedStyle ?
44570 getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44572 var padding_left = parseInt(style.paddingLeft) || 0;
44573 var padding_right = parseInt(style.paddingRight) || 0;
44575 canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44578 ctx.putImageData(img_data, 0, 0);
44582 _handleMouseDown: function(e)
44584 if (e.browserEvent.which === 1) {
44585 this.mouse_btn_down = true;
44586 this.strokeBegin(e);
44590 _handleMouseMove: function (e)
44592 if (this.mouse_btn_down) {
44593 this.strokeMoveUpdate(e);
44597 _handleMouseUp: function (e)
44599 if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44600 this.mouse_btn_down = false;
44605 _handleTouchStart: function (e) {
44607 e.preventDefault();
44608 if (e.browserEvent.targetTouches.length === 1) {
44609 // var touch = e.browserEvent.changedTouches[0];
44610 // this.strokeBegin(touch);
44612 this.strokeBegin(e); // assume e catching the correct xy...
44616 _handleTouchMove: function (e) {
44617 e.preventDefault();
44618 // var touch = event.targetTouches[0];
44619 // _this._strokeMoveUpdate(touch);
44620 this.strokeMoveUpdate(e);
44623 _handleTouchEnd: function (e) {
44624 var wasCanvasTouched = e.target === this.canvasEl().dom;
44625 if (wasCanvasTouched) {
44626 e.preventDefault();
44627 // var touch = event.changedTouches[0];
44628 // _this._strokeEnd(touch);
44633 reset: function () {
44634 this._lastPoints = [];
44635 this._lastVelocity = 0;
44636 this._lastWidth = (this.min_width + this.max_width) / 2;
44637 this.canvasElCtx().fillStyle = this.dot_color;
44640 strokeMoveUpdate: function(e)
44642 this.strokeUpdate(e);
44644 if (this.throttle) {
44645 this.throttleStroke(this.strokeUpdate, this.throttle);
44648 this.strokeUpdate(e);
44652 strokeBegin: function(e)
44654 var newPointGroup = {
44655 color: this.dot_color,
44659 if (typeof this.onBegin === 'function') {
44663 this.curve_data.push(newPointGroup);
44665 this.strokeUpdate(e);
44668 strokeUpdate: function(e)
44670 var rect = this.canvasEl().dom.getBoundingClientRect();
44671 var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44672 var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44673 var lastPoints = lastPointGroup.points;
44674 var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44675 var isLastPointTooClose = lastPoint
44676 ? point.distanceTo(lastPoint) <= this.min_distance
44678 var color = lastPointGroup.color;
44679 if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44680 var curve = this.addPoint(point);
44682 this.drawDot({color: color, point: point});
44685 this.drawCurve({color: color, curve: curve});
44695 strokeEnd: function(e)
44697 this.strokeUpdate(e);
44698 if (typeof this.onEnd === 'function') {
44703 addPoint: function (point) {
44704 var _lastPoints = this._lastPoints;
44705 _lastPoints.push(point);
44706 if (_lastPoints.length > 2) {
44707 if (_lastPoints.length === 3) {
44708 _lastPoints.unshift(_lastPoints[0]);
44710 var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44711 var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44712 _lastPoints.shift();
44718 calculateCurveWidths: function (startPoint, endPoint) {
44719 var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44720 (1 - this.velocity_filter_weight) * this._lastVelocity;
44722 var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44725 start: this._lastWidth
44728 this._lastVelocity = velocity;
44729 this._lastWidth = newWidth;
44733 drawDot: function (_a) {
44734 var color = _a.color, point = _a.point;
44735 var ctx = this.canvasElCtx();
44736 var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44738 this.drawCurveSegment(point.x, point.y, width);
44740 ctx.fillStyle = color;
44744 drawCurve: function (_a) {
44745 var color = _a.color, curve = _a.curve;
44746 var ctx = this.canvasElCtx();
44747 var widthDelta = curve.endWidth - curve.startWidth;
44748 var drawSteps = Math.floor(curve.length()) * 2;
44750 ctx.fillStyle = color;
44751 for (var i = 0; i < drawSteps; i += 1) {
44752 var t = i / drawSteps;
44758 var x = uuu * curve.startPoint.x;
44759 x += 3 * uu * t * curve.control1.x;
44760 x += 3 * u * tt * curve.control2.x;
44761 x += ttt * curve.endPoint.x;
44762 var y = uuu * curve.startPoint.y;
44763 y += 3 * uu * t * curve.control1.y;
44764 y += 3 * u * tt * curve.control2.y;
44765 y += ttt * curve.endPoint.y;
44766 var width = curve.startWidth + ttt * widthDelta;
44767 this.drawCurveSegment(x, y, width);
44773 drawCurveSegment: function (x, y, width) {
44774 var ctx = this.canvasElCtx();
44776 ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44777 this.is_empty = false;
44782 var ctx = this.canvasElCtx();
44783 var canvas = this.canvasEl().dom;
44784 ctx.fillStyle = this.bg_color;
44785 ctx.clearRect(0, 0, canvas.width, canvas.height);
44786 ctx.fillRect(0, 0, canvas.width, canvas.height);
44787 this.curve_data = [];
44789 this.is_empty = true;
44794 return this.el.select('input',true).first();
44797 canvasEl: function()
44799 return this.el.select('canvas',true).first();
44802 canvasElCtx: function()
44804 return this.el.select('canvas',true).first().dom.getContext('2d');
44807 getImage: function(type)
44809 if(this.is_empty) {
44814 return this.canvasEl().dom.toDataURL('image/'+type, 1);
44817 drawFromImage: function(img_src)
44819 var img = new Image();
44821 img.onload = function(){
44822 this.canvasElCtx().drawImage(img, 0, 0);
44827 this.is_empty = false;
44830 selectImage: function()
44832 this.fileEl().dom.click();
44835 uploadImage: function(e)
44837 var reader = new FileReader();
44839 reader.onload = function(e){
44840 var img = new Image();
44841 img.onload = function(){
44843 this.canvasElCtx().drawImage(img, 0, 0);
44845 img.src = e.target.result;
44848 reader.readAsDataURL(e.target.files[0]);
44851 // Bezier Point Constructor
44852 Point: (function () {
44853 function Point(x, y, time) {
44856 this.time = time || Date.now();
44858 Point.prototype.distanceTo = function (start) {
44859 return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44861 Point.prototype.equals = function (other) {
44862 return this.x === other.x && this.y === other.y && this.time === other.time;
44864 Point.prototype.velocityFrom = function (start) {
44865 return this.time !== start.time
44866 ? this.distanceTo(start) / (this.time - start.time)
44873 // Bezier Constructor
44874 Bezier: (function () {
44875 function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44876 this.startPoint = startPoint;
44877 this.control2 = control2;
44878 this.control1 = control1;
44879 this.endPoint = endPoint;
44880 this.startWidth = startWidth;
44881 this.endWidth = endWidth;
44883 Bezier.fromPoints = function (points, widths, scope) {
44884 var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44885 var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44886 return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44888 Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44889 var dx1 = s1.x - s2.x;
44890 var dy1 = s1.y - s2.y;
44891 var dx2 = s2.x - s3.x;
44892 var dy2 = s2.y - s3.y;
44893 var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44894 var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44895 var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44896 var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44897 var dxm = m1.x - m2.x;
44898 var dym = m1.y - m2.y;
44899 var k = l2 / (l1 + l2);
44900 var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44901 var tx = s2.x - cm.x;
44902 var ty = s2.y - cm.y;
44904 c1: new scope.Point(m1.x + tx, m1.y + ty),
44905 c2: new scope.Point(m2.x + tx, m2.y + ty)
44908 Bezier.prototype.length = function () {
44913 for (var i = 0; i <= steps; i += 1) {
44915 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44916 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44918 var xdiff = cx - px;
44919 var ydiff = cy - py;
44920 length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44927 Bezier.prototype.point = function (t, start, c1, c2, end) {
44928 return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44929 + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44930 + (3.0 * c2 * (1.0 - t) * t * t)
44931 + (end * t * t * t);
44936 throttleStroke: function(fn, wait) {
44937 if (wait === void 0) { wait = 250; }
44939 var timeout = null;
44943 var later = function () {
44944 previous = Date.now();
44946 result = fn.apply(storedContext, storedArgs);
44948 storedContext = null;
44952 return function wrapper() {
44954 for (var _i = 0; _i < arguments.length; _i++) {
44955 args[_i] = arguments[_i];
44957 var now = Date.now();
44958 var remaining = wait - (now - previous);
44959 storedContext = this;
44961 if (remaining <= 0 || remaining > wait) {
44963 clearTimeout(timeout);
44967 result = fn.apply(storedContext, storedArgs);
44969 storedContext = null;
44973 else if (!timeout) {
44974 timeout = window.setTimeout(later, remaining);